概述

本篇博文完整讲述了如果通过 protocol buffers 定义并启动一个 gRPC 服务,然后在 gRPC 服务上提供一个 RESTful JSON API 的反向代理 gateway,最后通过 swagger ui 来提供 RESTful JSON API 的说明,完整代码 helloworld_restful_swagger

Helloworld gRPC Service

参考 gRPC Quick Start for Python

Install gRPC

安装 gRPC

运行命令,

$ python -m pip install grpcio

安装 gRPC 工具箱

Python 的 gRPC 工具箱包括 protol buffer 编译器 protoc 和一些特定插件用于从 .proto 服务定义文件生成 gRPC server 和 client 的代码。

运行命令,

$ python -m pip install grpcio-tools

服务定义文件 helloworld.proto

在 pb 目录下新建文件 helloworld.proto,然后在其中使用 protocol buffers 语法来编写 gRPC 服务定义。文件内容如下:

syntax = "proto3";

package helloworld;

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
} // The request message containing the user's name.
message HelloRequest {
string name = 1;
} // The response message containing the greetings
message HelloReply {
string message = 1;
}

protoc 编译生成 server 和 client 类定义

使用下面命令来生成 gRPC 的 server 和 client 类定义代码文件,

$ python -m grpc.tools.protoc -I. --python_out=. --grpc_python_out=. pb/helloworld.proto

命令没有报错的话,将会在 pb 目录下生成两个文件 helloworld_pb2.py 和 helloworld_pb2_grpc.py。

其中文件 helloworld_pb2.py 包含了 HelloRequest 和 HelloReploy 的消息类定义,而文件 helloworld_pb2_grpc.py 提供了 gRPC server 类(GreeterServicer)和 client 类(GreeterStub)定义。

编写具体的 gRPC 服务类

文件 helloworld_pb2_grpc.py 提供了 gRPC server 类(GreeterServicer)提供了 gRPC 服务的规范定义,没有具体的实现。我们需要自己编写 gRPC 服务类文件 server.py,代码如下,

from concurrent import futures
import time import grpc import pb.helloworld_pb2 as pb_dot_helloworld__pb2
import pb.helloworld_pb2_grpc as pb_dot_helloworld_pb2__grpc _ONE_DAY_IN_SECONDS = 60 * 60 * 24 class MyServer(pb_dot_helloworld_pb2__grpc.GreeterServicer):
def SayHello(self, request, context):
print("Receive request, request.name: {0}".format(request.name))
return pb_dot_helloworld__pb2.HelloReply(
message='Hello, {0}'.format(request.name)) def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
pb_dot_helloworld_pb2__grpc.add_GreeterServicer_to_server(MyServer(), server)
server.add_insecure_port('[::]:50051')
print("GreeterServicer start at port 50051...")
server.start()
try:
while True:
time.sleep(_ONE_DAY_IN_SECONDS)
except KeyboardInterrupt:
server.stop(0) if __name__ == '__main__':
serve()

然后启动 gRPC server,

$ python server.py
lienhuadeMacBook-Pro:helloworld_restful_swagger lienhua34$ python server.py
GreeterServicer start at port 50051...

编写 gRPC client.py

文件 helloworld_pb2_grpc.py 提供了 gRPC client 类(GreeterStub)定义。我们需要编写自己的 client.py 代码来通过 GreeterStub 调用 gRPC server 方法。代码内容如下:

import grpc
import pb.helloworld_pb2 as pb_dot_helloworld__pb2
import pb.helloworld_pb2_grpc as pb_dot_helloworld_pb2__grpc def run():
channel = grpc.insecure_channel('localhost:50051')
stub = pb_dot_helloworld_pb2__grpc.GreeterStub(channel)
response = stub.SayHello(pb_dot_helloworld__pb2.HelloRequest(name="world"))
print("GreeterService client received: " + response.message) if __name__ == '__main__':
run()

运行 client.py 代码,

