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() << '\n';
os << "Clinet ID = " << request->id() << '\n';
os << "Clinet Value= " << request->value()<< '\n';
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() << '\n';
os << "Clinet ID = " << request->id() << '\n';
os << "Clinet Value= " << request->value()<< '\n';
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()
<< "\nsvrname = " << reply.svrname()
<< "\ntakeuptime = " << reply.takeuptime() << std::endl;
}
else {
// 返回状态非OK
std::cout<< "StatusCode = "<< st.error_code()
<<"\nMessage: "<< 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、简单测试一下
编译了server和client后,都运行起来测试了一下,可行。
服务端这里输出,是因为我在代码里面加了一行输出,在Test3函数中输出了一下函数名(__func__)和行号(__LINE__)。

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

grpc使用记录(二)简单同步服务实例的更多相关文章
- grpc使用记录(三)简单异步服务实例
目录 grpc使用记录(三)简单异步服务实例 1.编写proto文件,定义服务 2.编译proto文件,生成代码 3.编写服务端代码 async_service.cpp async_service2. ...
- WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[下篇]
原文:WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[下篇] 在[第2篇]中,我们深入剖析了单调(PerCall)模式下WCF对服务实例生命周期的控制,现在我们来 ...
- 搭建简单Django服务并通过HttpRequester实现GET/POST http请求提交表单
调试Django框架写的服务时,需要模拟客户端发送POST请求,然而浏览器只能模拟简单的GET请求(将参数写在url内),网上搜索得到了HttpRequester这一firefox插件,完美的实现了模 ...
- 使用ssm(spring+springMVC+mybatis)创建一个简单的查询实例(二)(代码篇)
这篇是上一篇的延续: 用ssm(spring+springMVC+mybatis)创建一个简单的查询实例(一) 源代码在github上可以下载,地址:https://github.com/guoxia ...
- java版gRPC实战之二:服务发布和调用
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- Spring Cloud Netflix Zuul 重试会自动跳过经常超时的服务实例的简单说明和分析
在使用E版本的Spring Cloud Netflix Zuul内置的Ribbon重试功能时,发现Ribbon有一个非常有用的特性: 如果某个服务的某个实例经常需要重试,Ribbon则会在自己维护的一 ...
- [WCF REST] 一个简单的REST服务实例
Get:http://www.cnblogs.com/artech/archive/2012/02/04/wcf-rest-sample.html [01] 一个简单的REST服务实例 [02] We ...
- 基于Tcp协议的简单Socket通信实例(JAVA)
好久没写博客了,前段时间忙于做项目,耽误了些时间,今天开始继续写起~ 今天来讲下关于Socket通信的简单应用,关于什么是Socket以及一些网络编程的基础,这里就不提了,只记录最简单易懂实用的东西. ...
- Java学习-007-Log4J 日志记录配置文件详解及实例源代码
此文主要讲述在初学 Java 时,常用的 Log4J 日志记录配置文件详解及实例源代码整理.希望能对初学 Java 编程的亲们有所帮助.若有不足之处,敬请大神指正,不胜感激!源代码测试通过日期为:20 ...
随机推荐
- Mysql 游标初识
MySql 游标初识 认识 游标(cursor), 按字面意思可理解为, 游动的标识, 或者叫做"光标", 这样更容易理解. 就好比现有一张表存储了n行记录, 然后我想每次取出一行 ...
- SAMBA 服务配置
Samba文件共享服务 Linux系统中一种文件共享程序 在Windows网络环境中,主机之间进行文件和打印机共享是通过微软公司自己的SMB/CIFS网络协议实现的.SMB(Server Messag ...
- 92. 反转链表 II.反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
public ListNode reverseBetween(ListNode head, int m, int n) { ListNode dummy = new ListNode(0); //虚拟 ...
- 201671030123叶虹 实验十四 团队项目评审&课程学习总结
项目 内容 这个作业属于哪个课程 软件工程 这个作业的要求在哪里 实验十四 团队项目评审&课程学习总结 课程学习目标 掌握软件项目评审会流程:反思总结课程学习内容 一.实验一问题回答 1.实验 ...
- 项目Beta冲刺(团队) —— 总结
所属课程 软件工程1916|W(福州大学) 作业要求 Beta冲刺--总结篇 团队名称 待就业六人组 后端源码 Github地址 APP源码 Github地址 在线评审表 腾讯文档地址 Beta版本A ...
- Flux 和 Mono 的区别
Flux 和 Mono 是 Reactor 中的两个基本概念.Flux 表示的是包含 0 到 N 个元素的异步序列.在该序列中可以包含三种不同类型的消息通知:正常的包含元素的消息.序列结束的消息和序列 ...
- maven 查看依赖树结构命令mvn dependency:tree
使用maven 管理项目的依赖,可以使用如下命令查看依赖树结构: mvn dependency:tree 如下图是使用idea的终端执行命令的局部图: 也可以使用如下命令将输出定向到某个文件,这样就可 ...
- 51nod1803 森林直径
[传送门] 考虑计算直径的 dp 方法. $d[u]$ 表示以 $u$ 为根的子树能 $u$ 能走到的最远距离 $dp[u]$ 表示以 $u$ 为根的子树的直径那么每次dfs一个子节点后$dp[u] ...
- 51nod1681 公共祖先
[传送门] 很明显,可以转化成求每个点在两棵树中对应的子树中有多少个相同的节点,对答案的贡献就是$C(x, 2)$.关键就是怎么求这个东西.一是,对第一棵树求出dfs序,然后dfs第二棵树,用树状数组 ...
- 平衡二叉树(Java)
package com.rao.linkList; /** * @author Srao * @className AvlTree * @date 2019/12/3 21:23 * @package ...