通过grpc-gateway对外提供基于grpc的http接口API

麦穗儿 · · 1302 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:麦穗儿

查看原文:通过grpc-gateway对外提供基于grpc的http接口API

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1302 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传