lienhuadeMacBook-Pro:helloworld_restful_swagger lienhua34$ python client.py
GreeterService client received: Hello, world

至此,可见我们的 gRPC helloworld 服务已经可用。

RESTful JSON API Gateway

调用 gRPC 服务需要自己编写相对应的 client 代码才行,这无疑给访问 gRPC 带来了一定的难度。我们可以通过在 gRPC 服务上面提供一个 RESTful API gateway,可以直接通过 RESTful JSON API 来访问。

grpc-gateway 是 protoc 的一个插件,用于读取 gRPC 服务定义,然后生成一个反向代理服务来将 RESTful JSON API 转换为 gRPC 调用。

Install grpc-gateway

确保你本地安装了 golang 6.0 以上版本,并且将 $GOPATH/bin 添加到 $PATH 中。然后运行下面命令,

$ 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

修改 helloworld.proto

修改文件 helloworld.proto,添加gateway option,

syntax = "proto3";

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/hello"
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;
}

生成 gRPC golang stub 类

运行下面命令生成 gRPC golang stub 类文件,

$ python -m grpc.tools.protoc -I. \
-I/usr/local/include \
-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:. \
pb/helloworld.proto

此时便在 pb 目录下生成 helloworld.pb.go 文件。

生成反向代理代码

运行下面命令生成反向代理代码,

$ python -m grpc.tools.protoc -I. \
-I/usr/local/include \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--grpc-gateway_out=logtostderr=true:. \
pb/helloworld.proto

没有报错的话,将在 pb 目录下生成文件 helloworld.pb.gw.go。

编写 entrypoint 文件

编写 entrypoint 文件 proxy.go,内容如下:

package main

import (
"flag"
"log"
"net/http" "github.com/grpc-ecosystem/grpc-gateway/runtime"
"golang.org/x/net/context"
"google.golang.org/grpc" gw "github.com/lienhua34/notes/grpc/helloworld_restful_swagger/pb"
) var (
greeterEndpoint = flag.String("helloworld_endpoint", "localhost:50051", "endpoint of Greeter gRPC Service")
) 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, *greeterEndpoint, opts)
if err != nil {
return err
} log.Print("Greeter gRPC Server gateway start at port 8080...")
http.ListenAndServe(":8080", mux)
return nil
} func main() {
flag.Parse() if err := run(); err != nil {
log.Fatal(err)
}
}

编译,生成可执行文件 helloworld_restful_swagger,

$ go build .

启动服务

先启动 gRPC 服务,

lienhuadeMacBook-Pro:helloworld_restful_swagger lienhua34$ python server.py
GreeterServicer start at port 50051...

然后启动 RESTful JSON API gateway,

lienhuadeMacBook-Pro:helloworld_restful_swagger lienhua34$ ./helloworld_restful_swagger
2017/01/12 15:59:17 Greeter gRPC Server gateway start at port 8080...

通过 curl 进行访问,

lienhuadeMacBook-Pro:helloworld_restful_swagger lienhua34$ curl -X POST -k http://localhost:8080/v1/hello -d '{"name": "world"}'
{"message":"Hello, world"}
lienhuadeMacBook-Pro:helloworld_restful_swagger lienhua34$ curl -X POST -k http://localhost:8080/v1/hello -d '{"name": "lienhua34"}'
{"message":"Hello, lienhua34"}

自此,RESTful JSON API gateway 已经可用了。

swagger UI

RESTful JSON API 的 Swagger 说明

通过下面命令可以生成 RESTful JSON API 的 swagger 说明文件。

$ python -m grpc.tools.protoc -I. \
-I/usr/local/include \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--swagger_out=logtostderr=true:. \
pb/helloworld.proto

该命令在 pb 目录下生成一个 helloworld.swagger.json 文件。我们在 pb 目录下直接新增一个文件 helloworld.swagger.go,然后在里面定义一个常量 Swagger,内容即为 helloworld.swagger.json 的内容。

修改 proxy.go 文件中的 run() 方法来添加一个 API 路由来返回 swagger.json 的内容,

