概述

本篇博文完整讲述了如果通过 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. .NET源代码的内部排序实现

    使用JetBrains的DotPeek工具能够方便地查看.net的部分源代码.于是看了一下.NET的内部是怎样实现排序的算法. 在System.Collections.Generic 命名空间下能够看 ...

  2. JS解析DataSet.GetXML()方法产生的xml

    在实际的项目制作过程中,经常要采用ajax方式来进行,当然,这就免不了要进行数据交换.如果采用拼接字符串的方式来进行,不仅拼接的时候麻烦,而且在拆解的时候更加麻烦,一旦遇到特殊字符,那么就是灾难了.因 ...

  3. WebIM(2)---消息缓存

    WebIM系列文章 在一步一步打造WebIM(1)一文中,已经介绍了如何实现一个简单的WebIM,但是,这个WebIM有一个问题,就是每一次添加消息监听器时,都必须访问一次数据库去查询是否有消息,显然 ...

  4. 【C#版本详情回顾】C#3.0主要功能列表

    隐式类型的本地变量和数组 在与本地变量一起使用时,var 关键字指示编译器根据初始化语句右侧的表达式推断变量或数组元素的类型 对象初始值设定项 支持无需显式调用构造函数即可进行对象初始化 集合初始值设 ...

  5. status状态栏实现字符串走动

    <script type="text/javascript" language="javascript"> var i = 0; var str=& ...

  6. Android学习笔记-Intent(一)

    Intent对象在Android官方API这样描述:It is a passive data structure holding an abstract description of an opera ...

  7. GLIBC_2.7升级

    GLIBC_2.7: ftp://ftp.ntua.gr/pub/FreeBSD/ports/distfiles/rpm/i386/fedora/8/glibc-2.7-2.i386.rpm ftp: ...

  8. Asycn/Await 异步编程

    Asycn/Await 异步编程初窥(二)   经过总过4天的学习和实践,做完了 WinForm 下 .Net 4.5 的基本异步应用,实现了一个 Http 协议下载的测试程序,为以后使用 .Net ...

  9. CSS中文字体的英文名称 – 前台开发必备

    做什么用的?写过CSS的都晓得,一般用在font-family后面——为什么不用中文呢?有过一定开发经验的都晓得CSS里面用中文也是会乱码的,特别是没有中文字符集的浏览器,直接成了框框,用英文就可以解 ...

  10. [NLP自然语言处理]计算熵和KL距离,java实现汉字和英文单词的识别,UTF8变长字符读取

    算法任务: 1. 给定一个文件,统计这个文件中所有字符的相对频率(相对频率就是这些字符出现的概率——该字符出现次数除以字符总个数,并计算该文件的熵). 2. 给定另外一个文件,按上述同样的方法计算字符 ...