创建时间: 2019/8/24 16:35
作者: sunpengwei1992@aliyun.com

这篇文章里我们要实现一个基于GoLang编程语言的gRPC的客户端与服务端通信的HelloWorld案例

编写hello_world.proto文件,如下代码:

  1. syntax proto3
  2. package proto
  3. //接口请求入参
  4. message HelloRequest{
  5. string request = 1;
  6. }
  7. //接口返回出参
  8. message HelloResponse{
  9. string response = 1;
  10. }
  11. //定义接口
  12. service HelloService{
  13. //一个简单的rpc
  14. rpc HelloWorld(HelloRequest) returns (HelloResponse){}
  15. //一个服务器端流式rpc
  16. rpc HelloWorldServerStream(HelloRequest) returns (stream HelloResponse){}
  17. //一个客户端流式rpc
  18. rpc HelloWorldClientStream(stream HelloRequest) returns (HelloResponse){}
  19. //一个客户端和服务器端双向流式rpc
  20. rpc HelloWorldClientAndServerStream(stream HelloRequest)
  21. returns (stream HelloResponse){}
  22. }
  • 如上代码,通过protobuffer的service定义一个接口HelloService,接口中有四个方法都以HelloWorld开头,入参是HelloRequest,出参是HelloResponse,通过最前面的rpc关键字标识为这是一个rpc接口

    编译hello_world.proto文件生成对应的go文件

  • 笔者在go_common项目里新建了grpc项目结构如下代码

  • 项目地址 :http://github.com/sunpengwei1992/go_common
  • 进入项目之后可以先阅读README.md,了解各个包的功能

    1. go_common
    2. grpc
    3. helloworld_new
    4. client //存放客户端代码
    5. proto 存放proto文件和生成的go文件
    6. server存放server文件
  • cd go_common/grpc/helloworld_new/proto,进入proto文件夹下

  • 执行命令:protoc —go_out=plugins=grpc:. hello_world.proto
  • 最后生成hello_world.pb.go文件,主要有以下几部分组成:
  1. 方法出入参结构体以及序列化和反序列方法
  2. 注册出入参结构体的init方法
  3. 客户端存根结构体和接口以及实现
  4. 服务端结构体和接口以及一个空实现
  5. stream的send和recv结构体和接口以及实现
  6. 服务的一些描述
  • 代码太多,这里粘一些核心代码,并且简化了一些出入参,完成代码参考github上的源码

    1. //包名
    2. package proto
    3. //接口请求入参
    4. type HelloRequest struct {
    5. Request string
    6. }
    7. //接口返回出参
    8. type HelloResponse struct {
    9. Response string
    10. }
    11. //生成的helloServce的客户端接口,就跟java的api是的,让别人引用
    12. type HelloServiceClient interface {
    13. //一个简单的
    14. rpc HelloWorld(in *HelloRequest) (*HelloResponse, error)
    15. //一个服务器端流式
    16. rpc HelloWorldServerStream(in *HelloRequest) (ServerStream,error)
    17. //一个客户端流式
    18. rpc HelloWorldClientStream() (ClientStream, error)
    19. //一个客户端和服务器端双向流式
    20. rpc HelloWorldClientAndServerStream() (ServerClientStream error)
    21. }
    22. //helloServiceClient结构体
    23. type helloServiceClient struct {
    24. cc *grpc.ClientConn
    25. }
    26. //获取客户端存根,入参是一个client连接
    27. func NewHelloServiceClient(cc *grpc.ClientConn) HelloServiceClient {
    28. return &helloServiceClient{cc}
    29. }
    30. //这是服务器端的接口,我们的服务端需要实现这些接口
    31. type HelloServiceServer interface {
    32. //一个简单的
    33. rpc HelloWorld(in *HelloRequest) (*HelloResponse, error)
    34. //一个服务器端流式
    35. rpc HelloWorldServerStream(*HelloRequest, StreamServer) error
    36. //一个客户端流式
    37. rpc HelloWorldClientStream(ClientStream) error
    38. //一个客户端和服务器端双向流式
    39. rpc HelloWorldClientAndServerStream (ClientServerStream) error
    40. }

    编写服务端代码

  • 在server文件夹下新建hello_world_server.go文件,按如下步骤进行

  1. 创建HelloWorldServer结构体
  2. 实现pb.go文件中的HelloServiceServer接口(实现所有方法)
  3. StartServer(开启服务)
  • 详细代码依然看github

    1. type HelloServiceServer struct {}
    2. //简单rpc实现
    3. func (*HelloServiceServer) HelloWorld() {
    4. log.Printf("%v",req.Request)
    5. return &pb.HelloResponse{Response:"hello my is gRpcServer"}, nil
    6. }
    7. //服务端流式rpc实现
    8. func (*HelloServiceServer) HelloWorldServerStream() error {
    9. log.Printf("%v",req.Request)
    10. srv.Send(&pb.HelloResponse{Response:"hello my is gRpcServer stream"})
    11. return nil
    12. }
    13. //客户端流式rpc实现
    14. func (*HelloServiceServer) HelloWorldClientStream() error {
    15. for{
    16. //不断接受客户端发送的消息
    17. req,err := srv.Recv()
    18. //直到发送结束终止for循环
    19. if err != nil && err.Error() == "EOF"{
    20. break
    21. }
    22. if err != nil{
    23. log.Fatalf("%v",err)
    24. break
    25. }else{
    26. log.Printf("%v",req.Request)
    27. }
    28. }
    29. //返回给客户端处理结果
    30. srv.SendAndClose(&pb.HelloResponse{Response:"hello my is gRpcServer"})
    31. return nil
    32. }
    33. //双向流式rpc
    34. func (*HelloServiceServer) HelloWorldClientAndServerStream(){
    35. for{
    36. //接受客户端消息
    37. req,err := srv.Recv()
    38. if err != nil && err.Error() == "EOF"{
    39. break
    40. }
    41. if err != nil{
    42. log.Fatalf("%v",err)
    43. break
    44. }else{
    45. log.Printf("%v",req.Request)
    46. //返回客户端结果
    47. srv.Send(&pb.HelloResponse{Response:"hello my is gRpcServer stream"})
    48. }
    49. }
    50. return nil
    51. }
    52. func StartServer(){
    53. //开启服务端监听
    54. lis, err := net.Listen("tcp", "127.0.0.1:8090")
    55. if err != nil {
    56. log.Fatalf("failed to listen: %v", err)
    57. }
    58. //创建grpcServer
    59. gRpcServer := grpc.NewServer()
    60. //注册服务实现
    61. pb.RegisterHelloServiceServer(gRpcServer, &HelloServiceServer{})
    62. //启动服务
    63. gRpcServer.Serve(lis)
    64. }

    编写客户端

  • 在client目录下创建hello_world_client.go文件

    1. func StartClient(){
    2. //连接grpc的服务端
    3. conn, err := grpc.Dial("127.0.0.1:8090",grpc.WithInsecure())
    4. //创建客户端存根对象
    5. c := pb.NewHelloServiceClient(conn)
    6. // Contact the server and print out its response.
    7. //设置超时时间
    8. ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    9. defer cancel()
    10. //调用服务端的方法
    11. r, err := c.HelloWorldClientAndServerStream(ctx,grpc.EmptyCallOption{})
    12. if err != nil{
    13. log.Fatalf("%v", err)
    14. return
    15. }
    16. //客户端往服务端发送10次消息
    17. for i:=0;i<10;i++{
    18. r.Send(&pb.HelloRequest{Request:"my is golang gRpc client"})
    19. }
    20. //发送完毕
    21. r.CloseSend()
    22. //循环接受服务端返回的结果,直到返回EOF
    23. for{
    24. res,err := r.Recv()
    25. if err != nil && err.Error() == "EOF"{
    26. break
    27. }
    28. if err != nil {
    29. log.Fatalf("%v", err)
    30. break
    31. }
    32. log.Printf("result:%v",res.Response)
    33. }
    34. //关闭连接
    35. defer conn.Close()
    36. }

    github代码运行说明

  • github代码截图

  • 先启动 hello_world_server_test.go
  • 再启动hello_world_client_test.go
  • 如遇到启动的任何问题可留言

gRPC之GoLang入门HelloWord(三) - 图1

欢迎大家关注微信公众号:“golang那点事”,更多精彩期待你的到来
GoLang公众号.jpg