func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel() mux := http.NewServeMux()
mux.HandleFunc("/swagger.json", func(w http.ResponseWriter, req *http.Request) {
io.Copy(w, strings.NewReader(gw.Swagger))
}) gwmux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
err := gw.RegisterGreeterHandlerFromEndpoint(ctx, gwmux, *greeterEndpoint, opts)
if err != nil {
return err
} log.Print("Greeter gRPC Server gateway start at port 8080...")
http.ListenAndServe(":8080", mux)
return nil
}

重新编译,并启动 RESTful gateway,然后访问 http://localhost:8080/swagger.json 便得到 helloworld RESTful API 的 swagger 说明了。

但是,swagger.json 内容显示太不直观了。swagger 提供了非常好的可视化 swagger-ui。我们将 swagger-ui 添加到我们的 gateway 中。

下载 swagger-ui 代码

Swagger 提供了可视化的 API 说明。我们可以在 RESTful JSON API gateway 中添加 swagger-ui。

将 Swagger 源码的 dist 目录下包含了 swagger ui 所需的 HTML、css 和 js 代码文件,我们将该目录下的所有文件拷贝到 third_party/swagger-ui 目录下。

将 swagger-ui 文件制作成 go 内置文件

我们可以使用 go-bindata 将 swagger-ui 的文件制作成 go 内置的数据文件进行访问。

先安装 go-bindata,

$ go get -u github.com/jteeuwen/go-bindata/...

然后将 third-party/swagger-ui 下的所有文件制作成 go 内置数据文件,

$ go-bindata --nocompress -pkg swagger -o pkg/ui/data/swagger/datafile.go third_party/swagger-ui/...

生成文件 pkg/ui/data/swagger/datafile.go,

$ ls -l pkg/ui/data/swagger/datafile.go
-rw-r--r-- 1 lienhua34 staff 3912436 1 12 22:56 pkg/ui/data/swagger/datafile.go

swagger-ui 文件服务器

使用 go-bindata 将 swagger-ui 制作成 go 内置数据文件之后,我们便可以使用 elazarl/go-bindata-assetfs 结合 net/http 来将 swagger-ui 内置数据文件对外提供服务。

安装 elazarl/go-bindata-assetfs,

$ go get github.com/elazarl/go-bindata-assetfs/...

然后修改 proxy.go 代码,最终代码请看 proxy.go

重新编译,然后启动 gateway 服务,在浏览器中输入 http://localhost:8080/swagger-ui,

但是上面打开的 swagger-ui 默认打开的是一个 http://petstore.swagger.io/v2/swagger.json 的 API 说明信息。我们需要在输入框中输入我们 API 的地址 http://localhost:8080/swagger.json ,然后点击回车键才能看到我们的 API 说明,

如果我们想让它打开的时候默认就是我们的 API 说明怎么办?将文件 third_party/swagger-ui/index.html 中的 http://petstore.swagger.io/v2/swagger.json 替换成 http://localhost:8080/swagger.json ,然后重新生成 pkg/ui/data/swagger/datafile.go 文件,再重新编译一下即可。

参考:

********************************************************

转载请标注原创出处:lienhua34 http://www.cnblogs.com/lienhua34/p/6285829.html

********************************************************

