Providing an external http interface API based on grpc through grpc-gateway

Keywords: Go Google github Docker curl

The advantages of grpc are no longer described, but how to provide Restful interface to the outside world and how to use grpc-gateway to convert grpc to Restful API when there is no desire to develop the same function repeatedly.

install

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

If it is only used, it is very simple to add the import file and http configuration statement to the original grpc. proto file.

import file

import "google/api/annotations.proto";

http configuration statement

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


Probably explain

bookinfo.proto file

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;
}

Define the request interface name, parameters, and return results
The parameter of the Getall interface is the userid of string type. The return result is an array of BookInfo (identified by repeated)

Generating stub files for grpc services

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

Generate bookinfo.pb.go file

Generating stub files for grpc-gateway

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

Generate bookinfo.pb.gw.go file

Implementation of grpc function

  • 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 method is defined and implemented in bookinfo.pb.go

  • Specific Logical Implementation of Interface Method Defined by 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:"Preface", WordsCount:3259}
       cpsInfo[1] = &bookinfo.ChapterInfo{ChapterNum:2, ChapterName:"Boer", WordsCount:4559}
       cpsInfo[2] = &bookinfo.ChapterInfo{ChapterNum:3, ChapterName:"character", WordsCount:7559}
       cpsInfo[3] = &bookinfo.ChapterInfo{ChapterNum:4, ChapterName:"function", 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 provides http interface to the outside world

  • 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)
     }

    }

The RegisterBookinfoHandlerFromEndpoint method is defined and implemented in bookinfo.pb.gw.go

grpc request

  • client/main.go

    package main

    import (

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

    )

    const (

       ADDRESS = "rpcserver:50051"

    )

    func main() {

       // Connect to gRPC server
       conn, err := grpc.Dial(ADDRESS, grpc.WithInsecure())
       if err != nil {
           log.Fatalf("connect error: %v", err)
       }
       defer conn.Close()
    
       // Initialize gRPC client
       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 calls the implementation of grpc interface. Definition and Implementation of NewBookinfoClient Method in bookinfo.pb.go

http request test

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

Detailed code can be obtained from this https://github.com/BinWang-sh...

makefile in the root directory generates stub and docker images
Docker directory has docker-compose file execution docker-compose up can run directly

Posted by kmussel on Mon, 30 Sep 2019 16:21:48 -0700