Dubbo 2 入门教程之三

大纲

前言

学习资源

Dubbo 核心特性

序列化协议

Java 序列化库性能测试

  • 各种 Java 的序列化库的性能测试比较结果可以看 这里 或者 GitHub Wiki

特性说明

  • Dubbo 内部已经将序列化和反序列化的过程封装起来了。
  • 开发者只需要在定义 POJO 类时实现 Seria1izable 接口,这样就可以使用 Dubbo 提供的序列化机制。
  • 在企业开发中,一般会定义一个公共的 POJO 模块或者 API 模块,让生产者和消费者都依赖该模块。

协议选择

  • Dubbo 支持的序列化协议
序列化协议优点缺点适用场景
Hessian2(默认协议)- 跨语言支持好
- 二进制序列化,协议紧凑
- 使用简单,Dubbo 原生支持
- 性能中等,不是最高
- 不支持对象引用共享
默认通用场景,适合大多数 RPC 调用
FastJson(JSON 格式)- 可读性强,易于调试
- 跨语言方便,尤其适合前端交互
- 文本冗余,体积大
- 性能低于二进制序列化协议(如 Hessian、Protobuf)
需要与前端或非 Java 系统交互,或调试、日志记录场景
GSON(JSON 格式)- 可读性强,易于调试
- 跨语言方便,尤其适合前端交互
- 文本冗余,体积大
- 性能低于二进制序列化协议(如 Hessian、Protobuf)
需要与前端或非 Java 系统交互,或调试、日志记录场景
Kryo- 高性能,二进制序列化速度快
- 体积小
- 支持对象图
- 不支持跨语言
- 需要手动注册类才能获得最佳性能
内部 Java 服务之间的高性能 RPC 调用,大数据领域用得较多
FST(Fast-Serialization)- 二进制序列化速度快,性能接近 Kryo
- 使用更简单
- 不支持跨语言
- 对类版本变化敏感
内部 Java 服务之间的高性能 RPC 调用
Protobuf(Google Protocol Buffers)- 高性能,二进制序列化速度快
- 体积小
- 强类型定义
- 跨语言支持好
- 需要 .proto 文件定义
- 学习成本高
跨语言、高性能 RPC 或需要协议稳定性的场景
Protostuff(基于 Protobuf)- 高性能二进制序列化,速度快、体积小
- 无需 .proto 文件定义,直接支持 Java POJO
- 序列化 / 反序列化效率显著高于 Hessian2
- 仅适用于 Java 环境(不支持跨语言)
- 对象类结构变化后兼容性较差(需保持字段一致)
对性能要求高的 Java 内部服务调用,如高并发、高吞吐的微服务场景
Avro- Hadoop 的子项目
- 支持动态 Schema
- 跨语言支持好
- 二进制序列化
- 性能不如 Protobuf
- 使用复杂
大数据场景,如 Kafka、Hadoop,需要动态 Schema
Java 原生序列化 - Java 原生支持,无需额外依赖
- 保留完整对象结构
- 二进制序列化
- 体积大,性能低
- 安全性差(反序列化漏洞风险高)
- 不支持跨语言
仅用于内部 Java 服务调试或原型验证,不建议生产使用
  • Dubbo 序列化协议的选择建议
场景推荐的序列化协议说明
默认场景(通用 RPC 调用)Hessian2Dubbo 默认的序列化协议,跨语言支持好、易用,性能中等,适合大多数场景
Java 内部高性能 RPC 调用 Protostuff、Kryo、FST 二进制序列化速度快、体积小,适合 Java 内部服务之间的高性能调用
跨语言且追求极致性能 Protobuf 高性能、体积小、跨语言支持好,但需要 .proto 文件定义,学习成本较高
调试或日志场景 FastJson 可读性强,易于调试,适合调试、日志记录或与前端交互,但性能较低
不建议使用 Java 原生序列化性能低、体积大、安全性差(反序列化漏洞风险高),仅适合内部调试或原型验证

Dubbo 支持多端口多序列化协议

  • 如果同一个服务需要支持多种序列化协议,可以通过不同 <dubbo:protocol> 配置多个端口,比如:
    1
    2
    <dubbo:protocol name="dubbo" port="20881" serialization="kryo" />
    <dubbo:protocol name="dubbo" port="20880" serialization="hessian2" />

使用方式

