在twemproxy的发送和接收流程剖析中,我们已经完全弄清楚twemproxy如何将客户端以及服务端发来的包切分成msg,获得一个独立的msg后twemproxy应该如何处理?这是本文这次需要重点介绍的内容。
twemproxy的主干流程
图1 twemproxy的主干流程
如图1所示,twemproxy主要通过3个队列进行模块间的数据交互:客户端连接conn的发送队列conn->omsg_q,服务端连接s_conn的输入队列s_conn->imsg_q,服务端连接s_conn的发送队列s_conn->omsg_q以及conn->omsg_q里的msg的对应回复peer。
- 客户层接收模块将所有需要回复的msg处理后分别写入到客户端conn连接的发送队列conn->omsg_q以及服务端连接s_conn的输入队列s_conn->imsg_q。
- 服务层发送模块从服务端连接s_conn的输入队列s_conn->imsg_q取出,将其发送后写入服务端连接s_conn的发送队列s_conn->omsg_q。
- 服务层接收模块将接到服务端的响应通过s_conn->omsg_q的提供顺序将回复的msg写入到conn->omsg_q里的msg的对应回复peer。
- 客户层发送模块通过客户端连接conn的发送队列conn->omsg_q的顺序获得请求msg的对应的peer中的回复msg,然后发送个客户端。
由于twemproxy的队列里的元素是msg对象的指针,同样peer也是msg对象的指针,为此在twemproxy在接到请求直至发送回复的那段时间里没有进行内存拷贝,仅有在接收的时候申请内存。twemproxy通过这种精巧的设计使得内存零拷贝,同时配合空闲msg池的使用,大幅减少了系统申请和释放内存的次数,大幅提高了twemproxy性能,大幅减轻了twemproxycpu的压力。
twemproxy的客户层接收模块
图2 twemproxy的客户层接收
如图2,当客户层接收模块接收到客户端请求后:
- twemproxy会从空闲msg池和系统内存中请求空的msg,通过我们提到过的接收流程接收并解析客户端请求生成msg。
- 解析后发现如果类型是mset,mget或者del的多key操作,根据key值进行分片操作将一个msg分成多个msg。
- 将这些msg按顺序放入客户端连接conn的发送队列conn->omsg_q中(其中被分片的msg不放入服务端连接s_conn的输入队列s_conn->imsg_q中)。
- 根据key值经过哈希建立对应的服务端连接,每个服务端在twemproxy中对应一个key值。
- 在建立连接后,将msg插入服务端连接s_conn的输入队列s_conn->imsg_q中,并通知服务端层发送模块发送。
twemproxy的服务层发送模块
图3 twemproxy的服务层发送
如图3所示,当服务层接收模块接收到客户层接收模块的通知后:
- twemproxy从服务端连接s_conn的输入队列s_conn->imsg_q中取出msg。
- 在相应的s_conn发送msg,从服务端连接s_conn的输入队列s_conn->imsg_q中删除。
- 发送后,将发送过的msg插入到服务端连接s_conn的发送队列s_conn->omsg_q中。
twemproxy的服务层接收模块
图4 twemproxy的服务层接收
如图4所示,当服务层接收模块接收到服务端回复后:
- twemproxy会从空闲msg池和系统内存中请求空的msg,通过我们提到过的接收流程接收并解析服务端回复生成msg。
- 从服务端连接s_conn的发送队列s_conn->omsg_q中取出对应的客户端请求pmsg。
- 查看客户端请求pmsg和客户端msg是否对应,msg可能需要被丢弃(因为在此时客户端连接断开,msg不能被发送),并将丢弃的msg放入空闲msg池中。
- 将msg写入pmsg->peer中,然后从服务端连接s_conn的发送队列s_conn->omsg_q中删除pmsg。
- 如果存在分片,那么在所有已经分片的msg收到请求回复后,将这些已经分片的msg并入被分片的msg中。
- 将所有已经分片的msg置空,并将该消息通知给服务层接收模块。
twemproxy的客户层发送模块
图5 twemproxy的客户层发送
如图5所示,当客户层接收模块接收到服务层接收模块的通知后:
- twemproxy从客户端连接conn的发送队列conn->omsg_q取出msg。
- 将msg->peek中取出对应的回复pmsg并发送。
- 从客户端连接conn的发送队列conn->omsg_q删除msg。
- 将msg释放后放入空闲msg池。
总结
twemproxy通过客户端连接conn的发送队列conn->omsg_q,服务端连接s_conn的输入队列s_conn->imsg_q,服务端连接s_conn的发送队列s_conn->omsg_q以及conn->omsg_q里的msg的对应回复peer来完成twemproxy的代理服务,利用队列和peer指针特性实现了内存零拷贝。