- 这篇文章里我们要实现一个基于GoLang编程语言的gRPC的客户端与服务端通信的HelloWorld案例
编写hello_world.proto文件,如下代码:
syntax proto3
package proto
//接口请求入参
message HelloRequest{
string request = 1;
}
//接口返回出参
message HelloResponse{
string response = 1;
}
//定义接口
service HelloService{
//一个简单的rpc
rpc HelloWorld(HelloRequest) returns (HelloResponse){}
//一个服务器端流式rpc
rpc HelloWorldServerStream(HelloRequest) returns (stream HelloResponse){}
//一个客户端流式rpc
rpc HelloWorldClientStream(stream HelloRequest) returns (HelloResponse){}
//一个客户端和服务器端双向流式rpc
rpc HelloWorldClientAndServerStream(stream HelloRequest)
returns (stream HelloResponse){}
}
- 如上代码,通过protobuffer的service定义一个接口HelloService,接口中有四个方法都以HelloWorld开头,入参是HelloRequest,出参是HelloResponse,通过最前面的rpc关键字标识为这是一个rpc接口
编译hello_world.proto文件生成对应的go文件
- 笔者在go_common项目里新建了grpc项目结构如下代码
- 进入项目之后可以先阅读README.md,了解各个包的功能
go_common
grpc
helloworld_new
client //存放客户端代码
proto 存放proto文件和生成的go文件
server存放server文件
- cd go_common/grpc/helloworld_new/proto,进入proto文件夹下
- 执行命令:protoc --go_out=plugins=grpc:. hello_world.proto
- 最后生成hello_world.pb.go文件,主要有以下几部分组成:
- 方法出入参结构体以及序列化和反序列方法
- 注册出入参结构体的init方法
- 客户端存根结构体和接口以及实现
- 服务端结构体和接口以及一个空实现
- stream的send和recv结构体和接口以及实现
- 服务的一些描述
- 代码太多,这里粘一些核心代码,并且简化了一些出入参,完成代码参考github上的源码
//包名
package proto
//接口请求入参
type HelloRequest struct {
Request string
}
//接口返回出参
type HelloResponse struct {
Response string
}
//生成的helloServce的客户端接口,就跟java的api是的,让别人引用
type HelloServiceClient interface {
//一个简单的
rpc HelloWorld(in *HelloRequest) (*HelloResponse, error)
//一个服务器端流式
rpc HelloWorldServerStream(in *HelloRequest) (ServerStream,error)
//一个客户端流式
rpc HelloWorldClientStream() (ClientStream, error)
//一个客户端和服务器端双向流式
rpc HelloWorldClientAndServerStream() (ServerClientStream error)
}
//helloServiceClient结构体
type helloServiceClient struct {
cc *grpc.ClientConn
}
//获取客户端存根,入参是一个client连接
func NewHelloServiceClient(cc *grpc.ClientConn) HelloServiceClient {
return &helloServiceClient{cc}
}
//这是服务器端的接口,我们的服务端需要实现这些接口
type HelloServiceServer interface {
//一个简单的
rpc HelloWorld(in *HelloRequest) (*HelloResponse, error)
//一个服务器端流式
rpc HelloWorldServerStream(*HelloRequest, StreamServer) error
//一个客户端流式
rpc HelloWorldClientStream(ClientStream) error
//一个客户端和服务器端双向流式
rpc HelloWorldClientAndServerStream (ClientServerStream) error
}
编写服务端代码
- 在server文件夹下新建hello_world_server.go文件,按如下步骤进行
- 创建HelloWorldServer结构体
- 实现pb.go文件中的HelloServiceServer接口(实现所有方法)
- StartServer(开启服务)
- 详细代码依然看github
type HelloServiceServer struct {}
//简单rpc实现
func (*HelloServiceServer) HelloWorld() {
log.Printf("%v",req.Request)
return &pb.HelloResponse{Response:"hello my is gRpcServer"}, nil
}
//服务端流式rpc实现
func (*HelloServiceServer) HelloWorldServerStream() error {
log.Printf("%v",req.Request)
srv.Send(&pb.HelloResponse{Response:"hello my is gRpcServer stream"})
return nil
}
//客户端流式rpc实现
func (*HelloServiceServer) HelloWorldClientStream() error {
for{
//不断接受客户端发送的消息
req,err := srv.Recv()
//直到发送结束终止for循环
if err != nil && err.Error() == "EOF"{
break
}
if err != nil{
log.Fatalf("%v",err)
break
}else{
log.Printf("%v",req.Request)
}
}
//返回给客户端处理结果
srv.SendAndClose(&pb.HelloResponse{Response:"hello my is gRpcServer"})
return nil
}
//双向流式rpc
func (*HelloServiceServer) HelloWorldClientAndServerStream(){
for{
//接受客户端消息
req,err := srv.Recv()
if err != nil && err.Error() == "EOF"{
break
}
if err != nil{
log.Fatalf("%v",err)
break
}else{
log.Printf("%v",req.Request)
//返回客户端结果
srv.Send(&pb.HelloResponse{Response:"hello my is gRpcServer stream"})
}
}
return nil
}
func StartServer(){
//开启服务端监听
lis, err := net.Listen("tcp", "127.0.0.1:8090")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
//创建grpcServer
gRpcServer := grpc.NewServer()
//注册服务实现
pb.RegisterHelloServiceServer(gRpcServer, &HelloServiceServer{})
//启动服务
gRpcServer.Serve(lis)
}
编写客户端
- 在client目录下创建hello_world_client.go文件
func StartClient(){
//连接grpc的服务端
conn, err := grpc.Dial("127.0.0.1:8090",grpc.WithInsecure())
//创建客户端存根对象
c := pb.NewHelloServiceClient(conn)
// Contact the server and print out its response.
//设置超时时间
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
//调用服务端的方法
r, err := c.HelloWorldClientAndServerStream(ctx,grpc.EmptyCallOption{})
if err != nil{
log.Fatalf("%v", err)
return
}
//客户端往服务端发送10次消息
for i:=0;i<10;i++{
r.Send(&pb.HelloRequest{Request:"my is golang gRpc client"})
}
//发送完毕
r.CloseSend()
//循环接受服务端返回的结果,直到返回EOF
for{
res,err := r.Recv()
if err != nil && err.Error() == "EOF"{
break
}
if err != nil {
log.Fatalf("%v", err)
break
}
log.Printf("result:%v",res.Response)
}
//关闭连接
defer conn.Close()
}
github代码运行说明
- github代码截图
- 先启动 hello_world_server_test.go
- 再启动hello_world_client_test.go
- 如遇到启动的任何问题可留言
欢迎大家关注微信公众号:“golang那点事”,更多精彩期待你的到来