因為之前的測試結果讓筆者對PHP使用GRPC很失望友瘤,如果使用HTTP的方式Guzzle還可以提供并發(fā)能力來降低接口消耗扮饶,Grpc只能阻塞,但是如果使用HTTP的話對于調用限制和GO調用GO來說不太友好频敛,有沒有一個兩全其美的方式可以使用Grpc的前提下又兼容HTTP調用冈钦,讓客戶端可以更具自身情況自由選擇专挪,服務端工作只需要做一份呢授霸?還別說真還有一個準備好的輪子那就是今天的主角《grpc-gateway》裂允。
附上:
喵了個咪的博客:w-blog.cn
博文實例demo:https://github.com/sunmi-OS/grpc-gateway-demo
grpc-gateway官網:https://github.com/grpc-ecosystem/grpc-gateway
一里烦,grpc-gateway介紹
grpc-gateway是protoc的一個插件 凿蒜。它讀取Grpc服務定義,并生成反向代理服務器胁黑,將RESTful JSON API請求轉換為Grpc的方式調用废封。主要是根據 google.api.http定義中思想完成的,一下就是grpc-gateway結構圖:
二丧蘸,grpc-gateway環(huán)境準備
grpc-gateway使用完全的Go語言進行開發(fā)漂洋,所以安裝起來也非常簡單,首先需要獲取相關的依賴包
PS:需要先準備好準備好protoc的環(huán)境
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
cd $GOPATH/src/
mkdir -p grpc-gateway-demo/gateway
cd grpc-gateway-demo/gateway
vim gateway.proto
syntax = "proto3";
package gateway;
# 新增以下引入
import "google/api/annotations.proto";
message StringMessage {
string value = 1;
}
# 修改方法增加http定義
# service Gateway {
# rpc SayHello Echo(StringMessage) returns (StringMessage) {}
# }
service Gateway {
rpc Echo(StringMessage) returns (StringMessage) {
option (google.api.http) = {
post: "/v1/example/echo"
body: "*"
};
}
}
生成grpc結構文件和gateway文件:
protoc --proto_path=../ -I/usr/local/include -I. -I$GOPATH/src -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --go_out=plugins=grpc:. gateway.proto
protoc --proto_path=../ -I/usr/local/include -I. -I$GOPATH/src -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --grpc-gateway_out=logtostderr=true:. gateway.proto
最終可以看到以下文件
二力喷,編寫grpc-gateway服務
服務端代碼:
cd ..
vim grpc_service.go
package main
import (
"log"
"net"
pb "grpc-gateway-demo/gateway"
"google.golang.org/grpc"
"golang.org/x/net/context"
)
const (
PORT = ":9192"
)
type server struct {}
func (s *server) Echo(ctx context.Context, in *pb.StringMessage) (*pb.StringMessage, error) {
log.Println("request: ", in.Value)
return &pb.StringMessage{Value: "Hello " + in.Value}, nil
}
func main() {
lis, err := net.Listen("tcp", PORT)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGatewayServer(s, &server{})
log.Println("rpc服務已經開啟")
s.Serve(lis)
}
運行grpc服務端:
go build grpc_service.go
./grpc_service
編寫gateway服務
vim grpc_gateway.go
package main
import (
"flag"
"net/http"
"log"
"github.com/golang/glog"
"golang.org/x/net/context"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"google.golang.org/grpc"
gw "grpc-gateway-demo/gateway"
)
var (
echoEndpoint = flag.String("echo_endpoint", "localhost:9192", "endpoint of Gateway")
)
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
err := gw.RegisterGatewayHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts)
if err != nil {
return err
}
log.Println("服務開啟")
return http.ListenAndServe(":8080", mux)
}
func main() {
flag.Parse()
defer glog.Flush()
if err := run(); err != nil {
glog.Fatal(err)
}
}
運行網關程序
go build grpc_gateway.go
./grpc_gateway
使用http的方式調用網關:
curl -X POST -k http://localhost:8080/v1/example/echo -d '{"value":" world"}'
{"value":"Hello world"}
四刽漂,使用gateway生成swagger文檔
cd 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:. \
gateway.proto
五,性能對比
對比以下兩項:
- http -> go -> grpc -> go
- http -> go -> http -> grpc_gateway -> grpc -> go
全程使用ab 帶 -k進行壓測
http -> go -> grpc -> go
http -> go -> http -> grpc_gateway -> grpc -> go
六弟孟,總結
在GO的場景下基本上4倍差距贝咙,但是考慮到本身Go在grpc和http上本身就有3.5倍的差距,本身在同等HTTP的情況下經過grpc-gateway和不經過直接到API差距大概在20~30%左右拂募,這樣的性能消耗帶來的是兼容HTTP并且還可以自動生成swagger(還可以作為調試工具)庭猩,何樂而不為呢?