大纲
前言
学习资源
Dubbo 高级特性
代码下载
本节完整的案例代码可以直接从 GitHub 下载对应章节 dubbo-lesson-06
。
序列化
- Dubbo 内部已经将序列化和反序列化的过程封装起来了。
- 开发者只需要在定义 POJO 类时实现
Seria1izable
接口,这样就可以使用 Dubbo 的序列化。 - 在企业开发中,一般会定义一个公共的 POJO 模块或者 API 模块,让生产者和消费者都依赖该模块。
![]()
地址缓存
当注册中心宕机后,Dubbo 服务消费者是否可以正常调用服务提供者的接口?
直连模式(Direct Connection):
- 如果服务消费者配置了直连模式(即直接指定了服务提供者的地址,而不依赖注册中心),那么即使注册中心宕机,服务消费者仍然可以正常调用服务提供者的接口。
注册中心模式(Registry Mode):
- Dubbo 的服务消费者在启动时会从注册中心拉取服务提供者的地址列表,并将其缓存在本地,以后再调用服务时就不需要访问注册中心。当服务提供者的地址发生变化后,注册中心会通知服务消费者更新服务提供者的地址。
- 如果注册中心宕机,但服务消费者在注册中心宕机之前已经成功启动并获得了服务提供者的地址列表,那么服务消费者可以直接使用本地缓存的地址列表继续调用服务提供者的接口。
- 如果注册中心宕机,且服务消费者在注册中心宕机后才启动,由于无法从注册中心获取服务提供者的地址列表,因此服务消费者将无法正常调用服务提供者的接口。
![]()
Dubbo 的容错机制
- Dubbo 提供了以下几种容错机制,如果在消费者的配置中启用了这些机制,并且本地有多个服务提供者的地址,那么即使某些提供者不可用,服务调用可能仍然成功。
- Failover:当一个服务提供者调用失败时,可以自动切换到另一个服务提供者。
- Failback:在服务提供者调用失败时,可以记录失败请求并稍后重试。
超时时间
![]()
特别注意
- Dubbo 的调用超时时间(单位是毫秒),支持在服务消费者和服务提供者两边进行配置。
- 如果服务消费者和服务提供者都配置了超时时间,那么服务消费者的配置优先级更高。
- 由于服务提供者最了解接口的业务逻辑,因此在企业开发中,建议在服务提供者中配置超时时间。
注解配置方式
1 2 3 4 5 6 7 8 9 10 11 12
|
@DubboService(timeout = 1000) public class UserServiceImpl implements UserService {
@Override public User getById(Long id) { 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 {
@DubboReference(timeout = 1000) private UserService userService;
@GetMapping("/getById/{id}") public User getById(@PathVariable("id") Long id) { return userService.getById(id); }
}
|
版本说明
Dubbo 从 2.7.7
版本开始提供 @DubboService
和 @DubboReference
注解,目的是为了替代 Dubbo 旧版本的 @Service
和 @Reference
注解。
XML 配置方式
1 2 3 4 5
| <dubbo:service interface="com.clay.dubbo.service.DemoService" ref="demoService" timeout="1000"/>
<bean id="demoService" class="com.clay.dubbo.provider.service.DemoServiceImpl"/>
|
1 2
| <dubbo:reference id="demoService" interface="com.clay.dubbo.service.DemoService" timeout="1000"/>
|
重试次数
![]()
特别注意
- Dubbo 的重试次数通常是配合超时时间(或者集群容错模式 FailoverCluster )一起使用的,当接口调用超时(或者调用出错),就重试 N 次接口调用。
- Dubbo 的重试次数(默认是重试 2 次,加上第一次接口调用,一共会调用 3 次 接口),支持在服务消费者和服务提供者两边进行配置。
- 如果服务消费者和服务提供者都配置了重试次数,那么服务消费者的配置优先级更高。
- 由于服务提供者最了解接口的业务逻辑,因此在企业开发中,建议在服务提供者中配置重试次数。
- 特别注意,只有幂等接口(比如查询、更改、删除接口)才建议设置重试次数,非幂等接口(比如新增接口)不建议设置重试次数,否则会出现数据一致性问题,例如插入重复数据。
注解配置方式
1 2 3 4 5 6 7 8 9 10 11 12
|
@DubboService(timeout = 1000, retries = 3) public class UserServiceImpl implements UserService {
@Override public User getById(Long id) { 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 {
@DubboReference(timeout = 1000, retries = 3) private UserService userService;
@GetMapping("/getById/{id}") public User getById(@PathVariable("id") Long id) { return userService.getById(id); }
}
|
版本说明
Dubbo 从 2.7.7
版本开始提供 @DubboService
和 @DubboReference
注解,目的是为了替代 Dubbo 旧版本的 @Service
和 @Reference
注解。
XML 配置方式
1 2 3 4 5
| <dubbo:service interface="com.clay.dubbo.service.DemoService" ref="demoService" timeout="1000" retries="3"/>
<bean id="demoService" class="com.clay.dubbo.provider.service.DemoServiceImpl"/>
|
1 2
| <dubbo:reference id="demoService" interface="com.clay.dubbo.service.DemoService" timeout="1000" retries="3"/>
|
多版本
![]()
在 Dubbo 中使用 version
属性来设置同一个接口的不同版本(即同一个接口有的不同实现类)时,会有多个不同版本的服务被注册到注册中心,如下图所示:
![]()
提示
Dubbo 的多版本机制可以用于实现灰度发布功能。当发布新功能(新版本)时,会让一部分用户先使用新功能(新版本),用户反馈没问题后,再将新功能(新版本)应用到所有用户。
注解配置方式
- 第一步:服务提供者配置多版本(即同一个接口有多个不同的实现类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
@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
|
@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 {
@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
| <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"/>
<bean id="demoService" class="com.clay.dubbo.provider.service.DemoServiceImpl"/> <bean id="demoService2" class="com.clay.dubbo.provider.service.DemoServiceImpl2"/>
|
1 2
| <dubbo:reference id="demoService" interface="com.clay.dubbo.service.DemoService" version="2.0"/>
|
负载均衡
Dubbo 提供了五种负载均衡策略,如下所示:
负载均衡策略 | 说明 | 适用场景 |
---|
RandomLoadBalance | 随机(默认策略),支持按权重设置随机概率 | 适用于请求量分散、服务性能相近的场景,能保证简单有效的负载均衡效果 |
RoundRobinLoadBalance | 轮询,支持按公约后的权重设置轮询比率 | 适用于服务节点性能相近、需要保证调用次数均匀的场景 |
LeastActiveLoadBalance | 最少活跃调用数,支持相同活跃数的权重随机 | 适用于服务性能差异较大或请求响应时间差异较大的场景,能提高请求分配效率 |
ConsistentHashLoadBalance | 一致性哈希,相同参数的请求总是发送到同一个服务提供者 | 适用于需要保证请求一致性的场景,例如:缓存服务或用户会话粘性 |
ShortestResponseLoadBalance | 最短响应时间优先,选择响应时间最短的服务提供者 | 适用于请求响应时间差异较大,且希望优先使用低延迟服务节点的场景 |
特别注意
- Dubbo 的负载均衡策略支持在服务消费者和服务提供者两边进行配置,但是服务消费者的配置优先级更高,即:
- (1) 如果服务消费者明确配置了负载均衡策略,则会使用服务消费者的策略。
- (2) 如果服务消费者未配置负载均衡策略,则会使用服务提供者通过
@DubboService
注解指定的负载均衡策略。 - (3) 如果服务消费者和服务提供者都未配置负载均衡策略,则会使用 Dubbo 的全局默认负载均衡策略(RandomLoadBalance)。
提示
Dubbo 的负载均衡策略都统一实现了 AbstractLoadBalance
接口,比如常见的实现类有:RandomLoadBalance
、RoundRobinLoadBalance
、LeastActiveLoadBalance
等。
注解配置方式
- 第一步:服务消费者设置负载均衡策略,可选策略包括:
random
、roundrobin
、leastactive
、consistenthash
、shortestresponse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @RestController @RequestMapping("/user") public class UserController {
@DubboReference(loadbalance = "random") private UserService userService;
@GetMapping("/getById/{id}") public User getById(@PathVariable("id") Long id) { return userService.getById(id); }
}
|
- 第二步:若需要设置负载均衡策略的权重,则可以在服务提供者中设置
weight
属性
1 2 3 4 5 6 7 8 9 10 11 12
|
@DubboService(weight = 100) public class UserServiceImpl implements UserService {
@Override public User getById(Long id) { return new User(id, "Peter", 18); }
}
|
XML 配置方式
- 第一步:服务消费者设置负载均衡策略,可选策略包括:
random
、roundrobin
、leastactive
、consistenthash
、shortestresponse
1 2
| <dubbo:reference id="demoService" interface="com.clay.dubbo.service.DemoService" loadbalance="random"/>
|
- 第二步:若需要设置负载均衡策略的权重,则需要在服务提供者中设置
weight
属性
1 2 3 4 5
| <dubbo:service interface="com.clay.dubbo.service.DemoService" ref="demoService" weight="100"/>
<bean id="demoService" class="com.clay.dubbo.provider.service.DemoServiceImpl"/>
|
集群容错
Dubbo 提供了八种集群容错模式,如下所示:
集群容错模式 | 说明 | 适用场景 |
---|
FailoverCluster | 失败重试(默认模式)。当出现服务调用失败,重试调用其它服务器,默认重试 2 次,使用 retries 属性配置重试次数 | 通常用于读操作(幂等操作),如查询数据时可以容忍偶尔失败并重试的场景 |
FailfastCluster | 快速失败,只发起一次调用,失败立即报错 | 通常用于写操作(非幂等操作),如数据插入操作,失败时不应该重试 |
FailsafeCluster | 失败安全,只发起一次调用,出现异常时,直接忽略掉,返回一个空结果 | 适用于不重要的操作,如日志记录或监控信息上报,出现失败不影响整体业务逻辑 |
FailbackCluster | 失败自动恢复,后台记录失败请求,定时重发 | 通常用于消息通知操作,如异步通知,失败后可自动补偿 |
ForkingCluster | 并行调用多个服务器,只要有一个成功调用即返回结果 | 通常用于实时性要求较高的读操作,但需要浪费更多的服务资源,可通过 forks = "2" 来设置最大并行数 |
BroadcastCluster | 广播调用所有服务提供者,逐个调用,任意一个服务提供者报错则报错 | 适用于更新配置或通知所有服务提供者的场景,如缓存更新或者服务健康检查 |
ZoneAwareCluster | 根据网络分区优先选择调用同区域内的服务提供者,降低跨区域调用延迟 | 适用于多区域部署场景,优先减少跨区域网络延迟的情况下实现高效负载均衡 |
MergeableCluster | 将多个服务提供者的调用结果进行合并,最终返回合并后的结果 | 适用于需要聚合结果的场景,例如分布式查询、汇总统计等操作 |
特别注意
Dubbo 的集群容错模式是在服务消费者中配置的。虽然服务提供者使用的 @DubboService
注解也可以配置 cluster
属性,但该 cluster
属性是用来声明服务的分组行为,通常配合服务分组或路由规则一起使用。简而言之,@DubboService
注解的 cluster
属性与消费者端的集群容错模式无关,它并不直接控制服务消费者的集群容错模式,而是影响服务的分发和消费方式。
提示
Dubbo 的集群容错模式都统一实现了 AbstractCluster
接口,比如常见的实现类有:FailoverCluster
、FailfastCluster
、FailsafeCluster
、FailbackCluster
等。
注解配置方式
- 服务消费者设置集群容错模式,可选模式包括:
failover
、failfast
、failsafe
、failback
、forking
、broadcast
、available
、mergeable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @RestController @RequestMapping("/user") public class UserController {
@DubboReference(cluster = "failfast") private UserService userService;
@GetMapping("/getById/{id}") public User getById(@PathVariable("id") Long id) { return userService.getById(id); }
}
|
XML 配置方式
- 服务消费者设置集群容错模式,可选模式包括:
failover
、failfast
、failsafe
、failback
、forking
、broadcast
、available
、mergeable
1 2
| <dubbo:reference id="demoService" interface="com.clay.dubbo.service.DemoService" cluster="failfast"/>
|
服务降级
特性说明
在 Dubbo 中有一种机制可以实现轻量级的服务降级(也叫 “本地伪装”)。Mock 是 Stub 的一个子集,便于服务提供者在客户端执行容错逻辑,因此经常需要在出现 RpcException(比如网络失败,超时等)时进行容错,而在出现业务异常(比如登录用户名 / 密码错误)时不需要容错。如果用 Stub,可能就需要捕获并依赖 RpcException 类,而用 Mock 就可以不依赖 RpcException,因为它的约定就是只有出现 RpcException 时才执行。
使用场景
本地伪装常被用于服务降级。比如某鉴权服务,当服务提供者全部挂掉后,假如此时服务消费者发起了一次远程调用,那么本次调用将会失败并抛出一个 RpcException 异常。为了避免出现这种直接抛出异常的情况出现,那么客户端就可以利用本地伪装来提供 Mock 数据返回授权失败。其他使用场景包括:
- 某服务或接口负荷超出最大承载能力范围,需要进行降级应急处理,避免系统崩溃。
- 调用的某非关键服务或接口暂时不可用时,返回模拟数据或空,业务还能继续可用。
- 降级非核心业务的服务或接口,腾出系统资源,尽量保证核心业务的正常运行。
- 某上游基础服务超时或不可用时,执行能快速响应的降级预案,避免服务整体雪崩。
使用方式
Dubbo 服务降级的使用方式:
第一种方式
mock = "force: return null"
:表示服务消费者对该服务的方法调用都直接返回 null
值,不发起远程调用。用于避免在不重要服务不可用时,对服务消费者产生负面影响。
第二种方式
mock = "fail:return null"
:表示服务消费者对该服务的方法调用出现失败时返回 null
值,不抛出异常。用于容忍不重要服务不稳定时,对服务消费者产生负面影响。最终效果类似使用 FailsafeCluster 集群容错模式。
更多使用方式
特别注意
- 在 Dubbo 中,服务降级是通过
mock
属性来实现,该属性是配置在服务消费者中的,不应该配置在服务提供者中。 - 服务降级逻辑应该完全由服务消费者端负责的,因为服务消费者需要决定在服务不可用时如何继续处理。
注解配置方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @RestController @RequestMapping("/user") public class UserController {
@DubboReference(mock = "fail:return null") private UserService userService;
@GetMapping("/getById/{id}") public User getById(@PathVariable("id") Long id) { return userService.getById(id); }
}
|
XML 配置方式
1 2
| <dubbo:reference id="demoService" interface="com.clay.dubbo.service.DemoService" mock = "fail:return null"/>
|
多协议支持
特性说明
协议类型 | 协议名 / 标识 | 传输层 | 核心特性 | 适用场景 | 是否默认协议 |
---|
Dubbo 协议 | dubbo | TCP | 单一长连接、NIO 异步通信、高性能 | 高并发小数据量场景(消费者数 >> 提供者) | 是 |
Triple 协议 | tri | HTTP/2 | 兼容 gRPC 生态、支持流式通信、跨语言能力增强 | 多语言交互、流式数据传输 | 否 |
gRPC 协议 | grpc | HTTP/2 | Google 开源 RPC 框架、高性能跨语言通信 | 跨语言高性能服务调用 | 否 |
REST 协议 | rest | HTTP | 严格遵循 RESTful 规范、支持 JSON/XML 格式 | 浏览器调用、跨平台集成 | 否 |
Hessian 协议 | hessian | HTTP | 表单序列化、短连接、传输效率优于 WebService | 文件 / 视频等大数据传输 | 否 |
Thrift 协议 | thrift | TCP | Apache 开源 RPC 框架、强类型接口定义 | 多语言服务交互 | 否 |
Redis 协议 | redis | 文本协议 | 基于 Redis 的轻量级通信 | 缓存操作、简单消息队列 | 否 |
MQTT 协议 | mqtt | TCP | 发布 / 订阅模式、低带宽占用 | 物联网设备通信 | 否 |
WebService | webservice | HTTP | 基于 SOAP 协议、XML 序列化 | 传统企业系统集成 | 否 |
HTTP 协议 | http | HTTP | 通用同步传输、支持表单序列化 | 混合数据大小场景 | 否 |
维度 | Triple | Dubbo | REST | gRPC |
---|
性能 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ |
跨语言 | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
流式通信 | 支持 | 不支持 | 不支持 | 支持 |
浏览器兼容 | 支持 | 不支持 | 直接支持 | 需要依赖 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-protocol: name: dubbo port: 20880 rest-protocol: name: rest port: 7777 server: tomcat hessian-protocol: name: hessian port: 8888 server: tomcat
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @DubboService(protocol = "dubbo") public class OrderServiceImpl implements OrderService { }
@DubboService(protocol = "rest") public class RoleServiceImpl implements RoleService { }
@DubboService(protocol = "hessian") public class UserServiceImpl implements UserService { }
@DubboService(protocol = {"dubbo", "rest"}) public class SystemServiceImpl implements SystemService { }
|
1 2 3 4 5 6 7 8 9 10 11
| @DubboReference(protocol = "dubbo") private OrderService orderService;
@DubboReference(protocol = "rest") private RoleService roleService;
@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:protocol name="dubbo" port="20880"/>
<dubbo:protocol name="rest" port="7777" server="tomcat"/>
<dubbo:protocol name="hessian" port="8888" server="tomcat"/>
|
1 2 3 4 5 6 7 8 9 10 11
| <dubbo:service interface="com.example.api.OrderService" ref="orderServiceImpl" protocol="dubbo"/>
<dubbo:service interface="com.example.api.RoleService" ref="roleServiceImpl" protocol="rest"/>
<dubbo:service interface="com.example.api.UserService" ref="userServiceImpl" protocol="hessian"/>
<dubbo:service interface="com.example.api.SystemService" ref="systemServiceImpl" protocol="dubbo,rest"/>
|
1 2 3 4 5 6 7 8
| <dubbo:reference id="orderService" interface="com.example.api.OrderService" protocol="dubbo"/>
<dubbo:reference id="roleService" interface="com.example.api.RoleService" protocol="rest"/>
<dubbo:reference id="systemService" interface="com.example.api.SystemService" protocol="dubbo,rest"/>
|
多注册中心
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 { }
|
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"/>
|
默认值配置
YML 配置方式
1 2 3 4 5 6 7
| dubbo: provider: timeout: 1000 consumer: timeout: 1000
|
XML 配置方式
1 2 3 4 5
| <dubbo:provider timeout="1000"/>
<dubbo:consumer timeout="1000"/>
|
1 2 3 4 5 6 7 8 9 10
| <dubbo:provider timeout="1000"> <dubbo:service interface="com.clay.api.HelloService" ref="helloService"/> <dubbo:service interface="com.clay.api.HelloService2" ref="helloService2"/> </dubbo:provider>
<dubbo:provider timeout="3000"> <dubbo:service interface="com.clay.api.DemoService" ref="demoService"/> <dubbo:service interface="com.clay.api.DemoService2" ref="demoService2"/> </dubbo:provider>
|
预览: