5 本地存根调用
- 实现流程
把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。
- 客户端存根实现:
增加service接口
public class OrderServiceStub implements OrderService { private final OrderService orderService; // 构造函数传入真正的远程代理对象 public OrderServiceStub(OrderService orderService){ this.orderService = orderService; } /** * 获取订单详情 * @param orderId * @return */ public String getOrder(Long orderId){ // 在客户端执行, 预先验证参数是否合法,等等 try { if(null != orderId) { return orderService.getOrder(orderId); } return "参数校验错误!"; } catch (Exception e) { //容错处理 return "出现错误:" + e.getMessage(); } } }
- 修改客户端调用配置
/** * 订单服务接口 */ @DubboReference(version = "${dubbo.spring.provider.version}",retries = 3, stub = "com.itheima.dubbo.spring.consumer.service.OrderServiceStub") private OrderService orderService;
stub要配置存根接口的完整路径。
- 测试调用
访问不带参数的地址: http://127.0.0.1:18084/order/getOrder
会进入存根接口的处理逻辑, 提示校验异常。
6 负载均衡机制
- 默认负载策略
Dubbo默认采用的是随机负载策略。
开启三个服务节点,通过消费端访问验证: http://127.0.0.1:18084/order/getOrder?orderId=123
通过控制后台日志输出, 可以看到每个服务节点呈现不规则的调用。
Dubbo 支持的负载均衡策略,可用参看源码:AbstractLoadBalance
- Random LoadBalance:默认
随机,按权重设置随机概率。 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
RoundRobin LoadBalance
加权轮询负载均衡,按公约后的权重设置轮询比率。 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。 活跃数其实就是在当前这个服务调用者中当前这个时刻 某个invoker(某个服务提供者的某个接口)某个方法的调用并发数,在调用之前+1 调用之后-1的一个计数器,如果出现多个活跃数相等invoker的时候使用随机算法来选取一个
- ConsistentHash LoadBalance
一致性 Hash,相同参数的请求总是发到同一提供者。 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
一致性Hash负载均衡涉及到两个主要的配置参数为hash.arguments 与hash.nodes。
hash.arguments : 当进行调用时候根据调用方法的哪几个参数生成key,并根据key来通过一致性hash算法来选择调用结点
hash.nodes: 为结点的副本数
ShortestResponseLoadBalance
2.7.7 +新增
最短响应时间负载均衡
从多个服务提供者中选择出调用成功的且响应时间最短的服务提供者,由于满足这样条件的服务提供者有可能有多个。所以当选择出多个服务提供者后要根据他们的权重做分析,如果权重一样,则随机
源码实现: org.apache.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance
- Dubbo提供了四种实现:
- 四种配置方式:优先级从下至上:
- 服务端的服务级别:
<dubbo:service interface="..." loadbalance="roundrobin" />
- 客户端的服务级别:
<dubbo:reference interface="..." loadbalance="roundrobin" />
注意:服务提供者配置后最终也只是同步到消费者端。故一般在消费端配置
- 服务端方法级别:
<dubbo:service interface="..."> <dubbo:method name="..." loadbalance="roundrobin"/> </dubbo:service>
- 客户端方法级别:
<dubbo:reference interface="..."> <dubbo:method name="..." loadbalance="roundrobin"/> </dubbo:reference>
当然这里还有全局配置(略),
- 调用验证
修改客户端的负载策略, 改为轮询策略:
注意很多配置都有三个级别,针对方法的,针对接口的,全局的。
@DubboReference(version = "${dubbo.spring.provider.version}",retries = 3, loadbalance = "roundrobin", stub = "com.itheima.dubbo.spring.consumer.service.OrderServiceStub")
开启三个服务节点, 进行访问验证: http://127.0.0.1:18084/order/getOrder?orderId=123
会依次轮询进行调用。
注意:测试时将服务提供者的版本号都调成一致(1.0.0),超时配置注释掉!
- 动态权重调整验证
管理后台地址: http://127.0.0.1:8080/#
通过管理后台修改服务的权重配置:
将两台节点的权重降低至20:
调整后可以看到权重配置已经生效:
通过消费者接口访问, 会发现第一台权重较大的节点, 访问次数会明显增多。
7 服务降级运用
- 服务动态禁用
启动单个服务节点,进入Dubbo Admin, 创建动态配置规则:
configVersion: v2.7 enabled: true configs: - side: provider addresses: - '0.0.0.0:20880' parameters: timeout: 3000 disabled: true
将disabled属性设为true, 服务禁用, 可以错误提示:
将disabled属性改为false,
configVersion: v2.7 enabled: true configs: - side: provider addresses: - '0.0.0.0:20880' parameters: timeout: 3000 disabled: false
恢复正常访问:
- 服务降级
- 配置规则
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension(); Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181")); registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));
> - `mock=force:return+null` 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。 > - 还可以改为 `mock=fail:return+null`表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
- 降级测试(force方式)
进入Dubbo Admin进行配置:
configVersion: v2.7 enabled: true configs: - side: consumer addresses: - 0.0.0.0 parameters: timeout: 3000 mock: 'force:return null'
客户端调用, 会直接屏蔽, 并且服务端控制台不会有任何调用记录: ![image-20210121165217882](D:\18、狂野架构师\狂野架构师资料\狂野架构 - 课件资料\01-02阶段课程资料\dubbo源码剖析\讲义\images\image-20210121165217882.png)
- 降级测试(fail方式)
进入Dubbo Admin配置:
configVersion: v2.7 enabled: true configs: - side: consumer addresses: - 0.0.0.0 parameters: timeout: 1000 mock: 'fail:return null'
这里为了触发调用异常, 超时时间缩短为1秒。 注意这里的超时时间可能不会起作用,最终的超时时间还是得看项目中配置,故在服务提供方将线程休眠时间延长,造成调用超时。 客户端调用, 会出现降级显示为空: ![image-20210121165544731](D:\18、狂野架构师\狂野架构师资料\狂野架构 - 课件资料\01-02阶段课程资料\dubbo源码剖析\讲义\images\image-20210121165544731.png) 同时服务端会有调用记录显示(请求会进入服务端,但由于超时, 调用是失败): ![image-20210121165617267](D:\18、狂野架构师\狂野架构师资料\狂野架构 - 课件资料\01-02阶段课程资料\dubbo源码剖析\讲义\images\image-20210121165617267.png)
8 并发与连接控制
实际运用, 会碰到高并发与峰值场景, Dubbo是可以做到并发与连接数控制。
可使用jmeter进行测试!
并发数控制
- 服务端控制
- 服务级别
<dubbo:service interface="com.foo.BarService" executes="10" />
服务器端并发执行(或占用线程池线程数)不能超过 10 个。
- 方法级别
<dubbo:service interface="com.foo.BarService"> <dubbo:method name="sayHello" executes="3" /> </dubbo:service>
限制具体的方法,服务器端并发执行(或占用线程池线程数)不能超过3 个。
- 客户端控制
- 调用的服务控制
<dubbo:reference interface="com.foo.BarService" actives="10" />
每客户端并发执行(或占用连接的请求数)不能超过 10 个。
- 调用的服务方法控制
<dubbo:reference interface="com.foo.BarService"> <dubbo:method name="sayHello" actives="10" /> </dubbo:service>
[dubbo:reference](dubbo:reference)比[dubbo:service](dubbo:service)优先。
- 客户端负载配置
<dubbo:reference interface="com.foo.BarService"loadbalance="leastactive" />
负载策略为最小连接数时, Loadbalance 会调用并发数最小的 Provider。
连接数控制
- 服务端连接控制
<dubbo:provider protocol="dubbo" accepts="10" />
限制服务器端接受的连接不能超过 10 个
- 客户端连接控制
<dubbo:reference interface="com.foo.BarService" connections="10" />
限制客户端服务使用连接不能超过 10 个
如果 dubbo:service 和 dubbo:reference 都配了 connections,dubbo:reference 优先。