《GO语言高级编程》设计中案例,仅作为笔记进行收藏。gRPC服务一般用于集群内部通信,如果需要对外暴露服务一般会提供等价的REST接口。通过REST接口比较方便前端JavaScript和后端交互。开源社区中的grpcgateway项目就实现了将gRPC服务转为REST服务的能力。
grpc-gateway的工作原理如下图:
通过在Protobuf文件中添加路由相关的元信息,通过自定义的代码插件生成路由相关的处理代码,最终将REST请求转给更后端的gRPC服务处理。
google/api/annotations.proto 采用 https://github.com/googleapis/googleapis 代替
go get -u github.com/grpc-ecosystem/grpc-gateway/tree/master/protoc-gen-grpc-gateway
go get -u github.com/grpc-ecosystem/grpc-gateway/tree/master/protoc-gen-swagger
go get -u github.com/swagger-api/swagger-ui
syntax = "proto3";
package main;
import "google/api/annotations.proto";
message StringMessage{
string value=1;
}
service RestService{
rpc Get(StringMessage) returns(StringMessage){
option(google.api.http)={
get:"/get/{value}"
};
}
rpc Post(StringMessage) returns(StringMessage){
option(google.api.http)={
post:"/post"
body:"*"
};
}
}
protoc
--proto_path=.
--proto_path=%GOPATH%github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis
--go_out=plugins=grpc:. helloworld.proto
protoc
--proto_path=.
--proto_path=%GOPATH%github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis
--grpc-gateway_out=. helloworld.proto
protoc
--proto_path=.
--proto_path=%GOPATH%github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis
--swagger_out=. helloworld.proto
package main
import (
"flag"
"log"
"net"
"net/http"
"github.com/golang/glog"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
var (
port = ":5000"
echoEndpoint=flag.String("echo_endpoint","localhost"+port,"endpoint of YourService")
)
type myGrpcServer struct {}
func(s *myGrpcServer) Get(ctx context.Context,in *StringMessage)(*StringMessage,error){
return &StringMessage{Value: "Get:"+in.Value},nil
}
func(s *myGrpcServer) Post(ctx context.Context,in *StringMessage)(*StringMessage,error){
return &StringMessage{Value: "Post: "+in.Value},nil
}
func run() error{
ctx:=context.Background()
ctx,cancel:=context.WithCancel(ctx)
defer cancel()
// 创建路由处理器
mux:=runtime.NewServeMux()
opts:=[]grpc.DialOption{grpc.WithInsecure()}
// 将RestService服务相关的REST接口转到后面的gRPC服务
err:=RegisterRestServiceHandlerFromEndpoint(ctx,mux,*echoEndpoint,opts)
if err!=nil{
return err
}
return http.ListenAndServe(":8080",mux)
}
func main(){
flag.Parse()
defer glog.Flush()
go startGrpcServer()
if err := run(); err != nil {
glog.Fatal(err)
}
}
func startGrpcServer(){
server:=grpc.NewServer()
RegisterRestServiceServer(server,new(myGrpcServer))
lis,err:=net.Listen("tcp",port)
if err != nil {
log.Panicf("could not list on %s: %s", port, err)
}
if err := server.Serve(lis); err != nil {
log.Panicf("grpc serve error: %s", err)
}
}
curl localhost:8080/get/gopher // {"value":"Get: gopher"}
curl localhost:8080/post -X POST --data '{"value":"grpc"}' // {"value":"Post: grpc"}
从 swagger-ui库中将 dist 文件夹移到自己的项目中,并更名为swagger。把swagger>>index.html中 url 进行更改。
项目路径如下:
在main中添加代码如下:
func run() error{
ctx:=context.Background()
ctx,cancel:=context.WithCancel(ctx)
defer cancel()
// 创建路由处理器
mux:=runtime.NewServeMux()
opts:=[]grpc.DialOption{grpc.WithInsecure()}
// 将RestService服务相关的REST接口转到后面的gRPC服务
err:=RegisterRestServiceHandlerFromEndpoint(ctx,mux,*echoEndpoint,opts)
if err!=nil{
return err
}
http.Handle("/swagger/", http.StripPrefix("/swagger/", http.FileServer(http.Dir("swagger"))))
return http.ListenAndServe(":8080",mux)
}
添加过 swagger 代码后,发现不出现效果,正常的 curl 测试还是可以的。
curl localhost:8080/get/gopher
{"value":"Get: gopher"}
curl localhost:8080/post -X POST --data '{"value":"grpc"}'
{"value":"Post: grpc"}
经研究,http.ListenAndServe 采用默认 handle 时可以正常运行,但是接口无法访问。
func run() error{
ctx:=context.Background()
ctx,cancel:=context.WithCancel(ctx)
defer cancel()
// 创建路由处理器
mux:=runtime.NewServeMux()
opts:=[]grpc.DialOption{grpc.WithInsecure()}
// 将RestService服务相关的REST接口转到后面的gRPC服务
err:=RegisterRestServiceHandlerFromEndpoint(ctx,mux,*echoEndpoint,opts)
if err!=nil{
return err
}
http.Handle("/swagger/", http.StripPrefix("/swagger/", http.FileServer(http.Dir("swagger"))))
return http.ListenAndServe(":8080",nil)
}
根据现在掌握的暂时无法解决,各位如果有解决的办法,请在评论区留言,谢过。