grpc服務整合RESFful JSON API grpc-gateway和swagger-ui介面
本文目的:
說明如何使用grpc-gateway(下圖紅色部分)反向代理外掛將RESTful JSON API轉換為gRPC,並使用swagger ui提供rest api介面。
整合步驟:
上圖所示,由下往上
一、HelloWorld gRPC服務
首先先有一個gRPC服務,我們這個服務使用java實現,並使用maven進行管理,具體細節可檢視git庫:
1.安裝所需軟體版本
安裝步驟不再展開,我相信你們可以安裝好的。
2.編寫Hello world gRPC服務
1)建立一個maven工程
最後的專案結構如下 (剛建立的專案沒有程式碼檔案,由接下來的步驟建立)
. ├── cmd │ ├── helloworld.pb.go │ ├── helloworld.pb.gw.go │ ├── helloworld.swagger.go │ └── swagger │ └── ui │ └── data │ └── datafile.go ├── helloworld.swagger.json ├── java-grpc-swagger.iml ├── pom.xml ├── proxy ├── proxy.go ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ └── tuputech │ │ │ └── grpc │ │ │ ├── HelloWorldClient.java │ │ │ └── HelloWorldServer.java │ │ ├── proto │ │ │ └── helloworld.proto │ │ └── resources │ │ └── swagger-ui │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── index.html │ │ ├── oauth2-redirect.html │ │ ├── swagger-ui-bundle.js │ │ ├── swagger-ui-bundle.js.map │ │ ├── swagger-ui-standalone-preset.js │ │ ├── swagger-ui-standalone-preset.js.map │ │ ├── swagger-ui.css │ │ ├── swagger-ui.css.map │ │ ├── swagger-ui.js │ │ └── swagger-ui.js.map │ └── test │ └── java
直接拷貝gRPC github上的程式碼,進行執行即可
2)拷貝gRPC的proto的檔案
helloworld.proto
https://github.com/grpc/grpc-java/tree/master/examples/src/main/proto/helloworld.proto
3)拷貝gRPC的server和client的java程式碼
HelloWorldServer.java和HelloWorldClient.java
https://github.com/grpc/grpc-java/tree/master/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java https://github.com/grpc/grpc-java/tree/master/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldClient.java
4)編譯直接執行
執行時日誌
Sep 17, 2018 8:57:00 PM com.tuputech.grpc.HelloWorldServer start
資訊: Server started, listening on 50051
二、RESTful JSON gRPC-gateway
1.安裝gprc-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
2.修改helloworld.proto檔案
新增gateway的選項
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: "*"
};
}
}
3.生成grpc golang stub類檔案
protoc -I/Users/stephen/Documents/develop/java-grpc-swagger/src/main/proto/ -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--go_out=plugins=grpc:. \
/Users/stephen/Documents/develop/java-grpc-swagger/src/main/proto/helloworld.proto
對應生成helloworld.pb.go檔案
4.生成反向代理程式碼
protoc -I/Users/stephen/Documents/develop/java-grpc-swagger/src/main/proto/ -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--plugin=protoc-gen-grpc=grpc_ruby_plugin \
--grpc-gateway_out=logtostderr=true:. \
/Users/stephen/Documents/develop/java-grpc-swagger/src/main/proto/helloworld.proto
對應生成helloworld.pb.gw.go
把以上生成的go檔案移到cmd的資料夾下面
5.編寫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 "../java-grpc-swagger/cmd"
)
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)
}
}
編譯生成可執行檔案proxy
go build proxy.go
6.啟動服務
1)啟動grpc服務
Sep 18, 2018 10:51:35 AM com.tuputech.grpc.HelloWorldServer start
資訊: Server started, listening on 50051
2)啟動RESTful JSON API gateway
➜ java-grpc-swagger proxy
2018/09/18 10:27:57 Greeter gRPC Server gateway start at port 8080...
3)使用curl訪問
➜ ~ curl -X POST "http://localhost:8080/v1/hello" -H "accept: application/json" -H "Content-Type: application/json" -d "{ \"name\": \"string\"}"
{"message":"Hello string"}%
三、整合swagger-ui
1.生成RESTful JSON API的Swagger說明
protoc -I/Users/stephen/Documents/develop/java-grpc-swagger/src/main/proto/ -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--swagger_out=logtostderr=true:. \
/Users/stephen/Documents/develop/java-grpc-swagger/src/main/proto/helloworld.proto
對應生成helloworld.swagger.json檔案。在cmd的目錄下新建helloworld.swagger.go,定義常量為Swagger,常量值為helloworld.swagger.json內容
package helloworld
const (
Swagger = `
{
"swagger": "2.0",
"info": {
"title": "helloworld.proto",
"version": "version not set"
},
"schemes": [
"http",
"https"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/v1/hello": {
"post": {
"summary": "Sends a greeting",
"operationId": "SayHello",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/helloworldHelloReply"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/helloworldHelloRequest"
}
}
],
"tags": [
"Greeter"
]
}
}
},
"definitions": {
"helloworldHelloReply": {
"type": "object",
"properties": {
"message": {
"type": "string"
}
},
"title": "The response message containing the greetings"
},
"helloworldHelloRequest": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"description": "The request message containing the user's name."
}
}
}
`
)
2.修改proxy.go程式碼
新增方法返回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
}
重新編譯並執行proxy。使用瀏覽器訪問http://localhost:8080/swagger.json,即可得到hello world RESful API的具體說明。
為了將API更直觀顯示出來,我們將swagger-ui安裝到我們的gateway裡面。
3.下載swagger github原始碼
git clone https://github.com/swagger-api/swagger-ui.git
將dist目錄下的所有檔案拷貝到專案目錄resources/swagger-ui裡面
const ui = SwaggerUIBundle({
// url: "https://petstore.swagger.io/v2/swagger.json",
url: "http://localhost:8080/swagger.json",
4.將swagger-ui檔案編譯成go的內建檔案:
1)安裝go-bindata工具
go get -u github.com/jteeuwen/go-bindata/...
2)製作成go的內建資料檔案
go-bindata --nocompress -pkg swagger -o cmd/swagger/ui/data/datafile.go src/main/resources/swagger-ui/...
5.swagger-ui的檔案伺服器
1)elazarl/go-bindata-assetf將內建的資料檔案對外提供http服務
go get github.com/elazarl/go-bindata-assetfs/...
2)修改proxy.go程式碼,新增swagger函式
3)重新編譯proxy.go
go build proxy.go
4)重新啟動gateway服務
使用瀏覽器檢視swagger-ui
http://localhost:8080/swagger-ui
總結
本文至此,已完成grpc服務整合RESTful Json grpc-gateway反向代理和提供swagger-ui介面的配置。