GRPC实践
在trip.proto
中
1 2 3 4 5 6 7 8 9 10 11 12 13
| message GetTripRequest{ string id = 1; }
message GetTripResponse{ string id = 1; Trip trip = 2; }
service TripService { rpc GetTrip (GetTripRequest) returns (GetTripResponse); }
|
生成代码,为了生成service的代码框架,需要加参数plugins=grpc
1
| protoc -I=. --go_out=plugins=grpc,paths=source_relative:gen/go trip.proto
|
命令出错:
-go_out: protoc-gen-go: plugins are not supported; use 'protoc --go-grpc_out=...' to generate gRPC
版本问题解决办法:重新下载grpc
和protoc-gen-go
两个包
1 2
| go get google.golang.org/grpc go get github.com/golang/protobuf/protoc-gen-go
|
运行成功后,在trip.pb.go里面有客服端和服务端的接口
在server下面创建目录tripservice
,创建文件trip.go
来实现Service
接口
服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package trip
import ( "context" trippb "coolcar/proto/gen/go" )
type Service struct{}
func (*Service) GetTrip(c context.Context, req *trippb.GetTripRequest) (*trippb.GetTripResponse, error) { return &trippb.GetTripResponse{ Id: req.Id, Trip: &trippb.Trip{ Start: "abc", End: "dfe", DurationSec: 3600, FeeCent: 10000, }, }, nil }
|
在server下创建main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main
import ( trippb "coolcar/proto/gen/go" trip "coolcar/tripservice" "log" "net"
"google.golang.org/grpc" )
func main() { l, err := net.Listen("tcp", ":8081") if err != nil { log.Fatalf("fail to listen: %v", err) }
s := grpc.NewServer() trippb.RegisterTripServiceServer(s, &trip.Service{}) log.Fatal(s.Serve(l)) }
|
运行即可启动服务:
模拟客户端:创建client/main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package main
import ( "context" trippb "coolcar/proto/gen/go" "fmt" "log"
"google.golang.org/grpc" )
func main() { cc, err := grpc.Dial("localhost:8081", grpc.WithInsecure()) if err != nil { log.Fatalf("cannot connect server:%v", err) } tsc := trippb.NewTripServiceClient(cc) r, err := tsc.GetTrip(context.Background(), &trippb.GetTripRequest{ Id: "trip423", }) if err != nil { log.Fatalf("cannot call GetTrip:%v", err) } fmt.Println(r) }
|
在TERMINAL运行:
服务器内部GRPC都用TCP连接,要将GRPC暴露给外部使用,下图有两种方法。web转二进制其实不太方便,JavaScript和HTTP设计之初都是为了文本所考虑的。
GRPC Gateway实现
gen.bat文件是生成命令:
1 2 3
| protoc -I=. --go_out=plugins=grpc,paths=source_relative:gen/go trip.proto
protoc -I=. --grpc-gateway_out=paths=source_relative,grpc_api_configuration=trip.yaml:gen/go trip.proto
|
trip.yaml
是配置文件:
1 2 3 4 5 6 7
| type: google.api.Service config_version: 3
http: rules: - selector: coolar.TripService.GetTrip get: /trip/{id}
|
运行gen.bat
生成trip.pb.gw.go
main.go代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package main
import ( "context" trippb "coolcar/proto/gen/go" trip "coolcar/tripservice" "log" "net" "net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "google.golang.org/grpc" )
func main() { log.SetFlags(log.Lshortfile) go startGRPCGateway() l, err := net.Listen("tcp", ":8081") if err != nil { log.Fatalf("fail to listen: %v", err) }
s := grpc.NewServer() trippb.RegisterTripServiceServer(s, &trip.Service{}) log.Fatal(s.Serve(l)) }
func startGRPCGateway() { c := context.Background() c, cancel := context.WithCancel(c) defer cancel() mux := runtime.NewServeMux(runtime.WithMarshalerOption( runtime.MIMEWildcard, &runtime.JSONPb{ MarshalOptions: protojson.MarshalOptions{ UseEnumNumbers: true, UseProtoNames: true}}, )) err := trippb.RegisterTripServiceHandlerFromEndpoint( c, mux, ":8081", []grpc.DialOption{grpc.WithInsecure()}, ) if err != nil { log.Fatalf("cannot start gateway: %v", err) }
err2 := http.ListenAndServe(":8080", mux) if err2 != nil { log.Fatalf("err2:%v", err2) } }
|
运行main.go
启动服务,之后客户端调用:
在网页上