注解配置方式
  • 服务提供者定义通信协议
1
2
3
4
5
6
7
8
9
10
11
12
dubbo:
# 服务信息
application:
name: dubbo-provider-application
# 注册中心地址
registry:
address: zookeeper://127.0.0.1:2181
# 服务提供者的通信协议
protocol:
name: dubbo # 使用 Dubbo 作为通信协议
port: 20880 # 监听端口
serialization: hessian2 # 使用 Hessian2 作为序列化协议
  • 服务提供者暴露服务
1
2
@DubboService
public class UserServiceImpl implements UserService { }
  • 服务消费者引用服务
1
2
3
// 使用 Hessian2 作为序列化协议
@DubboReference(protocol = "dubbo", serialization = "hessian2")
private UserService userService;
  • 在服务消费者中,还可以全局指定通信协议和序列化协议,这样就不需要在每个 @DubboReference 注解中单独指定
1
2
3
4
5
6
7
8
9
10
11
dubbo:
# 服务信息
application:
name: dubbo-consumer-application
# 注册中心地址
registry:
address: zookeeper://127.0.0.1:2181
# 服务消费者的通信协议
protocol:
name: dubbo # 使用 Dubbo 作为通信协议
serialization: hessian2 # 使用 Hessian2 作为序列化协议
XML 配置方式
  • 服务提供者定义协议
1
2
3
4
5
6
7
8
<!-- 配置服务应用名 -->
<dubbo:application name="dubbo-provider-application"/>

<!-- 注册中心配置 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="5000"/>

<!-- 使用 Hessian2 作为序列化协议 -->
<dubbo:protocol name="dubbo" port="20880" serialization="hessian2"/>
  • 服务提供者暴露服务
1
2
3
4
5
<!-- 服务实现类 Bean -->
<bean id="userServiceImpl" class="com.example.service.impl.UserServiceImpl" />

<!-- 声明要暴露的服务 -->
<dubbo:service interface="com.example.service.UserService" ref="userServiceImpl" />
  • 服务消费者引用服务
1
2
<!-- 引用远程服务,并使用 Hessian2 作为序列化协议 -->
<dubbo:reference id="userService" interface="com.example.service.UserService" protocol="dubbo" serialization="hessian2" />

多版本发布

特性说明

在 Dubbo 中使用 version 属性来设置同一个接口的不同版本(即同一个接口有的不同实现类)时,会有多个不同版本的服务被注册到注册中心,如下图所示:

提示

Dubbo 的多版本机制可以用于实现灰度发布功能。当发布新功能(新版本)时,会让一部分用户先使用新功能(新版本),用户反馈没问题后,再将新功能(新版本)应用到所有用户。

注解配置方式

  • 第一步:服务提供者配置多版本(即同一个接口有多个不同的实现类)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 暴露 Dubbo 服务
