2. Dubbo基础应用
1. 开篇
本文主要介绍一些dubbo的高级应用。
在平时使用dubbo时,最常用的还是直接加@DubboService
、@DubboReference
注解,dubbo还提供了更多的高级功能供我们使用。
本文采用的示例是在上一篇Dubbo框架介绍的基础上进行修改的,这里不再赘述。
2. 服务版本
@DubboService
、@DubboReference
都有version属性,代表一个服务的版本。比如在平时业务出现不兼容的升级时,为了不影响调用方,那么可以在服务提供者新加一个服务,用版本号来区分,想用新服务时,只要指定新服务的版本号就可以了。
服务提供者指定版本
@DubboService(version = "1.0.0")
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
System.out.println("provider received invoke of sayHello: " + name);
return "Hello, " + name;
}
}
服务消费者指定版本
@Service
public class ConsumerService {
@DubboReference(version = "1.0.0")
private HelloService helloService;
public void sayHello() {
String result = helloService.sayHello("world");
System.out.println(result);
}
}
除了在注解里指定版本外,还可以在配置文件里指定版本,例如:
dubbo.application.name=dubbo-consumer
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.provider.version=1.0.0
dubbo.consumer.version=1.0.0
3. 服务分组
服务分组用法与服务版本基本一致,可以直接参考服务版本,只需要把version
换成group
即可。
一般,当一个接口有多种方式实现时或有不同的处理逻辑处理时,用group
来区分。当服务升级出现不兼容的升级时,可以用version
来过渡隔离,不同版本号和group之间不能相互调用。
4. 负载均衡
Dubbo支持以下几种负载均衡策略:Random LoadBalance
、RoundRobin LoadBalance
、LeastActive LoadBalance
、ConsistentHash LoadBalance
、Shorttest response LoadBalance
。除了dubbo提供这些内置负载均衡策略,还支持自定义扩展负载均衡策略。
服务端和消费端都可以配置负载均衡策略,如果都配置的话,以消费端为准。
4.1 Random LoadBalance
随机策略
,是默认的负载均衡策略,是按按权重设置随机概率。如果服务端有多个示例,在消费端调用时会随机找一个服务端进行调用。根据调用量的情况,调用量越大,每个服务被调用的概率也就越均匀。
服务端配置
@DubboService(loadbalance = "random")
public class HelloServiceImpl implements HelloService {
}
消费端配置
@DubboReference(loadbalance = "random")
private HelloService helloService;
当然,随机策略是默认的,可以不配置。
4.2 RoundRobin LoadBalance
轮询策略
,dubbo会初始化一个本地权重表,然后按照权重进行轮询的负载均衡算法。消费端调用时会按一定的顺序去调用,此方式有个弊端是,如果服务端有一个服务出现异常执行很慢,那么时间长了,所有的请求都会卡在这个服务上。
服务端配置
@DubboService(loadbalance = "roundrobin")
public class HelloServiceImpl implements HelloService {
}
消费端配置
@DubboReference(loadbalance = "roundrobin")
private HelloService helloService;
4.3 LeastActive LoadBalance
最少活跃调用数策略
,相同活跃数的话会进行随机调用,活跃数指调用前后计数差,如果某个服务请求执行比较慢,那么这个服务接收到的请求会更少。
假如有3个服务端,A、B、C,他们正在处理的请求数分别为1、6、3,那么这时如果再来一个请求,那么理论上A会接收到这个请求。
那么,问题来了,我一个消费者是如何感知到服务端的正在请求的个数呢?
其实,消费端是不知道服务端正在请求的个数的。这个请求的计数是在消费端完成的,所以不同的消费端这个计数可能是不一样的。
服务端配置
@DubboService(loadbalance = "leastactive")
public class HelloServiceImpl implements HelloService {
}
消费端配置
@DubboReference(loadbalance = "leastactive")
private HelloService helloService;
4.4 ConsistentHash LoadBalance
一致性 Hash策略
,如果消费端的请求参数相同,那么之后他们都会调到同一个服务端。
在服务调用时,会把请求参数进行hash算法,然后根据服务端的实例个数计算出要调用哪个服务端,之后参数相同的请求都会调到这个服务端。
服务端配置
@DubboService(loadbalance = "consistenthash")
public class HelloServiceImpl implements HelloService {
}
消费端配置
@DubboReference(loadbalance = "consistenthash")
private HelloService helloService;
4.5 Shorttest response LoadBalance
最短响应时间策略
,顾名思义从多个服务提供者中选出执行成功并且响应时间最短的提供者。 服务端配置
@DubboService(loadbalance = "shortestresponse")
public class HelloServiceImpl implements HelloService {
}
消费端配置
@DubboReference(loadbalance = "shortestresponse")
private HelloService helloService;
5. 服务超时
消费者调用一个服务时,可以分为三步:
- 消费端发送调用请求
- 服务端执行请求
- 服务端返回响应数据
其中,第1、3两步涉及到网络传输,既然有网络,那么就可能存在网络延迟、也就存在执行超时。第2步,如果执行逻辑复杂、执行时间长,也可能导致服务超时。
服务端和消费端都可以配置服务超时时间。
如果服务端或消费端只有其中一个配置了超时时间timeout
,这时,如果消费者调用超过了这个时间没有收到响应,那么消费端就是抛出异常而服务端不能抛异常
。如果服务端执行超过这个时间只会打印一个超时日志,并不会抛异常,所以服务端的逻辑会正常处理完成。
如果服务端和消费端都配置了超时时间,那么会以消费端的超时时间为准。假如,
服务端的超时时间为2s,服务执行时间为3s,消费端的超时时间为1s,那么消费端会报超时异常,而服务端不会报异常。
服务端配置
@DubboService(timeout = 2000)
public class HelloServiceImpl implements HelloService {
}
消费端配置
@DubboReference(timeout = 3000)
private HelloService helloService;
除了在注解里指定版本外,还可以在配置文件里指定版本,例如:
dubbo.application.name=dubbo-consumer
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.provider.timeout=1000
dubbo.consumer.timeout=1000
6. 集群容错
集群容错是指消费者在调用服务时,如果服务有多个服务提供者,在经过负载均衡后选出其中一个服务提供者后进行调用,但这时如果调用报错,那么Dubbo会采取一定策略进行后续的处理。
Dubbo支持以下几种集群容错策略:Failover Cluster
、Failfast Cluster
、Failsafe Cluster
、Failback Cluster
、Forking Cluster
、Broadcast Cluster
、Available Cluster
、Mergeable Cluster
。
6.1 Failover Cluster
失败重试,当消费者调用失败时,会尝试调用其他提供者,可以配置retries
来控制重试次数,当retries
配置为0时,代表不进行重试。重试次数可以配置为应用级别、类级别、方法级别。对调用者屏蔽失败信息,会增加资源额外开销,适用于读操作、对响应时间不敏感的场景。Failover是dubbo的默认配置。
@DubboReference(cluster = "failover", retries = 0)
private HelloService helloService1;
@DubboReference(cluster = "failover",
methods = {@Method(name = "sayHello", retries = 0)})
private HelloService helloService2;
6.2 Failfast Cluster
快速失败,只发起一次调用,如果失败了就立即返回,不做其他事情了。适用于写操作、非幂等性的操作。注意:Failfast Cluster
没有重试次数的配置,就算配置了也不会生效。
@DubboReference(cluster = "failfast")
private HelloService helloService;
6.3 Failsafe Cluster
失败安全,当调用服务出现异常时,直接忽略,不做其他处理,失败了不会影响核心流程,但也屏蔽了失败信息。适用于写入审计日志、非核心流程等操作。
@DubboReference(cluster = "failsafe")
private HelloService helloService;
6.4 Failback Cluster
失败自动恢复,调用失败后,不会去重试其他的,而是后台记录一下失败的请求,然后进行定时重发,但也可能会引发任务堆积。适用于消息通知、对实时性要求不高的操作。
@DubboReference(cluster = "failback")
private HelloService helloService;
6.5 Forking Cluster
并行调用,调用时会同时调用多个服务端,并不是调用所有的服务端,其中只要有一个调用成功就直接返回代表调用成功,不会管其他的调用结果。可以通过forks
来配置最大并行的个数,默认为2次,当然这个值不能大于服务端的个数。适用于实时性要求较高的读操作。
@DubboReference(cluster = "forking", parameters = {"forks", "3"})
private HelloService helloService;
6.6 Broadcast Cluster
广播模式,会调用所有的服务端,只有所有的调用都成功时,才表示成功,如果有一台执行失败就表示执行失败。可以通过broadcast.fail.percent
来配置失败的比例,假如配置20代表当失败比例超过20%时调用失败。
@DubboReference(cluster = "broadcast",
parameters = {"broadcast.fail.percent", "20"})
private HelloService helloService;
6.7 Available Cluster
可用模式,调用服务列表可用的实例,只调用一个服务,如果所有服务都不可以,则调用失败。
@DubboReference(cluster = "available")
private HelloService helloService;
6.8 Mergeable Cluster
合并结果集,一般和group一起使用,调用配置的所有的提供者,将执行结果进行合并。
// 合并所有分组
@DubboReference(cluster = "mergeable", merger = "true", group = "*")
private HelloService helloService;
// 合并指定分组
@DubboReference(cluster = "mergeable", merger = "true", group = "a,b")
private HelloService helloService;
作者:Java星辰 链接:https://juejin.cn/post/7170690254834253831