• Feeds

  • Posts Tagged ‘network’


    多服务器通讯层应该如何设计—一次code review小记

    大型的网络应用服务器(比如即时通讯及游戏服务器等)通常有大量的服务器内部数据通讯需求,几个月前曾经介绍过通讯层的实现再谈点对点通讯模块的故障问题,目前这个程序已经经历了数个版本的重构。今天进行了一次小组code review,讨论了当前版本的一些问题。

    I. Background

    我们为什么需要一个独立的通讯层

    对于服务器内部的通讯功能,已经有TCP提供可靠传输,已经有类似Apache MINA这样的框架来提供上层的封装,业务逻辑代码完全可以直接基于socket或MINA这样的框架来直接传送数据,我们为什么还需要包装一个独立的通讯层?

    目前考虑到的原因有

    • Connection pool作用。为了节点间更高的throughput, 通常两节点之间会使用n个固定连接来传输。n可设为CPU内核数大小。
    • 多节点互相连接的处理。一个节点需要连接n个节点发送信息。如果这部分逻辑放在业务调用层来实现,业务层会变得臃肿。
    • 处理自动重连逻辑,单个节点可以停用并重新启用。将这部分逻辑封装到通讯层,上层调用会更优雅。
    • 服务器之间通讯很短时间内不稳定(比如10s左右的中断)的处理。如果IDC内部或IDC之间通讯不稳定会造成这种现象。我们希望这种短暂的中断,不要引起业务逻辑重新选择节点,造成大量数据迁移及cache重构带来的内部振荡。
    • 海量数据传输最优化,大部分时间单个节点单向传输会>10k个数据包/s,独立的通讯层容易测试观察该环节是否存在瓶颈。

    通讯层不做什么

    • 序列化/反序列化,这个通常使用其他的框架或技术来做,比如protocol buffers, json等
    • RPC, 我们不做rpc的事情,已有的各种RPC技术已经做得足够好了,如果有RPC需求,我们会使用其他现有的技术。

    II. Code Review过程

    逻辑介绍

    首先由主程介绍了目前的数据流程,架构图及核心代码片断。比较核心的重发机制图示意如下

    comm

    (Figure 1: 重发的流程)

    过程说明:

    • 每个数据包有个TTL值,TTL>0之前会一直重试发给同一节点。这主要避免短期网络中断带来集群内振荡。
    • 如果正常的节点投递失败,则继续投递到backup节点。Backup节点由调用方在投递时候指定。类似在邮政寄快递,需要填写如果投递不到收件人怎么处理。通讯层只是根据投递地址表(list)来进行二次投递。
    • 如果backup也失败,则返回调用方的callback FailureHandler。这点有点类似Erlang里面失败处理的思路。

    听众提出的问题

    • 重发那一段流程是否必要,因为TCP本身已经有重发逻辑。应用层再加上一层重发处理,原理上可以应付短暂的网络中断,但是实际运行过程中能否真正有很大效果。(目前运行的情况也没有很明确的数据来支持这一点)
    • 目前一个节点需要连接的多个节点是静态配置的,即由程序启动时候决定,没有做自动发现及删除。目标节点是否可用由发送方自行判断。因此提出一个问题,backup节点选择由调用方来指定是否最佳?可否让通讯层自动协商,内部选择另一个同一类型的节点来做替代节点?但实现通讯层节点自动协商机制会增加程序复杂度。毕竟我们不是P2P程序,节点故障发生的情况很少。因此需要综合来权衡。
    • 目前的结构处理几十到上百个节点的集群问题不大,如果要用在上千个节点的场合,有哪些环节需要调整?

    其他提出的一些问题是针对具体代码写法的,由于不具有广泛借鉴意义,就不一一赘述。