*/
@DubboService(version = "1.0")
public class UserServiceImpl implements UserService {


@Override
public User getById(Long id) {
System.out.println("===> invoke getById() v1.0");
return new User(id, "Peter", 18);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 暴露 Dubbo 服务
*/
@DubboService(timeout = 1000, retries = 3, version = "2.0")
public class UserServiceImpl2 implements UserService {

@Override
public User getById(Long id) {
System.out.println("===> invoke getById() v2.0");
return new User(id, "Peter", 18);
}

}
  • 第二步:服务消费者调用指定版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
@RequestMapping("/user")
public class UserController {

/**
* 引用 Dubbo 服务
*/
@DubboReference(version = "2.0")
private UserService userService;

@GetMapping("/getById/{id}")
public User getById(@PathVariable("id") Long id) {
return userService.getById(id);
}

}

XML 配置方式

  • 第一步:服务提供者配置多版本(即同一个接口有多个不同的实现类)
1
2
3
4
5
6
7
<!-- 服务实现类 Bean -->
<bean id="demoService" class="com.clay.dubbo.provider.service.DemoServiceImpl"/>
<bean id="demoService2" class="com.clay.dubbo.provider.service.DemoServiceImpl2"/>

<!-- 声明要暴露的服务 -->
<dubbo:service interface="com.clay.dubbo.service.DemoService" ref="demoService" version="1.0"/>
<dubbo:service interface="com.clay.dubbo.service.DemoService" ref="demoService2" version="2.0"/>
  • 第二步:服务消费者调用指定版本
1
2
<!-- 引用远程服务 -->
<dubbo:reference id="demoService" interface="com.clay.dubbo.service.DemoService" version="2.0"/>

多注册中心注册

特性说明

多注册中心的概述

  • Dubbo 支持同一个服务向多个注册中心同时注册,或者不同的服务分别注册到不同的注册中心上去,甚至可以同时引用注册在不同注册中心上的同名服务。

多注册中心的使用

  • 同一个服务注册到不同的注册中心。比如:中文站有些服务来不及在青岛部署,只在杭州部署,而青岛的其它应用需要引用此服务,就可以将青岛的服务同时注册到两个注册中心,具体配置如下图所示:

  • 不同的服务注册到不同的注册中心。比如:CRM 系统中有些服务是专门为国际站设计的,有些服务是专门为中文站设计的,具体配置如下图所示:

  • 单独引用不同注册中心的服务。比如:CRM 系统中需要同时调用中文站和国际站的 PC2 服务,而 PC2 服务在中文站和国际站均有部署,接口及版本号都一样,但连接的数据库不一样,具体配置如下图所示:

  • 同时引用不同注册中心的同名服务,可以使用竖号分隔多个不同注册中心地址。比如:CRM 系统中需要同时调用中文站和国际站的 Pay 服务,而 Pay 服务在中文站和国际站均有部署,接口及版本号都一样,连接的数据库也一样,具体配置如下图所示:

一个服务多个注册中心的应用场景

Dubbo 支持的注册中心类型

Dubbo 支持的注册中心有以下几种(不限于):

Dubbo 3 支持的注册中心类型

在 Dubbo 3 中,默认支持 ZooKeeper 与 Nacos 作为注册中心,而且 Dubbo 扩展生态提供了 Consul、Etcd、Redis、Multicast 等注册中心的扩展实现,还支持 Kubernetes、Mesh 体系的服务发现。

使用方式

YML 配置方式
  • 定义注册中心的方式一(适用于注册中心不同协议、不同集群的混合配置场景)
1
2
3
4
5
6
7
8
9
10
11
dubbo:
# 多注册中心配置
registries:
chinaRegistry:
protocol: zookeeper
address: 10.20.141.150:2181
default: true
intlRegistry:
protocol: nacos
address: 10.20.154.177:8848
default: false
  • 定义注册中心的方式二(适用于不同注册中心均为同一协议类型的场景)
1
2
3
4
5
dubbo:
# 多注册中心配置
registry:
protocol: zookeeper
address: 10.20.141.150:2181|10.20.145.136:2181
  • 服务提供者暴露服务
1
2
3
4
5
6
7
// 这个接口向中文站注册中心注册服务
@DubboService(registry = "chinaRegistry")
public class HelloServiceImpl implements HelloService { }

// 这个接口向国际站注册中心注册服务
@DubboService(registry = "intlRegistry")
public class DemoServiceImpl implements DemoService { }
  • 服务消费者引用服务
1
2
3
4
5
6
7
// 这个接口引用中文站注册中心里的服务
@DubboReference(registry = "chinaRegistry")
private HelloService helloService;

// 这个接口引用国际站注册中心里的服务
@DubboReference(registry = "intlRegistry")
private DemoService demoService;
XML 配置方式
  • 定义注册中心的方式一(适用于注册中心不同协议、不同集群的混合配置场景)
1
2
3
4
<!-- 多注册中心配置 -->
<dubbo:registry id="chinaRegistry" protocol="zookeeper" address="10.20.141.150:2181" default="true"/>

<dubbo:registry id="intlRegistry" protocol="nacos" address="10.20.154.177:8848 " default="false"/>
  • 定义注册中心的方式二(适用于不同注册中心均为同一协议类型的场景)
1
<dubbo:registry protocol="zookeeper" address="10.20.141.150:2181|10.20.145.136:2181"/>
  • 服务提供者暴露服务
1
2
3
4
5
<!-- 向中文站注册中心注册服务 -->
<dubbo:service interface="com.clay.api.HelloService" ref="helloService" registry="chinaRegistry"/>

<!-- 向国际站注册中心注册服务 -->
<dubbo:service interface="com.clay.api.DemoService" ref="demoService" registry="intlRegistry"/>
  • 服务消费者引用服务
1
2
3
4
5
<!-- 这个接口引用中文站注册中心里的服务 -->
<dubbo:reference id="helloService" interface="com.example.api.HelloService" registry="chinaRegistry"/>

<!-- 这个接口引用国际站注册中心里的服务 -->
<dubbo:reference id="demoService" interface="com.example.api.DemoService" registry="intlRegistry"/>

多通信协议发布

特性说明

  • 多端口多通信协议

    • 多通信协议发布是指为同一个服务同时提供多种通信协议访问方式,多通信协议可以是任意两个或多个通信协议的组合。
    • 比如:一个服务基于不同的端口同时支持 dubbo + rest 通信协议。
      • 服务提供者实例同时监听两个端口 208808080
      • 同一个服务提供者实例,会在注册中心注册两条地址 URL。
      • 不同的消费端可以选择以不同通信协议调用同一个提供者发布的服务。
    • 对于消费端而言,如果用户没有明确配置,默认情况下 Dubbo 框架会自动选择 dubbo 通信协议调用。
    • Dubbo 框架支持配置通过哪个通信协议访问服务,如 @DubboReference (protocol="rest"),或者在 application.yml 配置文件中指定全局默认值:
      1
      2
      3
      dubbo:
      consumer:
      protocol: rest
  • 单端口多通信协议

    • Dubbo 3 除了支持多端口多通信协议、注册多条 URL 到注册中心的方式,对于 dubbotriple 这两个内置通信协议,还提供了在单个端口上同时支持 dubbotriple 通信协议的能力,具体配置方式如下所示。
    • 这对于老用户来说是一个非常重要的能力,因为它可以做到不增加任何负担的情况下,让使用 dubbo 通信协议的用户可以额外发布 triple 通信协议,这样当所有的应用都实现多通信协议发布之后,就可以设置消费端去通过 triple 通信协议发起调用了。
      1
      2
      3
      4
      5
      // 在单个端口上,同时支持 dubbo  triple 通信协议
      dubbo:
      protocol:
      name: dubbo
      ext-protocol: tri

协议选择

特别注意

  • Dubbo 支持多种通信协议,在 3.0 版本之前支持 Dubbo、gRPC、Hessian2、REST 等核心通信协议。
  • 从 Dubbo 3.0 开始,Dubbo 官方新引入了自研的 Triple 通信协议,不过也保留了 Dubbo 和 REST 通信协议的支持。
  • 从 Dubbo 3.2 开始,Dubbo 官方已经废弃原有的 gRPC 通信协议,使用 Triple 通信协议进行替代,Triple 通信协议完全兼容 gRPC 通信协议。
  • 从 Dubbo 3.3 开始,Dubbo 官方直接移除了 REST 通信协议的直接支持,使用 Triple 通信协议间接支持 REST 通信协议。
  • Dubbo 3 支持的通信协议
通信协议通信协议名 / 标识传输层核心特性适用场景是否为默认通信协议
Dubbo 通信协议dubboTCP 单一长连接、NIO 异步通信、高性能高并发小数据量场景(消费者数 >> 提供者)
Triple 通信协议triHTTP/2 兼容 gRPC 生态、支持流式通信、跨语言能力增强多语言交互、流式数据传输
REST 通信协议restHTTP 严格遵循 RESTful 规范、支持 JSON/XML 格式浏览器调用、跨平台集成
gRPC 通信协议grpcHTTP/2Google 开源 RPC 框架、高性能跨语言通信跨语言高性能服务调用
Thrift 通信协议thriftTCPApache 开源 RPC 框架、强类型接口定义多语言服务交互
Hessian 通信协议hessianHTTP 表单序列化、短连接、传输效率优于 WebService 文件 / 视频等大数据传输
HTTP 通信协议httpHTTP 通用同步传输、支持表单序列化混合数据大小场景
Redis 通信协议redis文本通信协议基于 Redis 的轻量级通信缓存操作、简单消息队列
MQTT 通信协议mqttTCP 发布 / 订阅模式、低带宽占用物联网设备通信
WebServicewebserviceHTTP 基于 SOAP 通信协议、XML 序列化传统企业系统集成
  • 通信协议的选择对比
维度 TripleDubboRESTgRPC
性能⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
跨语言⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
流式通信支持不支持不支持支持
浏览器兼容支持不支持直接支持需要依赖 Gateway(网关)
配置复杂度
  • 通信协议使用的注意事项
问题解决方案
gRPC 通信协议浏览器无法直接调用通过 API Gateway(如 gRPC-Gateway)转换 HTTP 请求
序列化机制不一致 Triple 强制使用 Protobuf,其他通信协议使用 Protobuf 需要配置 serialization="protobuf"
多通信协议端口冲突确保每个通信协议使用的端口唯一

使用方式

注解配置方式
  • 服务提供者定义通信协议
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
dubbo:
# 服务信息
application:
name: dubbo-provider-application
# 注册中心地址
registry:
address: zookeeper://127.0.0.1:2181
# 服务提供者的通信协议
protocols:
# Dubbo 通信协议
dubbo-protocol:
name: dubbo
port: 20880
# REST 通信协议
rest-protocol:
name: rest
port: 7777
server: tomcat
# Hessian 通信协议
hessian-protocol:
name: hessian
port: 8888
server: tomcat
  • 服务提供者暴露服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 这个接口仅支持 Dubbo 通信协议
@DubboService(protocol = "dubbo")
public class OrderServiceImpl implements OrderService { }

// 这个接口仅支持 REST 通信协议
@DubboService(protocol = "rest")
public class RoleServiceImpl implements RoleService { }

// 这个接口仅支持 Hessian 通信协议
@DubboService(protocol = "hessian")
public class UserServiceImpl implements UserService { }

// 这个接口支持 Dubbo 通信协议和 REST 通信协议
@DubboService(protocol = {"dubbo", "rest"})
public class SystemServiceImpl implements SystemService { }
  • 服务消费者引用服务
1
2
3
4
5
6
7
8
9
10
11
// 这个接口使用 Dubbo 通信协议
@DubboReference(protocol = "dubbo")
private OrderService orderService;

// 这个接口使用 REST 通信协议
@DubboReference(protocol = "rest")
private RoleService roleService;

// 这个接口使用 Dubbo 通信协议和 REST 通信协议(多种通信协议使用逗号隔开),消费者默认会按 Dubbo -> REST 的通信协议顺序尝试调用
@DubboReference(protocol = "dubbo,rest")
private SystemService systemService;
XML 配置方式
  • 服务提供者定义通信协议
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 配置服务应用名 -->
<dubbo:application name="dubbo-provider-application"/>

<!-- 注册中心配置 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="5000"/>

<!-- Dubbo 通信协议 -->
<dubbo:protocol name="dubbo" port="20880"/>

<!-- REST 通信协议 -->
<dubbo:protocol name="rest" port="7777" server="tomcat"/>

<!-- Hessian 通信协议 -->
<dubbo:protocol name="hessian" port="8888" server="tomcat"/>
  • 服务提供者暴露服务
1
2
3
4
5
6
7
8
9
10
11
<!-- 这个接口仅支持 Dubbo 通信协议 -->
<dubbo:service interface="com.example.api.OrderService" ref="orderServiceImpl" protocol="dubbo"/>

<!-- 这个接口仅支持 REST 通信协议 -->
<dubbo:service interface="com.example.api.RoleService" ref="roleServiceImpl" protocol="rest"/>

<!-- 这个接口仅支持 Hessian 通信协议 -->
<dubbo:service interface="com.example.api.UserService" ref="userServiceImpl" protocol="hessian"/>

<!-- 这个接口支持 Dubbo 通信协议和 REST 通信协议(多种通信协议使用逗号隔开) -->
<dubbo:service interface="com.example.api.SystemService" ref="systemServiceImpl" protocol="dubbo,rest"/>
  • 服务消费者引用服务
1
2
3
4
5
6
7
8
<!-- 这个接口使用 Dubbo 通信协议 -->
<dubbo:reference id="orderService" interface="com.example.api.OrderService" protocol="dubbo"/>

<!-- 这个接口使用 REST 通信协议 -->
<dubbo:reference id="roleService" interface="com.example.api.RoleService" protocol="rest"/>

<!-- 这个接口使用 Dubbo 通信协议和 REST 通信协议(多种通信协议使用逗号隔开),消费者默认会按 Dubbo -> REST 的通信协议顺序尝试调用 -->
<dubbo:reference id="systemService" interface="com.example.api.SystemService" protocol="dubbo,rest"/>

参考资料