参考官网
- 大致流程图
1. 初始化项目
参考官网进行项目的初始化即可。
2. 相关配置
主要参考官网:这里仅给出提示
- application.yml
dubbo:
application:
name: dubbo-springboot-demo-consumer
protocol:
name: dubbo
port: -1
registry:
address: zookeeper://${zookeeper.address:127.0.0.1}:2181
#address: nacos://${nacos.address:127.0.0.1}:8848 # 也可以缓存nacos配置
- 服务端配置: @DubboService
@DubboService
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
return "Hello " + name;
}
}
- 启动类配置:@EnableDubbo
@SpringBootApplication
@EnableDubbo
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
3. 公共实体类的序列化
在公共模块中记得将实体类进行序列化,因为网络传输会进行序列化和反序列化
public class ABS implements Serializable {
private static final long serialVersionUID = 3193869407076435938L;
}
4. 地址缓存
Dubbo的地址缓存(Address Cache)是用于缓存服务提供者的网络地址的机制,旨在提高服务调用的性能和稳定性。地址缓存由服务消费者端维护,在调用远程服务时,Dubbo会从缓存中选择一个地址进行调用,而不是每次都从注册中心重新获取地址列表。
Dubbo的地址缓存更新策略包括两种:
- 定时更新:Dubbo会定期(周期性地)从注册中心获取服务提供者的地址列表,并更新到地址缓存中。这样做可以保证地址缓存中的地址与注册中心中的地址保持同步,确保服务调用的准确性和稳定性。定时更新的频率可以通过Dubbo的配置进行调整。
- 事件驱动更新:Dubbo还支持通过注册中心的事件通知机制来实现地址缓存的更新。当注册中心中的服务提供者地址发生变化时,注册中心会向Dubbo发送事件通知,Dubbo会接收并处理这些事件通知,从而及时更新地址缓存中的地址。这样做可以减少不必要的定时更新操作,提高系统的性能和效率。
Dubbo默认情况下会采用定时更新的方式来更新地址缓存,但也支持事件驱动的方式。开发者可以根据实际情况选择合适的更新策略,以满足应用场景的需求。Dubbo提供了灵活的配置选项,允许开发者自定义地址缓存的更新策略和更新频率,以便更好地适应不同的应用场景。
5. 超时与重试
Dubbo中的超时和重试问题可能由多种原因造成,常见的包括网络延迟、服务提供者负载过高、服务调用失败等。
例如:服务消费者在调用服务提供者的时候发生了阻塞、等待的情形,这个时候,服务消费者会一直等待下去。在某个峰值时刻,大量的请求都在同时请求服务消费者,会造成线程的大量堆积,势必会造成雪崩。
超时问题:
dubbo 利用超时机制来解决这个问题,设置一个超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。
- 可以通过配置
timeout
参数来设置服务调用的超时时间。 - 建议在服务端注解上设置:如下:设置3000ms自动超时断开
@DubboService(timeout = 3000, retries = 2) // 超时时间,和重试次数
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
return "Hello " + name + "娃哈哈";
}
}
重试机制:
重试问题通常是由于服务调用失败或者服务提供者不可用导致的。Dubbo提供了重试机制,可以在服务调用失败时自动进行重试。
-
可以通过配置
retries
参数来设置重试次数。 -
建议在服务端注解上设置:如下:设置服务调用失败时自动重试2次
@DubboService(timeout = 3000, retries = 2) // 超时时间,和重试次数
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
return "Hello " + name + "娃哈哈";
}
}
6. 多版本控制
Dubbo的多版本控制功能允许服务消费者在调用远程服务时指定所需的服务版本,以便灵活地管理服务的升级和兼容性。
下面介绍一些使用场景和实现方式:
- 平滑升级:当服务提供者发布新版本时,可以通过多版本控制实现平滑升级,避免影响到已有的服务消费者。
- A/B测试:在进行新功能的试验或测试时,可以将部分流量引流到新版本的服务上,通过多版本控制实现A/B测试。
- 兼容旧版本:为了保持与老版本的兼容性,可以在新版本发布时通过多版本控制指定特定的服务版本。
- 灰度发布:在新版本发布前,先将少量流量引流到新版本服务上进行测试和验证,然后逐步扩大范围,通过多版本控制实现灰度发布。
dubbo 中使用version 属性来设置和调用同一个接口的不同版本:
如下:
服务端
- DemoServiceImpl
@DubboService(version = "v1.0", timeout = 3000, retries = 0) // 超时时间,和重试次数
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
return "Hello " + name + "娃哈哈";
}
}
- DemoServiceImpl2
@DubboService(version = "v2.0", timeout = 3000, retries = 0) // 超时时间,和重试次数
public class DemoServiceImpl2 implements DemoService {
@Override
public String sayHello(String name) {
return "Hello " + name + "娃哈哈";
}
}
消费端(调用端):
- 这里使用version 指定 v2.0版本。
@Component
public class Task implements CommandLineRunner {
@DubboReference(version = "v2.0") // 配置版本控制,对应调用不同版本的服务,达到灰度发布的效果
private DemoService demoService;
@Override
public void run(String... args) throws Exception {
String result = demoService.sayHello("world");
System.out.println("Receive result ======> " + result);
}
}
7. 负载均衡
说到负载均衡,就表明有多个服务的提供者
Dubbo可以在每个服务提供者上配置权重,使用weight参数实现,如下:
- 这里配置100的权重
@DubboService(weight = 100, version = "v1.0", timeout = 3000, retries = 0) // 设置版本控制, 超时时间,和重试次数
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
return "Hello " + name + "娃哈哈";
}
}
图示:
Dubbo提供了多种负载均衡策略:
负载均衡策略:
- 随机负载均衡(Random Load Balance):
- 使用场景:适用于服务提供者数量较多、服务实例性能相近的场景。随机负载均衡策略可以均匀地分配请求到各个服务提供者上,从而实现负载均衡。
- 轮询负载均衡(Round Robin Load Balance):
- 使用场景:适用于服务提供者数量较多、服务实例性能相近的场景。轮询负载均衡策略按照固定顺序依次选择服务提供者,将请求依次分配给每个服务提供者,从而实现负载均衡。
- 加权轮询负载均衡(Weighted Round Robin Load Balance):
- 使用场景:适用于服务提供者数量较多、服务实例性能不均匀的场景。加权轮询负载均衡策略根据服务提供者的权重值来动态调整分配请求的比例,将更多的请求分配给性能更好的服务提供者。
- 一致性哈希负载均衡(Consistent Hash Load Balance):
- 使用场景:适用于需要保持请求与服务提供者之间的一致性映射关系的场景,如分布式缓存等。一致性哈希负载均衡策略通过哈希算法将请求分配给服务提供者,保持了请求与服务提供者之间的一致性映射关系。
如下案例:调用端配置
@Component
public class Task implements CommandLineRunner {
// 配置负载均衡(4种策略):
// - Random: 表示随机访问,默认
// - RoundRobin: 表示按权重轮询访问
// - LeastActive: 表示最小活跃调用
// - ConsistentHash: 表示一致性Hash,相同参数的请求总是发到同一提供者
@DubboReference(loadbalance = "random")
private DemoService demoService;
@Override
public void run(String... args) throws Exception {
String result = demoService.sayHello("world");
System.out.println("Receive result ======> " + result);
}
}
8. 集群容错
Dubbo的集群容错指的是在分布式系统中,当服务调用失败或者服务提供者出现故障时,Dubbo能够采取一定的容错机制来保证系统的可用性和稳定性。
Dubbo提供了多种集群容错解决方案,以应对不同的故障情况,以下是常见的集群容错解决方案:
- 失败自动切换(Failover):
- 当服务调用失败时,Dubbo会自动切换到其他可用的服务提供者进行重试。这种策略适用于需要保证服务调用的可用性,对于服务调用失败时能够自动切换到其他可用服务提供者的情况。
- 失败快速(Failfast):
- 当服务调用失败时,Dubbo会立即返回失败结果,不进行重试。这种策略适用于对于服务调用的实时性要求较高,对于失败情况不进行重试,快速返回失败结果。
- 失败安全(Failsafe):
- 当服务调用失败时,Dubbo会忽略失败的调用结果,直接返回空结果。这种策略适用于对于服务调用的结果不敏感,可以容忍部分调用失败的情况。
- 失败忽略(Failover):
- 当服务调用失败时,Dubbo会忽略失败的调用结果,继续进行后续调用。这种策略适用于对于部分调用失败的情况不做处理,继续进行后续调用。
- 失败重试(Failback):
- 当服务调用失败时,Dubbo会记录失败的调用结果,并在服务提供者恢复之后进行重试。这种策略适用于服务提供者出现故障时,等待服务提供者恢复后进行重试。
Dubbo默认采用的集群容错解决方案是失败自动切换(Failover),即在服务调用失败时会自动切换到其他可用的服务提供者进行重试。开发者可以根据实际情况选择合适的集群容错解决方案,以确保系统的可用性和稳定性。Dubbo提供了灵活的配置选项,允许开发者根据需求自定义集群容错策略。
如下案例:
@Component
public class Task implements CommandLineRunner {
// 容错机制
// - Failover Cluster: 默认,失败自动切换,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟。可通过retries="2"来设置重试次数(不含第一次)。
// - Failfast Cluster: 快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
// - Failsafe Cluster: 失败安全,出现异常时,直接忽略,返回一个空结果。通常用于写入审计日志等操作。
// - Failback Cluster: 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
// - Forking Cluster: 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过forks="2"来设置最大并行数。
// - Broadcast Cluster: 广播调用所有提供者,逐个调用,任意一台报错则报错。(1.0.12版本开始支持)
@DubboReference(cluster = "failover")
private DemoService demoService;
@Override
public void run(String... args) throws Exception {
String result = demoService.sayHello("world");
System.out.println("Receive result ======> " + result);
}
}
9. 服务降级
Dubbo的服务降级指的是在服务调用失败或者服务提供者出现故障时,为了保证系统的稳定性和可用性,通过一定的手段来保障核心功能的正常运行。
以下是常见的服务降级解决方案:
- 返回默认值:
- 当服务调用失败时,返回一个默认值或者空值作为结果,保证服务调用的正常返回。这种解决方案适用于对于服务调用的结果不敏感,可以容忍部分调用失败的情况。
- 返回缓存数据:
- 当服务调用失败时,返回缓存中的数据作为结果,避免因服务调用失败而导致整个系统的不可用。这种解决方案适用于对于服务调用的实时性要求不高,可以容忍部分数据的延迟和不一致性。
- 返回降级页面:
- 当服务调用失败时,返回一个友好的降级页面,提示用户服务暂时不可用,引导用户使用其他功能或者稍后重试。这种解决方案适用于对于服务调用的实时性要求较低,可以通过降级页面来保护核心功能的正常运行。
- 返回错误码:
- 当服务调用失败时,返回一个错误码或者错误信息作为结果,告知调用方服务调用失败的原因,让调用方根据错误码来进行处理。这种解决方案适用于需要明确告知调用方服务调用失败原因的情况。
Dubbo并未提供原生的服务降级解决方案,但可以通过结合其他组件或者自定义代码来实现服务降级功能。例如,可以结合Hystrix等熔断器来实现服务降级,或者在服务消费者端自定义代码来处理服务调用失败时的降级逻辑。总的来说,服务降级是分布式系统中保证系统稳定性和可用性的重要手段,需要根据实际情况选择合适的解决方案来应对服务调用失败的情况。
如下案例:
@Component
public class Task implements CommandLineRunner {
// 服务降级:
// - fore:return null: 表示对该服务的方法调用都直接返回null值,不发起远程调用。通常用于测试时对某些服务进行禁用。
// - fail:return exception: 表示对该服务方法调用失败后抛出MockException异常。
@DubboReference(mock = "fore:return null")
private DemoService demoService;
@Override
public void run(String... args) throws Exception {
String result = demoService.sayHello("world");
System.out.println("Receive result ======> " + result);
}
}