超时处理
由于网络或服务端不可靠,会导致调用出现一种不确定的中间状态(超时)。为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间。
当消费者调用提供时,提供者执行的时间过长,会超时,消费者会认为服务调用失败,调用失败之后就会进行重试默认会重试二次。
在provider工程配置方法超时:
//@Service(timeout = 2000)//接口级别超时 2s
@Service(timeout=2000,methods = {
@Method(name = "addUser",timeout = 1000)//方法级别
})
public class UserServiceImpl implements UserService {
//...
}
提供者全局配置:
provider:
timeout: 4000
配置覆盖
一些配置可以在提供者和消费者中配置,在同一级别,消费者的优先级大于提供者。
覆盖:消费者方法>提供者方法>消费者接口级别>提供者接口级别>消费者全局级别>提供者全局。
方法级别配置优于接口级别,即小Scope优先。Consumer 端配置优于 Provider 配置、优于全局配置。
实际开发中一般配置提供者。
启动时检查
Dubbo缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check=“true”。
可以通过 check=“false” 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。
另外,如果你的 Spring 容器是懒加载的,或者通过 API 编程延迟引用服务,请关闭 check,否则服务临时不可用时,会抛出异常,拿到 null 引用,如果 check=“false”,总是会返回引用,当服务恢复时,能自动连上。
说白就是如果提供者没有上线,启动消费者默认会报错,关闭检查之后只要不使用远程引用户对象,就不会报错。
@RestController
@RequestMapping("/user")
public class UserController {
@Reference(interfaceClass = UserService.class,check = false)
private UserService userService;
}
全局配置
dubbo:
consumer:
check: false
默认情况下是启动时就要检查引用是否存在,那么就必须要求先启动提供者再启动消费者,如果关闭之后选择顺序就没有要求了。
重试原则
提供者配置:
//retries配置重试次数,-1表示不重试,非幂等操作:执行多次对数据有影响的操作 不能重试
@Service(timeout=2000,methods = {
@Method(name = "addUser",timeout = 1000,retries = -1)//方法级别
})
public class UserServiceImpl implements UserService {
//...
}
提供者全局配置:
dubbo:
provider:
timeout: 4000
retries: 2
消费者配置:
public class UserController {
@Reference(interfaceClass = UserService.class,check = false,retries = 2)
private UserService userService;
}
消费者全局配置:
dubbo:
consumer:
retries: 2
灰度发布
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:在低压力时间段,先升级一半提供者为新版本;再将所有消费者升级为新版本;然后将剩下的一半提供者升级为新版本。
//提供者配置
@Service(version = "1.0.0")
@Service(version = "2.0.0")
//消费者配置
@Reference(version = "*")
本地存根
远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub [1]
,然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。
本地存根是针对消费者的,在消费者创建StubUserServiceImpl。
public class StubUserServiceImpl implements UserService {
private UserService userService;
public StubUserServiceImpl(UserService userService) {
this.userService=userService;
}
@Override
public List<UserAddress> getUserAddressList(String userId) {
System.out.println("本地存根执行=---开始");
//做远程调用
List<UserAddress> addressList=null;
try {
addressList = userService.getUserAddressList(userId);
}catch (Exception e){
e.printStackTrace();
addressList= Arrays.asList(new UserAddress(1,"地址获取出错",""));
}
System.out.println("本地存根执行----结束");
return addressList;
}
}
消费者配置:
@Reference(stub="com.coydone.service.impl.StubUserServiceImpl")
ZK宕机与Dubbo直连
Zookeeper宕机
现象:Zookeeper注册中心宕机,还可以消费Dubbo暴露的服务。
原因:健壮性。
-
监控中心宕掉不影响使用,只是丢失部分采样数据。
-
数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务。
-
注册中心对等集群,任意一台宕掉后,将自动切换到另一台。
-
注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯。
-
服务提供者无状态,任意一台宕掉后,不影响使用。
-
服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复。
-
高可用:通过设计,减少系统不能提供服务的时间。
注册中心挂掉测试Dubbo直连
客户端绕过注册中心直接对接服务提供者
1、启动提供者
2、关闭Zookeeper
3、把消费者的启动检查关闭
4、配置消费者直连提供者,一定要关闭check。
@Reference(url = "127.0.0.1:20080",check = false)
5、删除消费者的Zookeeper配置,要不然启动会报错。
dubbo:
application:
name: user-consumer
#registry:
# address: zookeeper://127.0.0.1:2181
6、启动消费者访问,正常。
集群下的Dubbo负载均衡配置
我们至少需要3个provider提供者工程。这些provider对外提供的接口一样,所暴露的端口不一样。
操作:将provider工程复制两份,修改端口即可。
负载均衡的策略
Random LoadBalance
随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
RoundRobin LoadBalance
轮循,按公约后的权重设置轮循比率。
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
ConsistentHash LoadBalance
一致性 Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。算法参见:http://en.wikipedia.org/wiki/Consistent_hashing。
缺省只对第一个参数 Hash,如果要修改,请配置 <dubbo:parameter key="hash.arguments" value="0,1" />
。
缺省用 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key="hash.nodes" value="320" />
。
操作方式
1、可以通过Dubbo Admin的管理页面来操作负载均衡配置。
2、在provider工程中使用注解配置。
@Service(weight = 100,loadbalance = "random")
//可以配置:random/roundrobin/leastactive/consistenthash
LoadBalance接口说明:
package org.apache.dubbo.rpc.cluster;
@SPI("random")
public interface LoadBalance {
@Adaptive({"loadbalance"})
<T> Invoker<T> select(List<Invoker<T>> var1, URL var2, Invocation var3) throws RpcException;
}
它的实现类有:
public class RandomLoadBalance extends AbstractLoadBalance {
public static final String NAME = "random";
public RandomLoadBalance() {
}
}
评论区