GRPC-Gateway
https://github.com/grpc-ecosystem/grpc-gateway
https://gitee.com/go-ecosystem/grpc-gateway.git
https://gitee.com/tkxiong/grpc-go.git
install
git clone
https://gitee.com/go-ecosystem/grpc-gateway.git
cd grpc-gateway/protoc-gen-grpc-gatewqy
go build
cp protoc-gen-grpc-gateway /bin
cd ../cd protoc-gen-openapiv2/
go build
cp protoc-gen-openapiv2 /bin
git clone https://github.com/grpc/grpc-go.git
cd grpc-go/cmd/protoc-gen-go-grpc
go build
cp protoc-gen-go-grpc /bin
编译
拷贝$GOMODCACHE/github.com/grpc-ecosystem/grpc-gateway@v1.16.0/third_party/googleapis/google 到项目proto目录下
syntax="proto3";
option go_package="../pb";
import "google/api/annotations.proto";
message ReqMsg{
string value=1;
}
message ResMsg{
string value=1;
}
message FileBinary{
bytes data = 1;
}
service TripService{
rpc Echo(ReqMsg) returns (ResMsg){
option (google.api.http)={
post : "/v1/pb/echo"
body : "*"
};
}
//获取数据流
rpc DownFile (ReqMsg) returns (stream FileBinary){
option (google.api.http)={
post : "/v1/pb/getfile"
body : "*"
};
}
}
protoc --go_out=plugins=grpc:./ trip.proto
protoc --grpc-gateway_out=logtostderr=true:./ trip.proto //trip.pb.gw.go
//生成trip.pb.go和trip_grpc.pb.go
protoc -I ./ --go_out ../pb --go_opt paths=source_relative --go-grpc_out ../pb --go-grpc_opt paths=source_relative trip.proto
//trip.pb.gw.go
protoc -I . --grpc-gateway_out ../pb --grpc-gateway_opt logtostderr=true --grpc-gateway_opt paths=source_relative --grpc-gateway_opt generate_unbound_methods=true trip.proto
build.sh
#!/bin/bash
#生成普通的pb文件 /trip.pb.go和trip_grpc.pb.go
protoc -I ./ --go_out ../pb --go_opt paths=source_relative --go-grpc_out ../pb --go-grpc_opt paths=source_relative $1
#生成反向代理的pb网关文件 trip.pb.gw.go
protoc -I . --grpc-gateway_out ../pb --grpc-gateway_opt logtostderr=true --grpc-gateway_opt paths=source_relative --grpc-gateway_opt generate_unbound_methods=true $1
build.sh trip.proto
server.go
package main
import (
"context"
"log"
"net"
"grpcStream/grpcGateway/pb"
"google.golang.org/grpc"
)
const (
port = ":8088"
)
type server struct {
pb.UnimplementedTripServiceServer
}
func (s *server) Echo(ctx context.Context, req *pb.ReqMsg) (*pb.ResMsg, error) {
return &pb.ResMsg{Value: req.GetValue()}, nil
}
func (s *server) DownFile(req *pb.ReqMsg, rep pb.TripService_DownFileServer) error {
if req.Value == "" {
return errors.New("input params empty")
}
fileBuffer, err := ioutil.ReadFile(req.Value)
if err != nil {
return err
}
/*数据过大会报错
err = rep.Send(&pb.FileBinary{Data: fileBuffer})
if err != nil {
log.Println("err:", err.Error())
} */
//循环发送
byteOnce := 50 //每次发送50字节
index := 0 //第几次发送
cnt := 0 //总发送数据量
for {
//如果最后一次发送大于最大长度,认为最后一次发送
if index*byteOnce+byteOnce >= len(fileBuffer) {
if err := rep.Send(&pb.FileBinary{
Data: fileBuffer[index*byteOnce:],
}); err != nil {
log.Panicln("send failed:", err.Error())
return err
}
cnt += len(fileBuffer) - index*byteOnce
break
} else {
//正常的每次50个字节
if err := rep.Send(&pb.FileBinary{
Data: fileBuffer[index*byteOnce : index*byteOnce+byteOnce],
}); err != nil {
log.Panicln("send failed:", err.Error())
return err
}
cnt += byteOnce
}
}
log.Panicln("send bytes len:", cnt)
return nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v\n", err)
}
s := grpc.NewServer()
pb.RegisterTripServiceServer(s, &server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
client.go
package main
import (
"context"
"log"
"time"
"grpcStream/grpcGateway/pb"
"google.golang.org/grpc"
)
const (
addr = "localhost:8088"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
conn, err := grpc.DialContext(ctx, addr, grpc.WithBlock(), grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v\n", err)
}
defer conn.Close()
// c := pb.NewEchoServiceClient(conn)
c := pb.NewTripServiceClient(conn)
log.Printf("echo request: wang\n")
r, err := c.Echo(ctx, &pb.ReqMsg{Value: "wang"})
if err != nil {
log.Fatalf("could not echo: %v\n", err)
}
log.Printf("Echo reply: %s\n", r.GetValue())
}
httpserver.go
package main
import (
"context"
"flag"
"net/http"
gw "grpcStream/grpcGateway/pb"
"github.com/golang/glog"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
)
var (
grpcServerEndpoint = flag.String("grpc-server-endpoint", "localhost:8088", "gRPC server endpoint")
)
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
err := gw.RegisterTripServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
if err != nil {
return err
}
return http.ListenAndServe(":8081", mux)
}
func main() {
flag.Parse()
defer glog.Flush()
if err := run(); err != nil {
glog.Fatal(err)
}
}
curl -XPOST --data '{"value":"wangzhilei"}' http://192.168.11.140:18081/v1/pb/echo
{"value":"wangzhilei"}
获取数据流
[root@localhost gRPCStream]# curl -XPOST --data '{"value":"server.go"}' http://192.168.11.140:18081/v1/pb/getfile
{"result":{"data":"LyoKICogQERlc2NyaXB0dGlvbjoKICogQHZlcnNpb246CiAqIEBBdXRob3I6IHdhbmd6aGlsZWkKICogQERhdGU6IDIwMjEtMDktMTUgMTY6MDc6NTUKICogQExhc3RFZGl0b3JzOiB3YW5nemhpbGVpCiAqIEBMYXN0RWRpdFRpbWU6IDIwMjEtMDktMTYgMDk6MjY6NDUKICovCnBhY2thZ2UgbWFpbgoKaW1wb3J0ICgKCSJjb250ZXh0IgoJImVycm9ycyIKCSJmbXQiCgkiaW8vaW91dGlsIgoJImxvZyIKCSJuZXQiCgoJImdycGNTdHJlYW0vZ3JwY0dhdGV3YXkvcGIiCgoJImdvb2dsZS5nb2xhbmcub3JnL2dycGMiCikKCmNvbnN0ICgKCXBvcnQgPSAiOjgwODgiCikKCnR5cGUgc2VydmVyIHN0cnVjdCB7CglwYi5VbmltcGxlbWVudGVkVHJpcFNlcnZpY2VTZXJ2ZXIKfQoKZnVuYyAocyAqc2VydmVyKSBFY2hvKGN0eCBjb250ZXh0LkNvbnRleHQsIHJlcSAqcGIuUmVxTXNnKSAoKnBiLlJlc01zZywgZXJyb3IpIHsKCWZtdC5QcmludGxuKCJyZXE6IiwgcmVxLkdldFZhbHVlKCkpCglyZXR1cm4gJnBiLlJlc01zZ3tWYWx1ZTogcmVxLkdldFZhbHVlKCl9LCBuaWwKfQpmdW5jIChzICpzZXJ2ZXIpIERvd25GaWxlKHJlcSAqcGIuUmVxTXNnLCByZXAgcGIuVHJpcFNlcnZpY2VfRG93bkZpbGVTZXJ2ZXIpIGVycm9yIHsKCWlmIHJlcS5WYWx1ZSA9PSAiIiB7CgkJcmV0dXJuIGVycm9ycy5OZXcoImlucHV0IHBhcmFtcyBlbXB0eSIpCgl9CglmaWxlQnVmZmVyLCBlcnIgOj0gaW91dGlsLlJlYWRGaWxlKHJlcS5WYWx1ZSkKCWlmIGVyciAhPSBuaWwgewoJCXJldHVybiBlcnIKCX0KCWVyciA9IHJlcC5TZW5kKCZwYi5GaWxlQmluYXJ5e0RhdGE6IGZpbGVCdWZmZXJ9KQoJaWYgZXJyICE9IG5pbCB7CgkJbG9nLlByaW50bG4oImVycjoiLCBlcnIuRXJyb3IoKSkKCX0KCXJldHVybiBlcnIKfQoKZnVuYyBtYWluKCkgewoJbGlzLCBlcnIgOj0gbmV0Lkxpc3RlbigidGNwIiwgcG9ydCkKCWlmIGVyciAhPSBuaWwgewoJCWxvZy5GYXRhbGYoImZhaWxlZCB0byBsaXN0ZW46ICV2XG4iLCBlcnIpCgl9CgoJcyA6PSBncnBjLk5ld1NlcnZlcigpCglwYi5SZWdpc3RlclRyaXBTZXJ2aWNlU2VydmVyKHMsICZzZXJ2ZXJ7fSkKCglpZiBlcnIgOj0gcy5TZXJ2ZShsaXMpOyBlcnIgIT0gbmlsIHsKCQlsb2cuRmF0YWxmKCJmYWlsZWQgdG8gc2VydmU6ICV2IiwgZXJyKQoJfQp9Cg=="}}
解码
[root@localhost gRPCStream]# echo "LyoKICogQERlc2NyaXB0dGlvbjoKICogQHZlcnNpb246CiAqIEBBdXRob3I6IHdhbmd6aGlsZWkKICogQERhdGU6IDIwMjEtMDktMTUgMTY6MDc6NTUKICogQExhc3RFZGl0b3JzOiB3YW5nemhpbGVpCiAqIEBMYXN0RWRpdFRpbWU6IDIwMjEtMDktMTYgMDk6MjY6NDUKICovCnBhY2thZ2UgbWFpbgoKaW1wb3J0ICgKCSJjb250ZXh0IgoJImVycm9ycyIKCSJmbXQiCgkiaW8vaW91dGlsIgoJImxvZyIKCSJuZXQiCgoJImdycGNTdHJlYW0vZ3JwY0dhdGV3YXkvcGIiCgoJImdvb2dsZS5nb2xhbmcub3JnL2dycGMiCikKCmNvbnN0ICgKCXBvcnQgPSAiOjgwODgiCikKCnR5cGUgc2VydmVyIHN0cnVjdCB7CglwYi5VbmltcGxlbWVudGVkVHJpcFNlcnZpY2VTZXJ2ZXIKfQoKZnVuYyAocyAqc2VydmVyKSBFY2hvKGN0eCBjb250ZXh0LkNvbnRleHQsIHJlcSAqcGIuUmVxTXNnKSAoKnBiLlJlc01zZywgZXJyb3IpIHsKCWZtdC5QcmludGxuKCJyZXE6IiwgcmVxLkdldFZhbHVlKCkpCglyZXR1cm4gJnBiLlJlc01zZ3tWYWx1ZTogcmVxLkdldFZhbHVlKCl9LCBuaWwKfQpmdW5jIChzICpzZXJ2ZXIpIERvd25GaWxlKHJlcSAqcGIuUmVxTXNnLCByZXAgcGIuVHJpcFNlcnZpY2VfRG93bkZpbGVTZXJ2ZXIpIGVycm9yIHsKCWlmIHJlcS5WYWx1ZSA9PSAiIiB7CgkJcmV0dXJuIGVycm9ycy5OZXcoImlucHV0IHBhcmFtcyBlbXB0eSIpCgl9CglmaWxlQnVmZmVyLCBlcnIgOj0gaW91dGlsLlJlYWRGaWxlKHJlcS5WYWx1ZSkKCWlmIGVyciAhPSBuaWwgewoJCXJldHVybiBlcnIKCX0KCWVyciA9IHJlcC5TZW5kKCZwYi5GaWxlQmluYXJ5e0RhdGE6IGZpbGVCdWZmZXJ9KQoJaWYgZXJyICE9IG5pbCB7CgkJbG9nLlByaW50bG4oImVycjoiLCBlcnIuRXJyb3IoKSkKCX0KCXJldHVybiBlcnIKfQoKZnVuYyBtYWluKCkgewoJbGlzLCBlcnIgOj0gbmV0Lkxpc3RlbigidGNwIiwgcG9ydCkKCWlmIGVyciAhPSBuaWwgewoJCWxvZy5GYXRhbGYoImZhaWxlZCB0byBsaXN0ZW46ICV2XG4iLCBlcnIpCgl9CgoJcyA6PSBncnBjLk5ld1NlcnZlcigpCglwYi5SZWdpc3RlclRyaXBTZXJ2aWNlU2VydmVyKHMsICZzZXJ2ZXJ7fSkKCglpZiBlcnIgOj0gcy5TZXJ2ZShsaXMpOyBlcnIgIT0gbmlsIHsKCQlsb2cuRmF0YWxmKCJmYWlsZWQgdG8gc2VydmU6ICV2IiwgZXJyKQoJfQp9Cg==" | base64 -d
yaml
type: google.api.Service
config_version: 3
http:
rules:
- selector: pb.TripService.GetTrip
get: /trip/{id}
protoc --grpc-gateway_out=paths=source_relative,grpc_api_configuration=trip.yaml:./ trip.proto