最近读了一下thrift0.7 noblocking server 的部分源码,稍微做下记录。
thrift 使用libevent库做IO多路,
TNonblockingServer 负责初始化event, 增加监听端口,运行loop, 主线程有两个监听端口,一个是tcp listen,用来accept链接,
一个是notify event , 使用socketpair 实现,一个端口用来读,一个端口用来写, 用在多线程完成读数据,执行任务后执行写操作,会通知到主线程将event_flag 设置成写操作,libevent就会选择合适的时候发送数据。server在一开始运行的时候会将notify event 的回调函数设置为:
926 static void taskHandler(evutil_socket_t fd, short /* which */, void* /* v */) {
927 TConnection* connection;
928 ssize_t nBytes;
929 while ((nBytes = recv(fd, cast_sockopt(&connection), sizeof(TConnection*), 0))
930 == sizeof(TConnection*)) {
931 connection->transition();
932 }
933 if (nBytes > 0) {
934 throw TException("TConnection::taskHandler unexpected partial read");
935 }
936 if (errno && errno != EWOULDBLOCK && errno != EAGAIN) {
937 GlobalOutput.perror("TConnection::taskHandler read failed, resource leak", errno);
938 }
939 }
940
这个函数回去读socket的值,取回connection, 并按照connection之前设置的状态进行设置event flag。
线程安全:
nonblocking采用一个队列,主线程操作io, 所以io操作等没有锁,多线程去读队列,当一个线程在处理task时,会把该task对应的fd在主线程上关掉 event_del,无法处理新的数据,任务完成后把event_add回去。
每一个task最小单位是connection.void TConnection::transition()负责每个连接状态的改变以及把task push进队列。
TTransport 负责发送,接收数据
TProtocol 负责解包,压包
TProcess 负责协议包的分发执行。
connection有imput output buff, 负责读取数据之后放进input, ,然后push进task queue, task queue 执行run函数, processor_ process:
69 void run() {
70 try {
71 for (;;) {
72 if (serverEventHandler_ != NULL) {
73 serverEventHandler_->processContext(connectionContext_, connection_->getTSocket());
74 }
75 if (!processor_->process(input_, output_, connectionContext_) ||
76 !input_->getTransport()->peek()) {
77 break;
78 }
79 }