brpc源码:server类

源码路径

brpc-master\src\brpc\server.cpp

server类的启动涉及有以下几个方法的调用:

AddService、Start、RunUntilAskedToQuit

 

添加服务:AddService

代码的几个AddService 基本都是再调用AddServiceInternal 方法

AddServiceInternal 

这个方法主要实现了:

  • InitializeOnce :保证初始化一次
  • 将参数service包含的方法信息放到_method_map 中,key是方法的fullname
  • 将这个service的信息放到_fullname_service_map中,key是service的fullname
  • 将service信息放到_service_map中,key是service的fullname
  • _global_restful_map 处理url映射

关于protobuf的rpc的一些方法如GetDescriptor、full_name等,可以参考:descriptor.h

 

server启动:Start

start 基本是在调用StartInternal 方法,主要实现了:

  • InitializeOnce:保证初始化一次
  • 初始化_keytable_pool
  • 为_method_map 的每个方法设置最大并发数量
  • 创建监听socket与对应的acceptor,BuildAcceptor 里面初始化了每种协议对应的handler和处理方法process
  • 启动bthread

这里的关键在于,将监听的ip+port加入到eventloop,在brpc中是请求分发器eventdispatcher来完成,整个流程是:

  1. 创建acceptor
  2. 调用Acceptor::StartAccept
  3. 调用Socket::Create
  4. 调用Socket::ResetFileDescriptor
  5. 调用GetGlobalEventDispatcher 获得监听的socket对应的 EventDispatcher;这里会调用InitializeGlobalDispatchers方法,创建dispatcher,并调用dispatcher的Start方法,就是创建bthread来执行EventDispatcher::Run方法,这里面就是epoll_wait了,就是常说的eventloop~
  6. 调用EventDispatcher::AddConsumer,也就是epoll_ctl 方法了,添加client发起连接的事件

 

事件循环

这里就是EventDispatcher::Run 方法的逻辑,epoll_wait返回事件,然后遍历事件列表,输入事件用StartInputEvent 处理,输出事件用HandleEpollOut处理

Socket::StartInputEvent

对新建的socket调用Socket::ProcessEvent,也就是s->_on_edge_triggered_events;
对于监听socket,这个触发处理函数在Acceptor::StartAccept里设置成了OnNewConnections,同样是将新建立的客户端socket添加到一个dispatcher,并指定_on_edge_triggered_events为InputMessenger::OnNewMessages,读取客户端写的内容。

InputMessenger::OnNewMessages

这里读取客户端发来的请求,然后调用QueueMessage 方法,启动bthread调用ProcessInputMessage 方法,里面调用的_process方法处理请求,而这个_process 的地址也是在InputMessenger::OnNewMessages 中赋值的,值等于每个handler的process(在GlobalInitializeOrDie中涉及)。

举例来说,对普通rpc,process 实际指向 ProcessRpcRequest方法

Socket::HandleEpollOut

调用Socket::HandleEpollOutRequest,调用请求req的on_epollout_event方法,这个方法在server往客户端回写时用到

 

处理请求:ProcessRpcRequest

此方法负责处理请求,功能包括:

  • 解析请求体
  • 处理trace追踪和并发量校验等
  • 根据解析到的service + method名称,在_method_map 中找到调用的方法,调用CallMethod,该定义在proto文件编译后产生的cc源文件中,一般对应proto中定义的rpc的方法,方法由使用者实现,可以看到在brpc提供的示例中,server的实现都会有一个闭包类对象:brpc::ClosureGuard done_guard(done); 用析构函数保证done一定会在返回前执行。可参考:闭包类ClosureGuard
  • 发送响应SendRpcResponse

 

ClosureGuard

 

 

发送响应:SendRpcResponse

  • 将响应序列化到buf
  • 将buf通过socket发送给客户端
  • 调用socket的write接口(Socket::StartWrite)

 

写响应包:StartWrite

  • ConnectIfNot:确认和客户端已连接;其中会调用Socket::Connect,方法,并将KeepWriteIfConnected 函数作为入参,随即调用EventDispatcher::AddEpollOut方法,将这个socket的EPOLLOUT 事件添加到epoll中。这样在上文说到的事件循环中,就会检测到这个socket的EPOLLOUT,然后用HandleEpollOut 进行处理,也即调用on_epollout_event 方法。
  • AddOutputBytes:尝试写
  • 若没写完,则开启一个后台bthread绑定KeepWrite 来写

 

 

退出检测:RunUntilAskedToQuit

void Server::RunUntilAskedToQuit() {
    while (!IsAskedToQuit()) {
        bthread_usleep(1000000L);
    }
    Stop(0/*not used now*/);
    Join();
}

不断查询s_signal_quit ,如果收到ctrl+c中断信号,会设置标志位并退出while,

调用acceptor的StopAccept,释放每个socket

 

以上就是server的部分流程,省略了很多细节,不过基本的流程大同小异:

  • 启动时间循环,创建监听套接字
  • 将监听套接字上收到的连接请求也添加到循环中一并监听
  • 收到请求,根据客户端参数定位到服务,执行
  • 回写响应,如果一次不能写完,则监听该套接字上的可写的信号,添加到事件循环中

 

可参考:

brpc源码解析(二)—— brpc收到请求的处理过程

基于protobuf实现一个极简版的RPC  可以通过这个了解rpc的过程

你可能感兴趣的:(brpc)