Dubbo为何不适合大文本传输

1,393 阅读3分钟

      年初的时候朋友参加面试,回来和我探讨过这个问题。当时只是知道平时使用过程中确实遵守了这一约定,但是真的认真分析起原因,确实没什么头绪,没想到入手的地方。

      几个月后的现在我想结合官方文档谈谈我对这个问题的理解。

      笔者将从三个方面阐述dubbo不适合大文本传输的具体原因。

I/O限制

      这里不仅仅是指网卡写入发出的I/O限制,同时包含网络带宽限制。我们从实际数据出发,计算一下单连接模型在大文本传输下的表现。

       模拟一下计算场景

  • 服务端配置1000兆网卡
  • 客户端配置50兆网络带宽
  • 单个大文本占用512字节

⚠️: 运营商公司,网卡生产方往往单位都是M Bit,而非M Byte

      整合单位之后的数据应该是:

  • 1000M Bit ≈ 128 M Byte
  • 50 M Bit ≈ 7 M Byte

      通过计算的单个服务提供者的 TPS(每秒处理事务数)最大为:128M Byte / 512K Byte = 256。单个消费者调用单个服务提供者的 TPS(每秒处理事务数)最大为:7MByte / 512K Byte = 14。

      假设服务端、客户端的CPU能力计算完全可以应付,那么最后服务端的瓶颈在于网卡写出速度,而客户端的瓶颈则在于网络带宽,如果使用者能接受这种TPS,那么可以酌情考虑使用。

序列化限制

      Dubbo这一类的Rpc框架执行远程调用的本质就是消费者将执行上下文通过网络IO传输给提供者,提供者能够通过上下文查找到虚拟机内部服务对象,然后通过反射执行对应逻辑最后将结果返还给消费端。

      在Dubbo中执行上下文其实就是Invocation,Invocation中会提供明确的调用信息。

/* 节选部分代码 */
public interface Invocation {

    /* 获取方法名 */
    String getMethodName();
    
    /* 获取接口名 */
    String getServiceName();

    /* 获取参数类型数组 */
    Class<?>[] getParameterTypes();

    /* 获取调用参数 */
    Object[] getArguments();

}

      消费者在发生远程调用时会将Invocation通过序列化协议转化为字节数组,而提供者则相反,将网络IO过来的字节数组反序列化为对象(decode过程其实比这个复杂的多,笔者省略掉跟本文无关的内容)。

      实际处理就是decode过程中解析出此数据包的大小之后需要等待该包所有字节到达然后参与序列化,此时数据全部在内存中,序列化出来的大对象也占用内存,于我们的服务器而言,并不想看到这种情况。

Channel的限制

      Dubbo默认使用Netty来实现网络传输,Netty其实还是基于Java NIO,为了接收方能够方便快捷的进行拆包其实要求Channel的写入事件是根据顺序排队处理的。

      那么在单连接下,多个请求共用一个Channel实现数据写入;当多个请求到达,如果报文过大,会导致Channe一直在发送这个报文,其他请求的报文写入事件会进行排队,迟迟无法发送,当然也就迟迟没有响应,最坏的情况就是大量请求堆积、超时。