一、基础库开发
日志和配置模块等工具类开发
配置文件 xml(也可切换为 yaml、json 等作为配置文件)
简单,容易读取
可读性能接受
二、Eventloop模块
EventLoop 核心开发
Reactor 是一种事件处理模型,又叫做反应堆模式。
核心是一个 Loop 循环,在循环里面不断调用 epoll_wait 监听套接字IO事件,一旦发生 IO 事件,epoll_wait 返回,线程转而去处理这些 IO 事件(一般是执行绑定在其上的回调函数)。处理完事件之后,又重新陷入到 epoll_wait 中,不断循环这个过程。
mainReactor 由主线程运行,他作用如下:通过epoll监听listenfd的可读事件,当可读事件发生后,调用accept函数获取clientfd,然后随机取出一个subReactor,将cliednfd的读写事件注册到这个subReactor的epoll上即可。mainReactor只负责建立连接事件,不进行业务处理,也不关心已连接套接字的IO事件。
subReactor通常有多个,每个subReactor由一个线程来运行。其注册clientfd的读写事件,当发生IO事件后,需要进行业务处理。
定时器开发
IO线程封装
三、TCP模块
TcpBuffer
为什么需要应用层buffer?
方便数据处理,特别是应用层的包组装和拆解(粘包黏包问题);
方便异步的发送(发送数据直接塞到发送缓冲区里面,等待 epoll 异步去发送);
提高发送效率,多个包合并一起发送;
InBuffer:
服务端调用 read 成功从 socket 缓冲区读到数据,会写入到 InBuffer 后面
服务端从 InBuffer 前面读取数据,进行解码得到请求
OutBuffer:
服务端向外发送数据,会将数据编码后写入到 OutBuffer 后面
服务端在 fd 可写的情况下,调用 write 将 OutBuffer 里面的数据全部发送出去
TcpServer
mainReactor 由主线程运行,他作用如下:通过epoll监听listenfd的可读事件,当可读事件发生后,调用accept函数获取clientfd,然后随机取出一个subReactor,将cliednfd的读写事件注册到这个subReactor的epoll上即可。mainReactor只负责建立连接事件,不进行业务处理,也不关心已连接套接字的IO事件。
subReactor通常有多个,每个subReactor由一个线程来运行。其注册clientfd的读写事件,当发生IO事件后,需要进行业务处理。
TcpConnection
Read:读取客户端发来的数据,组装为RPC请求
Excute:将RPC请求作为入参,执行业务逻辑得到RPC响应
Write:将RPC响应发送给客户端
TcpClient
非阻塞 Connect
返回 0,表示连接成功
返回 -1,但 errno== EINPROGRESS,表示连接正在建立,此时可以添加到 epoll 中去监听其可写事件。等待可写事件就绪后,调用 getsockopt 获取fd上的错误,错误为0代表连接建立成功。
其他 errno 直接报错
四、RPC协议封装
为什么要自定义协议格式?
既然用了 Protobuf 做序列化,为什么不直接把序列化后的结果直接发送,而要在上面在自定义一些字段?
1、为了方便分割请求:因为 protobuf 后的结果是一串无意义的字节流,你无法区分哪里是开始或者结束。 比如说把两个 Message 对象序列化后的结果排在一起,你甚至无法分开这两个请求。在 TCP 传输是按照字节流传输,没有包的概念,因此应用层就更无法去区分了;
2、为了定位:加上 MsgID 等信息,能帮助我们匹配一次 RPC 的请求和响应,不会串包;
3、错误提升:加上错误信息,能很容易知道 RPC 失败的原因,方便问题定位;
protobuf使用流程:
1.编写proto文件
2.怎么调用proto文件
生成.cc .h文件
基于protobuf的自定义RPC协议编码
encode // 将 protobuf后的数据转化自定义协议格式(字节流),写入到 buffer
基于protobuf的自定义RPC协议解码
decode // 将 buffer 里面的字节流读出,是一个自定义协议格式,然后转换为 protobuf后的数据,加入处理队列
五、RPC模块封装
RpcController、Closure、RpcChannel都是对google::protobuf::RpcController、google::protobuf::Closure、 google::protobuf::RpcChannel等继承;
RPC 服务端流程 RpcDispatcher:
启动的时候就注册OrderService 对象。
-
从buffer读取数据,然后 decode 得到请求的 TinyPBProtobol 对象。然后从请求的 TinyPBProtobol 得到 method_name, 从 OrderService 对象里根据 service.method_name 找到方法 func
-
找到对应的 requeset type 以及 response type
-
将请求体 TinyPBProtobol 里面的 pb_date 反序列化为 requeset type 的一个对象, 声明一个空的 response type 对象
-
func(request, response)
-
将 reponse 对象序列为 pb_data。 再塞入到 TinyPBProtobol 结构体中。做 encode 然后塞入到buffer里面,就会发送回包了
test_rpc_server.cc
1、#include “order . pb.h”
2、定义service对象 Order
3、成功将我们的service对象注册到RPC方法里面
4、就启动tcp服务,也就是rpc
RpcChannel 用于客户端跟服务端通信
RpcController:The primary purpose of the controller is to provide a way to manipulate settings specific to the RPC implementation and to find out about RPC-level errors.(设置请求的消息号)等
RcpClosure:Abstract interface for a callback.
test_ rpc_ client.cc
1、构造message对象makeOrderRequest
2、构造message对象makeOrderResponse,设置controller
3、设置回调函数
4、初始化并调用channel
文章来源:https://www.toymoban.com/news/detail-531272.html
RpcChannel 和RpcAsyncChannel
六、项目完善
日志的完善和优化
把logger的buffer中的数据写入到异步的队列缓存中,
利用一个线程,采用条件变量来实现生产者消费者模型来处理队列中的缓存,实现:读取队列中的数据并将数据写入日志。文章来源地址https://www.toymoban.com/news/detail-531272.html
代码生成工具脚手架封装
项目的构建和测试
到了这里,关于C++ 异步 RPC框架Rocket的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!