使用 grpc 的优点很多,二进制的数据可以加快传输速度,基于 http2 的多路复用可以减少服务之间的连接次数,和函数一样的调用方式也有效的提升了开发效率。
不过使用 grpc 也会面临一个问题,我们的微服务对外一定是要提供 Restful 接口的,如果内部调用使用 grpc,在某些情况下要同时提供一个功能的两套 API 接口,这样就不仅降低了开发效率,也增加了调试的复杂度。于是就想着有没有一个转换机制,让 Restful 和 gprc 可以相互转化。
在网上看到一个解决方案,,简单的说就是有一个网关服务器负责转化和代理转发。
如下图:
首先要安装 ProtocolBuffers 3.0 及以上版本。
- mkdir tmp
- cd tmp
- git clone https://github.com/google/protobuf
- cd protobuf
- ./autogen.sh
- ./configure
- make
- make check
- sudo make install
然后使用 go get 获取 grpc-gateway。
- go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
- go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
- go get -u github.com/golang/protobuf/protoc-gen-go
这里最好把编译生成的二进制文件的目录放在
中,可以把
- $PATH
放入
- $GOPATH/bin
中。
- $PATH
本示例是基于我的上一篇博客《google 的 grpc 在 glang 中的使用》中的示例,如果有必要请先了解上一篇博客。
示例代码获取地址:。
代码文件结构如下
- └── src
- └── grpc-helloworld-gateway
- ├── gateway
- │ └── main.go
- ├── greeter_server
- │ └── main.go
- └── helloworld
- ├── helloworld.pb.go
- ├── helloworld.pb.gw.go
- └── helloworld.proto
我们还是先看一下协议文件。helloworld.proto 有一些变动,引入了 google 官方的 api 相关的扩展,为 grpc 的 http 转换提供了支持。
具体改动如下:
- syntax = "proto3";
- option java_multiple_files = true;
- option java_package = "io.grpc.examples.helloworld";
- option java_outer_classname = "HelloWorldProto";
- package helloworld;
- import "google/api/annotations.proto";
- // The greeting service definition.
- service Greeter {
- // Sends a greeting
- rpc SayHello (HelloRequest) returns (HelloReply) {
- option (google.api.http) = {
- post: "/v1/example/echo"
- body: "*"
- };
- }
- }
- // The request message containing the user's name.
- message HelloRequest {
- string name = 1;
- }
- // The response message containing the greetings
- message HelloReply {
- string message = 1;
- }
和之前的 proto 文件比较,新的文件增了
- import "google/api/annotations.proto";
和
- option (google.api.http) = {
- post: "/v1/example/echo"
- body: "*"
这里增加了对 http 的扩展配置。
然后编译 proto 文件,生成对应的 go 文件
- cd src/grpc-helloworld-gateway
- protoc -I/usr/local/include -I. \
- -I$GOPATH/src \
- -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
- --go_out=Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api,plugins=grpc:. \
- helloworld/helloworld.proto
这里生成了 helloworld/helloworld.pb.go 文件。
helloworld.pb.go 是 server 服务需要的,下一步我们需要使用 protoc 生成 gateway 需要的 go 文件。
- cd src/grpc-helloworld-gateway
- protoc -I/usr/local/include -I. \
- -I$GOPATH/src -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
- --swagger_out=logtostderr=true:. \
- helloworld/helloworld.proto
这里生成了 helloworld/helloworld.pb.gw.go 文件。这个文件就是 gateway 用来的协议文件,用来做 grpc 和 http 的协议转换。
协议文件处理完毕,就需要写 gateway 代码了。
gateway 代码如下:
- package main
- import (
- "flag"
- "net/http"
- "github.com/golang/glog"
- "github.com/grpc-ecosystem/grpc-gateway/runtime"
- "golang.org/x/net/context"
- "google.golang.org/grpc"
- gw "grpc-helloworld-gateway/helloworld"
- )
- var (
- echoEndpoint = flag.String("echo_endpoint", "localhost:50051", "endpoint of YourService")
- )
- func run() error {
- ctx := context.Background()
- ctx, cancel := context.WithCancel(ctx)
- defer cancel()
- mux := runtime.NewServeMux()
- opts := []grpc.DialOption{grpc.WithInsecure()}
- err := gw.RegisterGreeterHandlerFromEndpoint(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)
- }
- }
首先 echoEndpoint 存储了需要连接的 server 信息,然后将这些信息和新建的 server 用 gw.go 中的 RegisterGreeterHandlerFromEndpoint 进行一个注册和绑定,这时低层就会连接 echoEndpoint 提供的远程 server 地址,这样 gateway 就作为客户端和远程 server 建立了连接,之后用 http 启动新建的 server,gateway 就作为服务器端对外提供 http 的服务了。
代码到此就完成了,我们测试一下。
先启动 greeter_server 服务,再启动 gateway,这时 gatway 连接上 greeter_server 后,对外建立 http 的监听。
然后我们用 curl 发送 http 请求
- curl - X POST - k http: //localhost:8080/v1/example/echo -d '{"name": " world"}
- {
- "message": "Hello world"
- }
流程如下:curl 用 post 向 gateway 发送请求,gateway 作为 proxy 将请求转化一下通过 grpc 转发给 greeter_server,greeter_server 通过 grpc 返回结果,gateway 收到结果后,转化成 json 返回给前端。
这样,就通过 grpc-gateway 完成了从 http json 到内部 grpc 的转化过程。
来源: http://www.cnblogs.com/andyidea/p/6529900.html