前阵子比较忙,好久没有更新了,后续争取恢复更新频率,这次先更一篇相对简单的,介绍下eventdispatcher类,前面的文章涉及过这个概念, eventdispatcher,顾名思义,就是用来分发事件的,在brpc里面,因为是不区分io线程和用户线程的,这个eventdispatcher,也仅仅是用来做事件分发,不负责具体的数据读取写入,所以本身的吞吐量可以很大。这个类很重要,但是本身比较简单,下面介绍下核心函数和类变量。
(1)void run()
核心功能函数,调用epoll_wait等待事件并分别调用函数处理,epoll_in事件来后调用socket:: startinputevent,epoll_out事件来后调用socket::handleepollout,socket:: startinputevent和socket::handleepollout则是分别去执行对应socket里相应的用户处理函数。核心代码如下:
(2)static void* runthis(void* arg);
封装了run,用于在bthread里启动调用run。
(3)virtual int start(const bthread_attr_t* consumer_thread_attr);
新建bthread以runthis为入口启动当前eventdispatcher,开始监听epoll事件并分发。
int addconsumer(socketid socket_id, int fd)
在fd上添加epoll_in事件(可读事件)进行监听,参数socket_id会保存到添加的事件里,后面事件发生了需要根据这个socket_id address实际的socket,拿到里面的_on_edge_triggered_events等进行实际的处理,总结来说就是调用这个函数来添加需要用socket_id对应的socket来处理的发生在fd上的边缘触发epoll_in事件。事件来后直接调用的是socket:: startinputevent。
addepollout(socketid socket_id, int fd, bool pollin)
添加监听epoll_out事件(可写事件),和添加epollin的方式类似,epoll_out事件,多了一个pollin参数,如果为true会同时监听epoll_in,事件来后直接调用的是socket::handleepollout,比如以前的文章提到过,brpc里在往某个socket里写数据的情况下,如果还没连接则发起连接并用“继续写入的函数”作为回调注册epollout后直接返回,让回调完成后续的写操作。
brpc支持多个eventdispatcher,具体eventdispatcher数量由参数决定,默认数量是1,每个eventdispatcher负责一部分fd的监听处理。
各个fd都是调用getglobaleventdispatcher来获得对应的eventdispatcher,这个函数就在event_dispatcher.cpp里,eventdispatcher类外brpc namespace下的一个函数,如下:
如果没初始化会先初始化并运行,initializeglobaldispatchers是具体的初始化函数,由pthread_once保证只执行一次。这里是基于murmurhash3将fd均匀分配到各eventdispatcher。initializeglobaldispatchers函数如下:
(1)int _epfd:监听事件的epfd
(2)bthread_t _tid:eventdispatcher当前所在的bthread的id
(3)bthread_attr_t _consumer_thread_attr:epoll_in或者epoll_out事件到来后新建bthread执行用户回调函数所要用到的线程属性