本篇博文完整講述了若是經過 protocol buffers 定義並啓動一個 gRPC 服務,而後在 gRPC 服務上提供一個 RESTful JSON API 的反向代理 gateway,最後經過 swagger ui 來提供 RESTful JSON API 的說明,完整代碼 helloworld_restful_swagger。css
參考 gRPC Quick Start for Python。html
運行命令,python
$ python -m pip install grpcio
Python 的 gRPC 工具箱包括 protol buffer 編譯器 protoc 和一些特定插件用於從 .proto 服務定義文件生成 gRPC server 和 client 的代碼。git
運行命令,github
$ python -m pip install grpcio-tools
在 pb 目錄下新建文件 helloworld.proto,而後在其中使用 protocol buffers 語法來編寫 gRPC 服務定義。文件內容以下:golang
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; }
使用下面命令來生成 gRPC 的 server 和 client 類定義代碼文件,shell
$ python -m grpc.tools.protoc -I. --python_out=. --grpc_python_out=. pb/helloworld.proto
命令沒有報錯的話,將會在 pb 目錄下生成兩個文件 helloworld_pb2.py 和 helloworld_pb2_grpc.py。json
其中文件 helloworld_pb2.py 包含了 HelloRequest 和 HelloReploy 的消息類定義,而文件 helloworld_pb2_grpc.py 提供了 gRPC server 類(GreeterServicer)和 client 類(GreeterStub)定義。api
文件 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...
文件 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 服務已經可用。
調用 gRPC 服務須要本身編寫相對應的 client 代碼才行,這無疑給訪問 gRPC 帶來了必定的難度。咱們能夠經過在 gRPC 服務上面提供一個 RESTful API gateway,能夠直接經過 RESTful JSON API 來訪問。
grpc-gateway 是 protoc 的一個插件,用於讀取 gRPC 服務定義,而後生成一個反向代理服務來將 RESTful JSON API 轉換爲 gRPC 調用。
確保你本地安裝了 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,添加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 類文件,
$ 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 文件 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 已經可用了。
經過下面命令能夠生成 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 提供了可視化的 API 說明。咱們能夠在 RESTful JSON API gateway 中添加 swagger-ui。
將 Swagger 源碼的 dist 目錄下包含了 swagger ui 所需的 HTML、css 和 js 代碼文件,咱們將該目錄下的全部文件拷貝到 third_party/swagger-ui 目錄下。
咱們可使用 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
使用 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
********************************************************