通過grpc-gateway對外提供基於grpc的http接口API

grpc優點不再多敘述,但如何對外提供Restful接口的,而有不希望重複開發相同的功能可以使用grpc-gateway將grpc轉換爲對外的Restful API。

安裝

go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
go get -u github.com/micro/protobuf/{proto,protoc-gen-go}

如果只是使用,其實處理起來很簡單在原有grpc的.proto文件中添加相應的import文件和http配置聲明即可。

import文件

import "google/api/annotations.proto";

http配置聲明

service Bookinfo {
    // get book information
    rpc Getall (GetallRequest) returns (GetallResponse) {
        option (google.api.http) = {
            post: "/bookinfo/getall"
            body: "*"
        };
    };
}


大概說明
圖片描述

bookinfo.proto文件

syntax = "proto3";

package bookinfo;

import "google/api/annotations.proto";

option go_package = "bookinfopb";

// The book service makes it possible to get or set book's detail information
service Bookinfo {
    // get book information
    rpc Getall (GetallRequest) returns (GetallResponse) {
        option (google.api.http) = {
            post: "/bookinfo/getall"
            body: "*"
        };
    };
    // add books information
    rpc Add (AddRequest) returns (AddResponse){
        option (google.api.http) = {
            post: "/bookinfo/add"
            body: "*"
        };
    };
}

message GetallRequest {
    // ID of user
    string userid = 1;
}

message GetallResponse {
    // Book info list
    repeated BookInfo bookinfolist = 1;
}

message BookInfo {
    // Name of book
    string book_name = 1;
    // Author
    string author = 2;
    // Chapters name
    repeated ChapterInfo chapters_info = 3;
}

message ChapterInfo {
    // Chapter number
    sint32 chapter_num = 1;
    // Chapter name
    string chapter_name = 2;
    // Words cound
    sint32 words_count = 3;
}

message AddRequest {
    // Books information
    repeated BookInfo books_info = 1;
}

message AddResponse {
    repeated sint32 field = 1;
}

定義請求接口名,參數和返回結果
Getall接口的參數是string類型的userid。返回結果是書籍詳情(BookInfo)的數組(通過repeated標識)

生成grpc服務的stub文件

protoc -I/usr/local/include -I. \
    -I$(GOPATH)/src \
    -I$(GOPATH)/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
    --go_out=plugins=grpc:. bookinfo-srv/proto/bookinfo/bookinfo.proto

生成bookinfo.pb.go文件

生成grpc-gateway的stub文件

protoc -I/usr/local/include -I. \
-I $(GOPATH)/src \
-I $(GOPATH)/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--grpc-gateway_out=logtostderr=true:. bookinfo-srv/proto/bookinfo/bookinfo.proto

生成bookinfo.pb.gw.go文件

圖片描述

grpc功能的實現

  • service.go

    package main

    import (

       pb "grpcT1/bookinfo-srv/proto/bookinfo"
       bkinfoprocess "grpcT1/bookinfo-srv/process"
       "net"
       "log"
       "google.golang.org/grpc"

    )
    const (

       PORT = ":50051"

    )

    func main() {

       listener, err := net.Listen("tcp", PORT)
       if err != nil {
           log.Fatalf("failed to listen: %v", err)
       }
       log.Printf("listen on: %s\n", PORT)
    
       server := grpc.NewServer()
    
    
       pb.RegisterBookinfoServer(server, bkinfoprocess.NewBookinfo())
    
       if err := server.Serve(listener); err != nil {
           log.Fatalf("failed to serve: %v", err)
       }
    

    }

