• grpc使用记录(二)简单同步服务实例


    已经折腾grpc几天了,也基本搞明白了怎么用,这里做一个简单的记录,以便日后需要的时候有个参考。

    按照顺序,先写同步服务的简单实例,然后写异步服务的,最后写4中服务类型的使用。

    grpc源码的example目录下都有相关的实例,但是讲的不够清楚,特别是异步服务这一块,注释说明不够详尽,CallData的封装也不利于理接异步服务的流程结构。所以我这里不按照官方的示例来做了。

    参考资料

    1、编写proto文件,定义服务

    如何编写proto文件,可以看Protobuf3 语法指南 (英文原文地址https://developers.google.com/protocol-buffers/docs/proto3)中的说明,这里就不再叙述了。

    我这里写一个简单的simple.proto文件,定义三个简单的服务接口,流式接口以后再说。

    syntax="proto3";
    
    // 包名是生成代码的使用的namespace,所有代码都在这个下面
    package Simple;
    
    // 指定服务的名称,作为生成代码里面的二级namespace
    service Server {
    	// 测试接口一
    	rpc Test1(TestRequest) returns (TestNull ){}
    	// 测试接口二
    	rpc Test2(TestNull ) returns (TestReply) {}
    	// 测试接口三
    	rpc Test3(TestRequest) returns (TestReply) {}
    }
    
    message TestNull {
    }
    
    message TestRequest {
    	string name   = 1; // 客户端名称
    	int32  id     = 2; // 客户端IP
    	double value  = 3; // 附带一个值
    }
    
    message TestReply {
    	int32  tid        = 1; // 服务线程ID
    	string svrname    = 2; // 服务名称
    	double takeuptime = 3; // 请求处理耗时
    }
    

    上面的接口中,必须有参数和返回值,如果不需要参数或者返回值,也必须定义一个空的(没有成员)message,否则无法通过编译。

    2、编译proto文件,生成代码

    安装好grpc之后,可以使用grpc的相关命令行程序,来使用proto文件生成C++代码(也可以生成别的语言的),这里需要分两步,一是生成protobuf(反)序列化的代码,二是生成基本服务框架代码。

    # 1、生成protobuf序列化代码
    # 执行下面命令后,将在当前目录下生成 simple.pb.h 和 simple.pb.cc 文件
    protoc -I ./ --cpp_out=. simple.proto
    
    # 2、生成服务框架代码
    # 执行下面命令后,将在当前目录下生成 simple.grpc.pb.h 和 simple.grpc.pb.cc 文件
    protoc -I ./ --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` simple.proto
    # 上面的 `which grpc_cpp_plugin` 也可以替换为 grpc_cpp_plugin 程序的路径(如果不在系统PATH下)
    # 生成的代码需要依赖第一步生成序列化代码,所以在使用的时候必须都要有(生成的时候不依赖)
    

    如果自己生成比较麻烦,可以在这个网站上生成 protobuf compiler

    3、编写服务端代码

    查看上一步骤生成的代码(simple.grpc.pb.cc),可以看到它已经生成了服务代码Simple::Server::Service类,里面已经有三个Test函数的空实现,这三个函数都是虚函数,所以可以继承这个类,并实现这三个函数,也可以直接修改生成的代码,把这三个函数的实现改为自己的实现。

    server.cpp 代码

    
    #include "simple.grpc.pb.h"
    #include <grpcpp/grpcpp.h>
    
    #include <memory>
    #include <iostream>
    #include <strstream>
    
    // 继承自生成的Service类,实现三个虚函数
    class ServiceImpl:
            public Simple::Server::Service {
        public:
    
        // Test1 实现都是差不都的,这里只是为了测试,就随便返回点数据了
        grpc::Status Test1(grpc::ServerContext*       context,
                           const Simple::TestRequest* request,
                           Simple::TestNull*          response)
                           override
        {
          std::ostrstream os;
          os << "Client Name = " << request->name() << '
    ';
          os << "Clinet ID   = " << request->id()   << '
    ';
          os << "Clinet Value= " << request->value()<< '
    ';
          std::string message = os.str();
          // grpc状态可以设置message,所以也可以用来返回一些信息
          return grpc::Status(grpc::StatusCode::OK,message);
        }
        // Test2
        grpc::Status Test2(grpc::ServerContext*       context,
                           const Simple::TestNull*    request,
                           Simple::TestReply*         response)
                           override
        {
          response->set_tid(100);
          response->set_svrname("Simple Server");
          response->set_takeuptime(0.01);
          return grpc::Status::OK;
        }
        // Test3
        grpc::Status Test3(grpc::ServerContext*       context,
                           const Simple::TestRequest* request,
                           Simple::TestReply*         response)
                           override
        {
          std::ostrstream os;
          os << "Client Name = " << request->name() << '
    ';
          os << "Clinet ID   = " << request->id()   << '
    ';
          os << "Clinet Value= " << request->value()<< '
    ';
          std::string message = os.str();
    
          response->set_tid(__LINE__);
          response->set_svrname(__FILE__);
          response->set_takeuptime(1.234);
          // grpc状态可以设置message
          return grpc::Status(grpc::StatusCode::OK,std::move(message));
        }
    };
    
    int main()
    {
      // 服务构建器,用于构建同步或者异步服务
      grpc::ServerBuilder builder;
      // 添加监听的地址和端口,后一个参数用于设置认证方式,这里选择不认证
      builder.AddListeningPort("0.0.0.0:33333",grpc::InsecureServerCredentials());
      // 创建服务对象
      ServiceImpl service;
      // 注册服务
      builder.RegisterService(&service);
      // 构建服务器
      std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
      std::cout<<"Server Runing"<<std::endl;
      // 进入服务处理循环(必须在某处调用server->Shutdown()才会返回)
      server->Wait();
      return 0;
    }
    
    

    编译

    写完代码之后,需要进行编译。编译的命令如下:

    g++ -o server server.cpp simple.grpc.pb.cc simple.pb.cc 
        -std=c++11 -I. `pkg-config --cflags protobuf grpc` 
        `pkg-config --libs protobuf grpc++ grpc` 
        -pthread -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed -ldl
    
    # 因为我这里没有grpc.pc文件,导致pkg-config找不到相关配置,所以我实际使用下面命令编译
    g++ -o service service.cpp simple.grpc.pb.cc simple.pb.cc -std=c++11 -I. -lgrpc++ -lgrpc -lprotobuf -lgpr -lz -lcares -laddress_sorting -lpthread -Wno-deprecated
    

    这里自己编译的grpc或者安装的可能没有grpc.pc文件,导致pkg-config程序找不到相关的配置,如果没有就直接链接以下的库即可:

    # VC下使用
    grpc.lib grpc++.lib libprotobuf.lib gpr.lib zlib.lib cares.lib address_sorting.lib ws2_32.lib
    # gcc/clang下使用
    -lgrpc++ -lgrpc -lprotobuf -lgpr -lz -lcares -laddress_sorting -lpthread
    

    4、编写客户端代码

    客户端代码比服务端简单多了,下面直接给出代码(因为比较简单,这里就只写Test3的调用代码了)。

    关于客户端,可以看一下这篇文章从grpc源码讲起(Client端的消息发送)

    client.cpp代码

    客户端这里只写了一个接口的测试,另外两个也都是差不多的,就不写了。

    #include "simple.grpc.pb.h"
    #include <grpcpp/grpcpp.h>
    
    #include <memory>
    #include <iostream>
    
    int main()
    {
        // 创建一个连接服务器的通道
        std::shared_ptr<grpc::Channel> channel =
            grpc::CreateChannel("localhost:33333", grpc::InsecureChannelCredentials());
        // 创建一个stub
        std::unique_ptr<Simple::Server::Stub> stub = Simple::Server::NewStub(channel);
        
        // 上面部分可以复用,下面部分复用的话要自己考虑多线程安全问题
        {
            // 创建一个请求对象,用于打包要发送的请求数据
            Simple::TestRequest request;
            // 创建一个响应对象,用于解包响要接收的应数据
            Simple::TestReply   reply;
            
            // 创建一个客户端上下文。它可以用来向服务器传递附加的信息,以及可以调整某些RPC行为
            grpc::ClientContext context;
            // 发送请求,接收响应
            grpc::Status st = stub->Test3(&context,request,&reply);
            if(st.ok()){
                // 输出下返回数据
                std::cout<< "tid = " << reply.tid()
                         << "
    svrname = " << reply.svrname()
                         << "
    takeuptime = " << reply.takeuptime() << std::endl;
            }
            else {
                // 返回状态非OK
                std::cout<< "StatusCode = "<< st.error_code()
                         <<"
    Message: "<< st.error_message() <<std::endl;
            }
            
        }
            
    }
    

    客户端的编译和服务是一样的,只是把server.cpp改为client.cpp即可。

    g++ -o client client.cpp simple.grpc.pb.cc simple.pb.cc -std=c++11 -I. -lgrpc++ -lgrpc -lprotobuf -lgpr -lz -lcares -laddress_sorting -lpthread -Wno-deprecated
    

    5、简单测试一下

    编译了serverclient后,都运行起来测试了一下,可行。

    服务端这里输出,是因为我在代码里面加了一行输出,在Test3函数中输出了一下函数名(__func__)和行号(__LINE__)。

    这里推荐一个工具BloomRPC ,这是一个GRPC服务的可视化界面客户端程序,可以直接加载proto文件,发送请求并接收返回。(我这里无法发送到非本地服务器,不知道是不是这个软件的原因)

  • 相关阅读:
    802.1x协议解析
    网上疯传河南高考零分作文:兔子 你傻啊?!
    802.1x协议解析
    图像旋转控件 TRotateImage Ver1.54(支持D3~D2010)
    如何在Windows下搭建Android开发环境(转)
    自制的小工具软件鼠标取色器
    【三九智慧】一卡通接口的 Delphi封装与测试
    自制的小工具软件鼠标取色器
    【三九智慧】一卡通接口的 Delphi封装与测试
    webservice soap hessian
  • 原文地址:https://www.cnblogs.com/oloroso/p/11176638.html
Copyright © 2020-2023  润新知