gRPC helloworld service, RESTful JSON API gateway and swagger UI的更多相关文章

  1. spring boot之使用springfox swagger展示restful的api doc

    摘要 springfox swagger展示restful的api doc, swagger is A POWERFUL INTERFACE TO YOUR API. 新增文件: import org ...

  2. Pattern: API Gateway / Backend for Front-End

    http://microservices.io/patterns/apigateway.html Pattern: API Gateway / Backend for Front-End Contex ...

  3. ETCD:HTTP JSON API通过gRPC网关

    原文地址:HTTP JSON API through the gRPC gateway etcd v3 使用 gRPC 作为消息协议.etcd项目包括一个基于gRPC的Go客户端和一个命令行工具,et ...

  4. 谈谈微服务中的 API 网关(API Gateway)

    前言 又是很久没写博客了,最近一段时间换了新工作,比较忙,所以没有抽出来太多的时间写给关注我的粉丝写一些干货了,就有人问我怎么最近没有更新博客了,在这里给大家抱歉. 那么,在本篇文章中,我们就一起来探 ...

  5. 微服务中的 API 网关(API Gateway)

    API 网关(API Gateway)提供高性能.高可用的 API 托管服务,帮助用户对外开放其部署在 ECS.容器服务等云产品上的应用,提供完整的 API 发布.管理.维护生命周期管理.用户只需进行 ...

  6. API Gateway微服务

    微服务中的 API 网关(API Gateway)   前言 又是很久没写博客了,最近一段时间换了新工作,比较忙,所以没有抽出来太多的时间写给关注我的粉丝写一些干货了,就有人问我怎么最近没有更新博客了 ...

  7. 服务中的 API 网关(API Gateway)

    我们知道在微服务架构风格中,一个大应用被拆分成为了多个小的服务系统提供出来,这些小的系统他们可以自成体系,也就是说这些小系统可以拥有自己的数据库,框架甚至语言等,这些小系统通常以提供 Rest Api ...

  8. Using Amazon API Gateway with microservices deployed on Amazon ECS

    One convenient way to run microservices is to deploy them as Docker containers. Docker containers ar ...

  9. Amazon API Gateway Importer整合过程小结

    (1)需要将swagger json转换成amazon api gateway 所需要的格式(根据Method Request中 Request PathsURL Query String Param ...

随机推荐

  1. PHP 10 : 流程控制

    原文:PHP 10 : 流程控制 感觉PHP和其他语言相似.说说PHP提供的流程控制关键字吧. 条件 ifelseelseifswitch 循环 whiledo{} while()breakconti ...

  2. 全球最快的JS模板引擎:tppl

    废话不多说,先上测试: 亲测请访问:[在线测试地址]单次结果不一定准确,请多测几次. tppl 的编译渲染速度是著名的 jQuery 作者 John Resig 开发的 tmpl 的 43 倍!与第二 ...

  3. 摘录DirectShow数据,视频采集

    DirectShow在,数据流(Data Flow)它们是依次流过每Filter的.管理其数据具有其自己的方法,并且并没有向用户提供一个统一的接口,供用户操作数据流.这里以提取视频採集在的每帧为位图数 ...

  4. openwrt路由器更换了Flash之后需要修改的源码

    假如我使用的是WR703N,改为8M内存: 1 修改openwrt/target/linux/ar71xx/image/Makefile文件 $(eval $(call SingleProfile,T ...

  5. SQL Server 2005中设置Reporting Services发布web报表的匿名访问

    原文:SQL Server 2005中设置Reporting Services发布web报表的匿名访问 一位朋友提出个问题:集成到SQL Server 2005中的Reporting Services ...

  6. SVN服务器搭建(3)

    转自:http://www.cnblogs.com/xiaobaihome/archive/2012/03/20/2408089.html vs 2013 svn插件:http://www.visua ...

  7. jquery 触屏滑动+定时滚动

    <!doctype html> <html> <head> <meta charset="utf-8"> <meta name ...

  8. Python的@符号

    Python一直都属于用,没有去系统学习过,在一次代码review中见到了@符号,回来看了下,这个符号用于装饰器中,用于修饰一个函数,把被修饰的函数作为参数传递给装饰器,下面举几个例子: 1. @cl ...

  9. HDU 4630、BOJ 某题

    两道离线线段树. 比赛时候没想到.... 扫描数组,i从1到n,线段树维护从1到i每一个约数(1~50000)的出现的最近位置,线段树存储的是约数的最大值 #include<cstdio> ...

  10. Markdown 代码测试

    Mou Overview Mou, the missing Markdown editor for web developers. Syntax Strong and Emphasize strong ...