Dubbo的网络通信

Dubbo 支持多种协议,如下图所示:
Protocol扩展
在通信过程中,不同的服务等级一般对应着不同的服务质量,那么选择合适的协议便是一件非常重要的事情,需要根据应用的特征来选择。例如,使用 RMI 协议,一般会受到防火墙的限制,所以对于外部与内部进行通信的场景,就不要使用 RMI 协议,而是基于 HTTP 协议或者 Hessian 协议。

Hessian 协议

  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:HTTP
  • 传输方式:同步传输
  • 序列化:Hessian 二进制序列化
  • 适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。
  • 适用场景:页面传输,文件传输,Hessian 是 Caucho 开源的一个 RPC 框架,其通讯效率高于 WebService 和 Java 自带的序列化,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。
1
2
3
4
5
6
7
8
9
10
11
12
<!--定义 hessian 协议 -->
<dubbo:protocol name="hessian" port="8080" server="jetty" />
<!--设置默认协议 -->
<dubbo:service protocol="hessian" />
<!--设置 service 协议 -->
<dubbo:service protocol="hessian" />

<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.33</version>
</dependency>

Http 协议

  • 连接个数:多连接
  • 连接方式:短连接
  • 传输协议:HTTP
  • 传输方式:同步传输
  • 序列化:表单序列化
  • 适用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或 URL 传入参数,暂不支持传文件。
  • 适用场景:需同时给应用程序和浏览器 JS 使用的服务。
1
2
<!--配置协议 -->
<dubbo:protocol name="http" port="8080" />

Thrift 协议

1
2
3
4
5
6
7
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.8.0</version>
</dependency>

<dubbo:protocol name="thrift" port="3030" />

Dubbo 使用的 Thrift 和原生的 Thrift 协议不兼容,在原生协议的基础上添加了一些额外的头信息,比如 service name,magic number 等。

Rest 协议

1
2
3
4
5
6
7
8
<!-- 用rest协议在8080端口暴露服务 -->
<dubbo:protocol name="rest" port="8080"/>

<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.service.OrderService" ref="orderService"/>

<!-- 和本地bean一样实现服务 -->
<bean id="orderService" class="com.service.OrderServiceImpl" />

在代码中需要通过注解指定访问路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class OrderService {    
void createOrder(Order order);
}

@Path("orders") // 访问Url的相对路径
public class OrderServiceImpl implements OrderService {

@POST
@Path("create") // 访问Url的相对路径
// 将传递过来的JSON数据反序列化为Order对象
@Consumes({MediaType.APPLICATION_JSON})
public void createOrder(Order order) {
// create the order...
}
}

长连接 OR 短连接

Dubbo 协议缺省每服务每提供者每消费者使用单一长连接,如果数据量较大,可以使用多个连接。

1
2
3
4
5
6
<!-- 表示该服务使用 JVM 共享长连接 -->
<dubbo:service connections="0">
<dubbo:reference connections="0">
<!-- 表示该服务使用独立长连接 -->
<dubbo:service connections="1">
<dubbo:reference connections="1">

为什么要消费者比提供者个数多

因为 dubbo 协议采用单一长连接,假设网络为千兆网卡 3,根据测试经验数据每条连接最多只能压满 7MByte(不同的环境可能不一样),理论上 1 个服务提供者需要 20 个服务消费者才能压满网卡。

为什么不能传大包

因 dubbo 协议采用单一长连接,如果每次请求的数据包大小为 500KByte,假设网络为千兆网卡 3,每条连接最大 7MByte(不同的环境可能不一样,供参考),单个服务提供者的 TPS(每秒处理事务数)最大为:128MByte / 500KByte = 262。单个消费者调用单个服务提供者的 TPS(每秒处理事务数)最大为:7MByte / 500KByte = 14。如果能接受,可以考虑使用,否则网络将成为瓶颈。

为什么采用异步单一长连接

因为服务的现状大都是服务提供者少,通常只有几台机器,而服务的消费者多,可能整个网站都在访问该服务,比如 Morgan 的提供者只有 6 台提供者,却有上百台消费者,每天有 1.5 亿次调用,如果采用常规的 hessian 服务,服务提供者很容易就被压跨,通过单一连接,保证单一消费者不会压死提供者,长连接,减少连接握手验证等,并使用异步 IO,复用线程池,防止 C10K 问题(服务器无法服务 1w 左右的并发连接)。

1
2
3
4
5
6
<!-- 配置协议端口和服务提供方最大连接数,防止服务被压垮 -->
<dubbo:protocol name="dubbo" port="20880" accepts="1000" />
<!--配置dubbo默认协议 -->
<dubbo:provider protocol="dubbo" />
<!--配置dubbo设置服务协议 -->
<dubbo:service protocol="dubbo" />