高可用-zookeeper宕机与dubbo直连
注册中心宕机,还可以消费dubbo暴露的服务
- 监控中心宕机了,不影响使用,只会丢失部分数据的采集
- 数据库宕机了,zookeeper仍然可以通过缓存查询服务提供者列表,但是不能注册新服务
- 注册中心集群对等集群,任何一台挂掉后都会切换到另一台
- 注册中心全部宕机,仍可一通过使用本地缓存进行通信
- 服务提供者提供无状态的服务,任意一台宕机,都不影响使用
- 服务提供者全部宕机后,服务消费者无法正常使用,无限次重连等待服务提供者的恢复
通过dubbo直连的方式
绕开注册中心直连服务提供者
@Reference(url="127.0.0.1:20888") // dubbo的注解
UserService userService;
public List<UserAddress> initOrder(String userId) {
return userService.getUserAddressList("1");
}
集群负载均衡
同一种服务提供者存在多份时需要负载均衡策略, loadbalance
Random LoadBalance
随机负载均衡, 按照权重设置随机概率,随机调用量越大,分布越均匀,有利于动态调整提供者的权重
例: UserService一共三台,我们根据实际的机器性能给这三个机器添加不同的权重
userService1 weight=100
userService2 weight=200
userService3 weight=300
这样这三台机器被随机调用到的比例就是1:2:3
这是Dubbo默认的负载均衡机制
@SPI("random")
public interface LoadBalance {
@Adaptive({"loadbalance"})
<T> Invoker<T> select(List<Invoker<T>> var1, URL var2, Invocation var3) throws RpcException;
}
RoundRobin LoadBalace
假如同样存在三台相同的服务提供者, 不设置权重的话,消费者的会被均匀的按瞬间分发到这个三台机器上123123123...
轮询, 按照公约后的权重值,设置查询比率 , 存在慢的提供者请求累积的问题, 这种方式的访问顺序也是提前就知道的,只不过添加上了权重的之后的顺序, 原来的123123... 可能变成了 123333
LeastActive LoadBalance
最少活跃调用数, 活跃数指的是调用前后的计时差,使慢的提供者接受更少的请求
当用户的请求会先查询服务提供者列表中,然后选择活跃数最低的,也就是上次响应时间最短的机器
ConsistentHash LoadBalance
一致性Hash, 使相同参数的请求总是发到同一个提供者上,当某一台提供者挂掉时,原本该发送到这个服务提供者的请求会平摊到其他提供者身上,不会产生巨大的动荡
- 缺省只对第一个参数 Hash,如果要修改,请配置
<dubbo:parameter key="hash.arguments" value="0,1" />
- 缺省用 160 份虚拟节点,如果要修改,请配置
<dubbo:parameter key="hash.nodes" value="320" />
负载均衡的修改方式
- 配置文件版
服务端服务级别
<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>
- 注解版
@Reference(loadbalance = "random")
@Reference(loadbalance = "roundrobin")
@Reference(loadbalance = "leastactive")
@Reference(loadbalance = "consistenthash")
权重的添加
- 硬编码
在暴露服务是,在@Service注解上边添加 weight属性 @Service(weight="200")
- 通过控制台,手动设置服务提供者的权重
服务降级
什么是服务降级?
在服务器压力剧增的情况下,根据实际的业务情况及流量,对一些服务和页面进行有策略的不处理或者换种简单方式处理的方式, 从而达到释放系统资源,维持系统核心功能的正常运作
实现: 向注册中心写入动态配置规则
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"));
- 实现方式1
上面配置的最后一条URL上,mock=force:return+null
,表示在服务消费者层面直接对用户的调用返回空,不行转发处理
- 实现方式2
最后的url位置还可以写成mock=fail:return+null
,表示消费者在对服务的调用方法失败后才返回null,用来缓冲服务调用不稳定时,对服务调用方的影响
``
如图在控制台进行可视化界面消费者模块实现服务降级
集群容错
服务在调用失败时,Dubbo提供了很多种容错方案
Failover Cluster(默认)
failover cluster 失败自动切换,当出现失败,重试其它服务器,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。
重试次数配置如下:
<dubbo:service retries="2" />
或
<dubbo:reference retries="2" />
或
<dubbo:reference>
<dubbo:method name="findFoo" retries="2" />
</dubbo:reference>
Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。
Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错 [2]。通常用于通知所有提供者更新缓存或日志等本地资源信息。
集群模式配置
按照以下示例在服务提供方和消费方配置集群模式
<dubbo:service cluster="failsafe" />
或
<dubbo:reference cluster="failsafe" />
整合 hystrix 做服务容错
hystrix 旨在通过控制远程系统,服务和第三方的节点,从而对延迟和故障提供更强大的容错能力,Hystrix同时具备回退机制和断路器能力功能的线程和信号隔离,请求缓存,请求打包已经监控和配置的功能
- 断路器可以实现快速失败,当它在一段时间内检测到许多类似的错误(例如超时),就会在之后的一段时间内,强迫对该服务的调用快速失败,即不再请求所依赖的服务。这样,应用程序就不须浪费CPU时间去等待长时间的超时。
- 断路器也可以自动诊断依赖的服务是否已经恢复正常。
hytrix的隔离策略有两种:
- THREAD 线程隔离,使用这种方法HystrixCommand将会在单独的线程上执行,并发请求收到线程数量的影响
- SEMAPHORE 信号量隔离, 使用这种方法,HystrixComand将会在调用线程上执行,并发请求受到信号量的个数限制
Hystrix默认的保护级别是THREAD,它出来超时保护还有额外的保护,一般当系统的负载特别大,每秒几百并发时,才选择信号量隔离,正常情况下使用默认的隔离级别
可以使用execution.isolation.strategy属性指定隔离策略。
@HystrixCommand(fallbackMethod = "notfindback", commandProperties=@HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE") )
public User findById( Long id)
public User notfindback(Long id)
{
User user = new User();
user.setId(0L);
return user;
}
编码实现:
- 配置,服务提供者和服务消费者,双方都添加Spring-cloud-starter-hystrix
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
- 服务提供者和消费者同时在启动类上添加开启此项功能
@EnableDubbo
@EnableHystrix
@SpringBootApplication
public class ProviderApp {
public static void main(String[] args) {
SpringApplication.run(ProviderApp.class);
}
}
- 对服务提供者对外暴露的接口实现进行改写
@Component
@Service //使用dubbo的Service 对外保留服务
public class UserServiceImpl implements UserService {
// 添加这个注解,将当期方法,交给Hystrix进行代理,当出现异常时,进行容错处理
@HystrixCommand
public List<UserAddress> getUserAddressList(String userId) {
UserAddress a1= new UserAddress(1,"张三","北京市朝阳区");
UserAddress a2= new UserAddress(2,"李四","山东济南");
if (Math.random()>0.5){
throw new RuntimeException();
}
return Arrays.asList(a1,a2);
}
}
- 改写服务消费者远程过程调用的方法
public class UserServiceImpl implements UserService{
// 注入这个被服务提供者支持的接口
@Reference
private final UserService userService;
// 提供构造函数
public UserServiceImpl(UserService userService) {
this.userService = userService;
}
@Override
@HystrixCommand(fallbackMethod = "correct") // 当出现错误时,回调correct方法
public List<UserAddress> getUserAddressList(String userId) {
// 判空
if (!StringUtils.isEmpty(userId)){
System.err.println();
return userService.getUserAddressList(userId);
}
return null;
}
public List<UserAddress> correct(String userId) {
return Collections.singletonList(new UserAddress(9, "correct", "correct"));
}
}