前言
记录加入青训营的每一天的笔记
gRPC
在Go中的使用之gRPC
实现简单通讯
上节课我用protobuf
定义了两个消息HelloWorldRequest
与HelloWorldResponse
以及一个HelloWorldService
服务。
同时,我还生成了相应的go代码.pb.go
。
那么客户端与服务端怎么去通过这些接口去完成通讯呢?
下面我们一起实现一个简单的gRPC
通讯。
在RPC
通讯中,客户端使用存根(SayHelloWorld)
发送请求到服务器并且等待响应返回,整个过程就像我们平常函数调用一样。
service HelloWorldService { rpc SayHelloWorld(HelloWorldRequest) returns (HelloWorldResponse){} }
那么接下来,我们先创建一个服务端。
创建服务端
在生成的hello_world.pb.go
中,已经为我们生成了服务端的接口:
// HelloWorldServiceServer is the server API for HelloWorldService service. type HelloWorldServiceServer interface { SayHelloWorld(context.Context, *HelloWorldRequest) (*HelloWorldResponse, error) }
在服务端我们首先要做的就是实现这个接口。
package main import ( "context" "log" "net" pb "github.com/razeencheng/demo-go/grpc/demo2/helloworld" "github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes/any" "google.golang.org/grpc" ) type SayHelloServer struct{} func (s *SayHelloServer) SayHelloWorld(ctx context.Context, in *pb.HelloWorldRequest) (res *pb.HelloWorldResponse, err error) { log.Printf("Client Greeting:%s", in.Greeting) log.Printf("Client Info:%v", in.Infos) var an *any.Any if in.Infos["hello"] == "world" { an, err = ptypes.MarshalAny(&pb.HelloWorld{Msg: "Good Request"}) } else { an, err = ptypes.MarshalAny(&pb.Error{Msg: []string{"Bad Request", "Wrong Info Msg"}}) } if err != nil { return } return &pb.HelloWorldResponse{ Reply: "Hello World !!", Details: []*any.Any{an}, }, nil }
简单如上面的几行,实现了这个接口我们只需要创建一个结构SayHelloServer
,同时实现HelloWorldServiceServer
的所有方法即可。
这里为了演示效果我打印了一些数据,同时利用any.Any
在不同的情况下返回不同的类型数据。
当然,只是现实了接口还不够,我们还需要启动一个服务,这样客户端才能使用该服务。启动服务很简单,就像我们平常启用一个Server一样。
func main() { // 我们首先须监听一个tcp端口 lis, err := net.Listen("tcp", ":8080") if err != nil { panic(err) } // 新建一个grpc服务器 grpcServer := grpc.NewServer() // 向grpc服务器注册SayHelloServer pb.RegisterHelloWorldServiceServer(grpcServer, &SayHelloServer{}) // 启动服务 grpcServer.Serve(lis) }
从上面的代码,我们可以看到,简单的4步即可启动一个服务。
- 监听一个服务端口,供客户端调用;
- 创建一个grpc服务器,当然这里可以设置
授权认证
,这个在下一篇中我们将详细介绍; - 注册服务,其实是调用生存的
.pb.go
中的RegisterHelloWorldServiceServer
方法,将我们这里实现的SayHelloServer
加入到该服务中。 - 启动服务,等待客户端连接。
我们go run server.go
,无任何报错,这样一个简单的grpc
服务的服务端就准备就绪了。接下来我们看看客户端。
创建客户端
例如:
package main import ( "context" "log" "google.golang.org/grpc" pb "github.com/razeencheng/demo-go/grpc/demo2/helloworld" ) func main() { // 创建一个 gRPC channel 和服务器交互 conn, err := grpc.Dial("localhost:8080", grpc.WithInsecure()) if err != nil { log.Fatalf("Dial failed:%v", err) } defer conn.Close() // 创建客户端 client := pb.NewHelloWorldServiceClient(conn) // 直接调用 resp1, err := client.SayHelloWorld(context.Background(), &pb.HelloWorldRequest{ Greeting: "Hello Server 1 !!", Infos: map[string]string{"hello": "world"}, }) log.Printf("Resp1:%+v", resp1) resp2, err := client.SayHelloWorld(context.Background(), &pb.HelloWorldRequest{ Greeting: "Hello Server 2 !!", }) log.Printf("Resp2:%+v", resp2) }
客户端的实现比服务端更简洁,三步即可。
- 创建一个
gRPC channel
和服务器交互。这里也是可以设置授权认证
的; - 创建一个客户端去执行
RPC
。用到的也是.pb.go
内的NewHelloWorldServiceClient
方法; - 像函数调用一样去调用
RPC
服务。
我直接RUN起来,如下,我们可以看到客户端发送到服务的消息以及服务端对不同消息的不同回复。
那么到这里,我们简单的实现了一个gRPC
通讯。但很多时候,我们可能希望客户端与服务器能更安全的通信,或者客户端与服务器不再是一种固定的结构的传输,需要流式的去处理一些问题等等。