brpc源码解析(一)—— rpc服务添加以及服务器启动主要过程

平时的工作用到了baidu-rpc搭建rpc服务,作为戈君大神的大作,在没有开源的时候,这个c++ 的rpc框架在厂内就已经好评颇多,无论是性能、文档、还是代码注释都很优秀,内部使用范围特别广,17年开源,开源版本叫做brpc,开源后不少大厂都有使用,目前已经进入Apache孵化器,源码以及文档的地址如下:https://github.com/apache/incubator-brpc。

个人对底层的东西比较感兴趣,因此有结合文档和源码深入学习下这个rpc框架的想法,整个brpc代码量不小,很早就开始看了,实话说一开始看其实挺费劲的,不过越往后看越觉得这个框架的博大精深,所以准备把看过的部分总结成文章,今天整理了一部分先发第一篇,算是看源码的随笔吧,第一次写博客,如有错误,还望指正。

brpc作为一个完整的rpc框架,自然同时支持作为Server和作为Client,这篇文章聊的是作为Server的使用方式以及启动Server、开启相应服务的内部处理过程。bprc面向用户的接口还是挺友好的,调用很简单,这里贴一个官方http的demo:
在这里插入图片描述搭建一个rpc Server,概括起来就是新建一个Server对象,设置好参数,往里面添加自己的Service,然后启动,Server可以包含多个Service,而Service又可以包含多个method,具体的某次请求就是针对某个method的,这个demo里的Server有三个Service。

具体怎么使用bprc这里不多说,官方文档有详细说明。因为是从业务使用开始接触这个框架的,所以我打算首先从和实际应用场景最接近的添加服务、启动服务器这块开始入手看源码。brpc里有个Server类,作为服务器的话均是通过这个类作为入口,从rpc服务器的构造过程来说,主要是如下三个过程:

1.往Server里添加Service(业务代码)

这里所说的Service,指的是继承自proto文件描述的Service的用户Service,brpc是基于protobuf的,protobuf不包含RPC的实现,但却有RPC的相关定义,比如Service,brpc就用到了这个,即便是用不到protobuf消息的http服务,服务接口也得定义在proto文件中,是为了确保所有的服务声明集中在proto文件中。

Addservice函数有多个重载,比如上面官方demo调用的第三个是基于restful mapping字符串的,如下:
在这里插入图片描述
各个不同的Addservice重载调用的都是AddServiceInternal这个内部具体实现。总的来说Addservice就是指定哪些服务去处理哪里来的请求。
在这里插入图片描述
参数有三个,protofuf Service类型的服务,是否是内部服务(brpc提供了监控之类的内部服务)的标识,以及添加的service的选项。
google::protobuf::Service是一个公共的基类,用户定义在proto文件里的Service会被预处理成继承自这个基类的一个类,用户需要继承这个类去写真正的业务代码(实现method对应的虚函数),框架层面上统一按父类google::protobuf::Service处理。AddServiceInternal就是把Service里的method拿出来映射到指定地址上,首先会调用InitializeOnce() 实现server的初始化(只会初始化一次,用pthread_once调用GlobalInitializeOrDieImpl,保证只执行一次),如下:
在这里插入图片描述
brpc是支持多协议的,而且是同一个端口,会有一个协议解析机制来保证相对高的效率,本身内置了各种协议,用户也可以很方便地自己新增协议支持,GlobalInitializeOrDieImpl函数里面,最重要的就是注册了包括http在内的各种各样的协议支持,简单来说就是指定了各协议的消息解析函数、server端用到的request处理函数、client端用到的response处理函数,比如request处理函数,以下图中http协议为例,ProcessHttpRequest就负责处理接收到的http request,这个函数里会处理request后交给用户自定义函数去处理。这里暂时不详细展开,后面的文章再详细说明。
在这里插入图片描述

2.设置服务器参数

brpc提供了丰富的参数设置,包括最大并发,是否开启ssl、关闭闲置连接时间等,具体的可以参考官方文档,这里不再赘述,主要是通过Serveroption类型指定参数和直接调用set_xxx来直接设置。

3.启动服务器

调用Start函数来启动服务器,和Addservice一样,start也有多种调用方式的重载,最终都是调用的StartInternal
在这里插入图片描述
在StartInternal函数里,首先是一些准备工作,根据option进行了一些设置,包括ssl设置以及是否创建tls数据、提前启动好需要的bthread(brpc用到的m:n线程库的线程,bthread也是bprc性能优异的关键之一,后面的文章再具体介绍)等。然后就是在指定的ip和端口范围(在范围内不断尝试,成功了就停止继续尝试)上启动监听,一个server也只支持监听一个端口。主要代码如下:
在这里插入图片描述
Acceptor顾名思义就是消息的接收器,BuildAcceptor也就是构建接收器,如果为NULL则调用BuildAcceptor
在这里插入图片描述
在BuildAcceptor里面,最重要的如下:
在这里插入图片描述
首先通过ListProtocols拿到所有注册支持的协议,然后遍历这些协议,通过AddHandler添加,handler是处理message的,注意到这里handler.process都是protocols[i]里面的process_request,也就是对应协议在服务端使用用来处理过接收到的请求的,对应的如果是客户端用的则是process_response。
在这里插入图片描述
StartAccept核心内容如下:
在这里插入图片描述
brpc是采用epoll来处理事件的,用的是边缘触发,options.on_edge_triggered_events是epoll边缘触发事件到来后的处理函数,也就是OnNewConnections作为事件的处理函数,顾名思义也就是新连接到来后使用的处理函数,这里不展开讲OnNewConnections的具体实现,后面的文章再详细介绍。

关于Socket类型,就是对fd等资源的的封装方便再多线程环境下使用,官方介绍是这样的:
和fd相关的数据均在Socket中,是rpc最复杂的结构之一,这个结构的独特之处在于用64位的SocketId指代Socket对象以方便在多线程环境下使用fd。
Socket::Create函数是根据options新建socket并把id存入第二个参数中,内部最重要的操作就是用options.on_edge_triggered_event所指代的函数进行epoll add,在当前服务端start的场景下,也就是在监听fd上用OnNewConnections注册epoll事件处理新过来的连接,至此启动完成,后续等待epoll事件进行相应处理。

负责第一步处理epoll事件的则是EventDispatcher,是分发epoll event的模块,负责把fd上边缘触发的事件分发给消费者(具体的业务处理函数),可以有多个,分别运行在不同的bthread上,具体的数量取决于参数,它所做的事情就是启动后不断去epoll_wait,获得epoll事件后交由相应函数处理,如果是epoll_in事件,调用Socket::StartInputEvent,如果是epoll_out,调用Socket::HandleEpollOut,简化后的核心代码如下:
在这里插入图片描述
StartAccept后,服务器基本就启动完成了。再回到最开始的实例里,最后会调用server.RunUntilAskedToQuit()
在这里插入图片描述
在这里插入图片描述
RegisterQuitSignalOrDie里主要是用signal函数注册了退出信号,一旦有退出信号s_signal_quit会为true,从而跳出循环并停止server。

brpc作为服务端整个启动过程基本就是这样,后面再写文章继续介绍一下brpc里一些关键的机制和类,以及在fd上收发请求的一些细节。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值