org.apache.cassandra.thrift - Server 端入口
CassandraDaemon: 启动类
CassandraServer implements org.apache.cassandra.thrift.Cassandra.Iface : 接口类,类似于web service中的skeleton
CustomTThreadPoolServer extends org.apache.thrift.server.TServer :监听类,监听thrift client请求
org.apache.cassandra.net
1. message 监听线程
启动 SocketThread 线程 "ACCEPT-" + localEp
StorageService.initServer
-> MessagingService.instance.listen(FBUtilities.getLocalAddress());
-> SocketThread.start()
监听7000端口,接受socket 连接. 这里有一点与一般SocketServer/Socket 编程不一样的是使用ServerSocketChannel来构造SocketServer,而不是通过 new ServerSocket(int port) 来构造.
每个接收的socket, 生成一个IncomingTcpConnectio线程
2. message read 线程
每个 IncomingTcpConnection 会成为一个独立的线程,loop 阻塞式的读取消息
2.1 comm protocol
每次读取的非stream消息格式如下(stream 消息另作分析):
| constant magic | 4 byte |
|header | 4 byte|
| content size | 4 byte|
| content | content size * 1 byte|
MessageService.packIt(byte[] content, boolean compress) 对应content打包过程
每个读到的消息content(byte[]),生成一个MessageDeserializationTask,交由单线程 pool(MessageService.messageDeserializerExecutor_,)来运行 - DebuggableThreadPoolExecutor并且实现了 JMXEnabledThreadPoolExecutorMBean,另作分析
从第p位(从右到左(从低到高)数)开始,连续n位(低位方向)的值。运算优先级 是加减,然后移位,然后与或
3. message 反序列化线程
MessageDeserializationTask 在 messageDeserializerExecutor 中运行,从content(byte[])反序列化生成Message对象
关于Message序列化与反序列化另作分析. 得到Message后,调用MessageService.receive(Message)
4. message handler 线程
4.1 Stage Thread poll
MessageService.receive 中生成 MessageDeliveryTask,每个Task会在不同Stage的ThreadPool中运行
StageManager 中对不同message type的ThreadPool初始化
MUTATION_STAGE : 32 // 写
READ_STAGE : 8 // 读
RESPONSE_STAGE : Max(2, process num) // 读写的response
STREAM_STAGE : 1 // ?
GOSSIP_STAGE : 1 // member detection
AE_SERVICE_STAGE : 1 // ?
LOADBALANCE_STAGE : 1 // ?
4.2 Message handler
MessageDeliveryTask 从MessagingService.instance中根据header中的verb找到对应的message handler(StorageService中注册),调用 IVerbHandler.doVerb(message_);
4.2.1 RowMutationVerbHandler
- 反序列化Message body得到RowMutation
- 判断是否是hinted写入(target server is down, 由该Server来保存记录) 如果是hinted写入,则作一个标记
- 将RowMutation写入到commit log,等待提交,
- 生成一个WriteResponse,返回一个Stage为RESPONSE_STAGE, verb为READ_RESPONSE的message,body包含table name,key,true(表示提交成功),注意Message id 和Request中的Message一致
- 调用 MessagingService.sendOneWay 将消息发送出去
4.2.2 ResponseVerbHandler
在发送Request的Message时,使用Message id注册了IAsyncCallback或IAsyncResult,而response的message和request message使用同一id。因此,在得到response message后,可根据id取出对应的callback
5. message write 线程
MessagingService.sendOneWay 中得到对应的OutboundTcpConnection, 将消息放到OutboundTcpConnection的队列中,由OutboundTcpConnection向target address的7000端口发送.
- 每个OutboundTcpConnection 对应一个线程,循环消费队列的消息
- 每个InetAddress to 对应两个OutboundTcpConnection(RESPONSE_STAGE, GOSSIP_STAGE类型的message使用一个通道,其他类型使用另外一个通道)
由以上代码可以看出,对单个Message,如果在RpcTimeout(2s)内没有连接上对方,则被直接丢弃.在2s内,每隔100ms会retry一次
总结
综上,网络端通信有如下几类线程:
- 监听线程 SocketThread(7000) "ACCEPT-" + localEp
- 每个socket(remote address)对应一个IncomingTcpConnection线程,保持长连接
- Message Deserialization 线程
- Message handler 线程池,不同类型的handler具有不同的线程池大小,读请求是8,写请求是32,处理响应是2
- 每个remote address 对应两个OutboundTcpConnection线程,保持长连接
设计特点
- socket读、写线程分离,使用不同的线程来分别监听,读,写
- 异步响应。所有sendRR(request/response)中的方法都是异步响应, 由回调IAsyncCallback或者IAsyncResult来得到异步响应.异步响应通过使用Message id 注册callback来实现
- 不同类型的Message在不同的线程池中处理(stage对应线程池类型, verb对应handler)
- 不同类型的Message有header来区分,而不是使用多个子类, 按层次对message进行包装
----------------
payload: RowMutation(...)
----------------
payload: Message[header + payload]
----------------
payload:Comm Protocol [magic number + comm head + size + payload]
----------------
wire payload