gRPC 同时提供 Restful API 接口
gRPC支持很多语言,但是种种原因,要么对方的语言不支持,要么老项目无法改造,这时就需要提供Restful API 接口。如果重新写一套肯定是不划算的,这时候使用gRPC-Gateway
,只需要在现有的gRPC项目做稍许修改就可以轻松实现Restful API 接口
引用etcd文档中的一段话
为什么你应该考虑使用gRPC网关? etcd v3使用gRPC作为其消息传递协议。etcd项目包括一个基于gRPC的Go客户端和一个命令行实用程序etcdctl,用于通过gRPC与etcd集群通信。对于不支持gRPC的语言,etcd提供JSON gRPC网关。此网关服务于RESTful代理,将HTTP/JSON请求转换为gRPC消息。
安装
编译
首先项目启用了Go Modules
,随便新建一个文件,比如tmp.go
,内容如下
// +build tools
package tools
import (
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway"
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2"
)
然后运行go mod tidy
来解析依赖。再通过运行以下命令来安装
go install \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
这时,你应该可以看到$GOBIN
目录多了以下两个文件
protoc-gen-grpc-gateway
protoc-gen-openapiv2
这里默认你已经提前装好了protoc-gen-go-grpc
和protoc-gen-go
最后,上面创建的tmp.go
文件就可以删除了
下载二进制文件
https://github.com/grpc-ecosystem/grpc-gateway/releases
生成代码
protoc
需要从 googleapis存储库 复制几个文件
google/api/annotations.proto
google/api/field_behavior.proto
google/api/http.proto
google/api/httpbody.proto
本地目录如下
cuiwei@weideMacBook-Pro grpc-demo % tree protobuf
protobuf
├── google
│ └── api
│ ├── annotations.proto
│ ├── field_behavior.proto
│ ├── http.proto
│ └── httpbody.proto
└── helloworld.proto
然后修改helloworld.proto
syntax = "proto3";
package your.service.v1;
option go_package = "github.com/yourorg/yourprotos/gen/go/your/service/v1";
+
+import "google/api/annotations.proto";
+
message StringMessage {
string value = 1;
}
service YourService {
- rpc Echo(StringMessage) returns (StringMessage) {}
+ rpc Echo(StringMessage) returns (StringMessage) {
+ option (google.api.http) = {
+ post: "/v1/example/echo"
+ body: "*"
+ };
+ }
}
最后生成代码
cuiwei@weideMacBook-Pro protobuf % protoc --go_out=. ./helloworld.proto
cuiwei@weideMacBook-Pro protobuf % protoc --go-grpc_out=require_unimplemented_servers=false:. ./helloworld.proto
cuiwei@weideMacBook-Pro protobuf % protoc -I . --grpc-gateway_out ../gen \
--grpc-gateway_opt logtostderr=true \
--grpc-gateway_opt paths=source_relative \
--grpc-gateway_opt generate_unbound_methods=true \
helloworld.proto
buf
安装请移步:https://www.cuiwei.net/p/1679512807
gw_mapping.yaml
type: google.api.Service
config_version: 3
http:
rules:
- selector: blog.v1.BlogService.AdminDetail
get: "/v1/admin/{admin_id}"
- selector: blog.v1.BlogService.AdminChange
put: "/v1/admin/{admin_id}"
- selector: blog.v1.BlogService.AdminChangepasswd
put: "/v1/admin/{admin_id}/password"
- selector: blog.v1.BlogService.AdminAvatar
put: "/v1/admin/{admin_id}/avatar"
- selector: blog.v1.BlogService.AdminLogout
delete: "/v1/admin/{admin_id}/logout"
buf.gen.yaml
version: v1
managed:
enabled: true
go_package_prefix:
default: ent/gen/proto/go
plugins:
- name: grpc-gateway
out: gen/proto/go
opt:
- paths=source_relative
- grpc_api_configuration=blogapis/blog/v1/gw_mapping.yaml
- allow_repeated_fields_in_body=true
- generate_unbound_methods=true
- name: openapiv2
out: gen/proto/go
opt:
- grpc_api_configuration=blogapis/blog/v1/gw_mapping.yaml
- allow_repeated_fields_in_body=true
创建入口文件
这里命名httpserver.go
package main
import (
"context"
"flag"
"net/http"
"github.com/golang/glog"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
gw "github.com/yourorg/yourrepo/proto/gen/go/your/service/v1/your_service" // Update
)
var (
grpcServerEndpoint = flag.String("grpc-server-endpoint", "localhost:9090", "gRPC server endpoint")
)
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// Register gRPC server endpoint
// Note: Make sure the gRPC server is running properly and accessible
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
err := gw.RegisterYourServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
if err != nil {
return err
}
// Start HTTP server (and proxy calls to gRPC server endpoint)
return http.ListenAndServe(":8081", mux)
}
func main() {
flag.Parse()
defer glog.Flush()
if err := run(); err != nil {
glog.Fatal(err)
}
}
以上代码需要根据实际情况做些修改
最后,执行go run httpserver.go
,没意外的话服务就起来了,然后以post方式请求http://localhost:8081/v1/example/echo
就能看到结果了
参考
https://github.com/grpc-ecosystem/grpc-gateway
https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/examples/