Dubbo 入门教程之二

大纲

前言

学习资源

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
/**
* 暴露 Dubbo 服务
*/
@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 {

/**
* 引用 Dubbo 服务
*/
@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 -->
<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
/**
* 暴露 Dubbo 服务
*/
@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 {

/**
* 引用 Dubbo 服务
*/
@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 -->
<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
/**
* 暴露 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
<!-- 声明要暴露的服务 -->
<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 -->
<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 接口,比如常见的实现类有:RandomLoadBalanceRoundRobinLoadBalanceLeastActiveLoadBalance 等。

注解配置方式

  • 第一步:服务消费者设置负载均衡策略,可选策略包括:randomroundrobinleastactiveconsistenthashshortestresponse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
@RequestMapping("/user")
public class UserController {

/**
* 引用 Dubbo 服务
*/
@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
/**
* 暴露 Dubbo 服务
*/
@DubboService(weight = 100)
public class UserServiceImpl implements UserService {

@Override
public User getById(Long id) {
return new User(id, "Peter", 18);
}

}

XML 配置方式

  • 第一步:服务消费者设置负载均衡策略,可选策略包括:randomroundrobinleastactiveconsistenthashshortestresponse
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 -->
<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 接口,比如常见的实现类有:FailoverClusterFailfastClusterFailsafeClusterFailbackCluster 等。

注解配置方式

  • 服务消费者设置集群容错模式,可选模式包括:failoverfailfastfailsafefailbackforkingbroadcastavailablemergeable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@RequestMapping("/user")
public class UserController {

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


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

}

XML 配置方式

  • 服务消费者设置集群容错模式,可选模式包括:failoverfailfastfailsafefailbackforkingbroadcastavailablemergeable
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 {

/**
* 引用 Dubbo 服务
*/
@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"/>