其中RegisterBookinfoServer方法是在bookinfo.pb.go中定義和實現的

  • process/bookinfo.go 定義的接口方法的具體邏輯實現

    package bookinfoprocess

    import (

       bookinfo "grpcT1/bookinfo-srv/proto/bookinfo"
       "context"
       "fmt"

    )

    type bookinfosrvc struct {}

    func NewBookinfo() bookinfo.BookinfoServer {

       return &bookinfosrvc{}

    }

    func (s bookinfosrvc) Getall(ctx context.Context, p bookinfo.GetallRequest) (*bookinfo.GetallResponse, error) {

       res := &bookinfo.GetallResponse{}
       fmt.Println("bookinfo.getall")
       cpsInfo := make([]*bookinfo.ChapterInfo,4)
       cpsInfo[0] = &bookinfo.ChapterInfo{ChapterNum:1, ChapterName:"序言", WordsCount:3259}
       cpsInfo[1] = &bookinfo.ChapterInfo{ChapterNum:2, ChapterName:"布爾", WordsCount:4559}
       cpsInfo[2] = &bookinfo.ChapterInfo{ChapterNum:3, ChapterName:"字符", WordsCount:7559}
       cpsInfo[3] = &bookinfo.ChapterInfo{ChapterNum:4, ChapterName:"函數", WordsCount:7859}
    
       bkInfo := &bookinfo.BookInfo{BookName:"Ugly language", Author:"Bill", 
                           ChaptersInfo:cpsInfo}
    
       res.Bookinfolist = append(res.Bookinfolist, bkInfo)
    
       return res, nil

    }

    func (s bookinfosrvc) Add(ctx context.Context, p bookinfo.AddRequest) (*bookinfo.AddResponse, error) {

       return nil, nil

    }

gateway方式對外提供http接口

  • gateway/main.go

    package main

    import (

     "flag"
     "net/http"
      
     "github.com/golang/glog"
     "golang.org/x/net/context"
     "github.com/grpc-ecosystem/grpc-gateway/runtime"
     "google.golang.org/grpc"
          
     gw "grpcT1/bookinfo-srv/proto/bookinfo"

    )

    var (

     echoEndpoint = flag.String("getall_endpoint", "rpcserver:50051", "endpoint of BookInfoService")

    )

    func run() error {

     ctx := context.Background()
     ctx, cancel := context.WithCancel(ctx)
     defer cancel()
      
     mux := runtime.NewServeMux()
     opts := []grpc.DialOption{grpc.WithInsecure()}
     err := gw.RegisterBookinfoHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts)
     if err != nil {
       return err
     }
      
     return http.ListenAndServe(":8080", mux)

    }

    func main() {

     flag.Parse()
     defer glog.Flush()
      
     if err := run(); err != nil {
       glog.Fatal(err)
     }

    }

其中RegisterBookinfoHandlerFromEndpoint方法在bookinfo.pb.gw.go中定義和實現

grpc請求

  • client/main.go

    package main

    import (

       pb "grpcT1/bookinfo-srv/proto/bookinfo"
       "google.golang.org/grpc"
       "log"
       "context"

    )

    const (

       ADDRESS = "rpcserver:50051"

    )

    func main() {

       // 連接到 gRPC 服務器
       conn, err := grpc.Dial(ADDRESS, grpc.WithInsecure())
       if err != nil {
           log.Fatalf("connect error: %v", err)
       }
       defer conn.Close()
    
       // 初始化 gRPC 客戶端
       client := pb.NewBookinfoClient(conn)
    
       resp, err := client.Getall(context.Background(), &pb.GetallRequest{Userid:"tt"})
       if err != nil {
           log.Fatalf("Getall error: %v", err)
       }
    
       length := len(resp.Bookinfolist)
       for i:=0;i<length;i++ {
           log.Printf("bookName:%s\n",resp.Bookinfolist[i].BookName)
           log.Printf("author:%s\n", resp.Bookinfolist[i].Author)
           
           chaptersCount := len(resp.Bookinfolist[i].ChaptersInfo)
           for j:=0;j<chaptersCount;j++ {
               log.Printf("---Chapter.Num:%d\n", resp.Bookinfolist[i].ChaptersInfo[j].ChapterNum)
               log.Printf("---Chapter.Name:%s\n", resp.Bookinfolist[i].ChaptersInfo[j].ChapterName)
               log.Printf("---Chapter.WordsCount:%d\n", resp.Bookinfolist[i].ChaptersInfo[j].WordsCount)
               log.Println("")
           }
       }

    }

client調用grpc接口的實現。NewBookinfoClient方法在bookinfo.pb.go中定義和實現

http請求測試

curl -X POST -k http://localhost:8080/bookinfo/getall -d '{"userid":"tt"}'

詳細代碼可以從這取得 https://github.com/BinWang-sh...

根目錄下有makefile可生成stub和docker鏡像
Docker目錄下有docker-compose文件執行docker-compose up可以直接run

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章