目录
分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免的失败;
服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它微服务,这就是所谓的“扇出”,如果扇出的的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,从而引起系统崩溃,所谓的“雪崩效应”;
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器的所有资源都在几秒中内饱和,导致服务之间延迟增加,系统资源紧张,导致整个系统发生更多的级联故障,这些都需要进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用或系统
所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接受流量,然后这个模块还调用了其他模块,会发生级联故障,或者叫雪崩
Hystrix断路器
什么是Hystrix
Hytrix是一个用于处理分布式系统延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性
“断路器”本身是一种开关装置,当某个单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方法返回一个符合的、可预期的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间的、不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩;
Hystrix重要概念
服务降级(fallback)
服务器忙,请稍后再试,不让客户端等待并立即返回一个友好提示,fallback
出现服务降级的情况:
程序运行异常、超时、服务熔断触发服务降级、线程池/信号量打满也会导致服务降级
服务熔断(break)
类似保险丝达到最大访问量之后,直接拒绝访问,然后调用服务降级的方法并返回友好提示
服务的降级->进而熔断->恢复调用线路
服务限流(flowlimit)
秒杀高并发的情况下,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
Hystrix案例
- 构建项目Hystrix项目
pom文件主要引入依赖
<!-- hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<!-- <version>2.2.1.RELEASE</version>-->
</dependency>
<!-- eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
降级容错解决的主要维度
对方8001超时,调用者8080不能一直卡死等待,必须有服务降级
对方服务8001down机了,调用者8080不能一直卡死等待,必须有服务降级
对方服务8001OK,调用者8080自己出故障或有自我要求(自己等待的时间小于服务提供者,自己降级处理)
服务降级
降级配置:@HystrixCommand
8001先从自身找问题:设置自身调用超时间的峰值,峰值内可以正常运行,超过了需要兜底的方法处理,作服务降级fallback
8001fallback:@HystrixCommand:一旦调用服务服务方法失败并抛出了错误信息后,会自动调用@HystixCommand标注好的fallbackMethid调用类中缀的指定方法
主启动类激活:添加新注解@EnableCircuitBreaker
在8001的Service中进行以下业务逻辑的编写,当发生服务降级的时候会运行指定的方法
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value="3000")
})
public String paymentInfo_TimeOut(Integer id){
int timeNumber=5;
try {
TimeUnit.SECONDS.sleep(timeNumber);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "线程池"+Thread.currentThread().getName()+"paymentInfo_TimeOut,id:"+id+"\t"+"O(∩_∩)O哈哈~"+"耗时"+timeNumber+"钟";
}
public String paymentInfo_TimeOutHandler(Integer id){
return "线程池"+Thread.currentThread().getName()+"paymentInfo_TimeOutHandler,id:"+id+"\t"+"o(╥﹏╥)o";
}
内部异常和运行超时都能进行服务降级,都能运行兜底方案
80fallback:
80订单微服务,也可以更好保护自己,自己也依样画葫芦进行客户端降级保护
使用Hytrix进行服务降级即可以放在服务端,也可以放在客户端
首先需要在客户端的配置文件application.yml文件中开启hytrix服务降级
feign:
hystrix:
enabled: true
同样的需要在主启动类上加入开启Hytrix注解@EnableHystrix
业务类
全局服务降级@DefaultProperties
使用一个统一降级处理方法解决代码重复的问题
@DefaultProperties(defaultFallback=“”)
1:1每个方法配置一个服务降级方法,技术上刻印机,但实际上提高了代码的冗余
1:N 除了个别重要业务有专属,其它普通的可以通过该注解统一跳转到统一处理结果页面
通用的和独享的各自分开,避免了代码的膨胀,合理减少了代码量
- 首先在所在中创建全局异常处理方法。例如:
//下面是全局fallback方法
public String payment_Global_FallbackMethod(){
return "Global异常处理信息,请稍后再试---";
}
- 在所需要进行服务降级的方法上加入@HystrixCommand注解,取代之前的注解中的参数
- 在方法所在类的上方使用@DefaultProperties注解指明兜底的方法,例如
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
通配服务降级FeignFallback
本次案例服务降级处理是在客户端8080实现的,与服务端8001没有任何关系,只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦
根据8080已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现该接口,统一为接口里面的方法进行异常处理
这样,当服务端宕机的时候,会默认使用客户端的异常处理
熔断机制
熔断机制是应对雪崩效应的一种微服务链路保护机制,当扇出链路的某个微服务出错不可用或者响应时间太长的时候,会进行服务的降级,进而熔断节点微服务的调用,快速返回错误的响应 信息
当检测到该节点微服务调用响应正常后,恢复调用链路
在Springcloud框架里,熔断机制通过Hytrix实现。Hytrix会监控微服务调用的状况。当失败的调用到一定的阈值,缺省是5s内20此调用失败,就会启动熔断机制,通断机制的注解是@HystrixCommand,
案例操作:
修改8001服务端:
在8001的Service层中加入以下代码:
//---------------------------------以下为服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuiBreaker_fallback",commandProperties = {
//在10s内10此次请求有百分之60无法成功就跳闸
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),//是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value="10"),//请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowINMillseconds",value="10000"),//时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value ="60" )//失败率达到多少以后跳闸
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
if (id<0){
throw new RuntimeException("#####id 不能为负数");
}
String seriaNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"调用成功"+"流水号"+seriaNumber;
}
public String paymentCircuiBreaker_fallback(@PathVariable("id") Integer id){
return "id不能为负数,请稍后再试 ID:"+id;
}
}
创建其对应的controller进行测试
服务熔断总结:
熔断打开:请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态
熔断关闭:熔断关闭不会对服务进行熔断
熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断
涉及到断路器的重要三个参数:快照时间窗、请求阈值数,错误百分比阈值
服务监控HystrixDashboard
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(HystrixDashboard),Hystrix会持续的记录所有通过Hystrix发起请求的执行信息,并以统计报表的形式展示给用户,包括每秒执行多少请求成功,多少失败等,Netflix通过hystrix-metrics-event-strean项目实现了对以上指标的监控。Spring cloud也提供了对Hystrix DashBoard的整合,对监控内容转化成可视化界面
构建步骤:
新建9001Dashboard仪表盘项目
pom文件主要依赖:
<!-- dashboard仪表盘图形化操作界面-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
新建主启动类,注意需要添加@EnableHystrixDashboard注解
注意所以Provider微服务提供类8001/8002等都需要监控依赖配置
<!--监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
测试:访问http://localhost:9001/hystrix地址出现监控界面即代表搭建完成
注意:新版本Hystrix需要在主启动类MainAppHystrix8001中指定监控路径,加入以下代码:
@Bean
public ServletRegistrationBean getservlet(){
HystrixMetricsStreamServlet streamServlet =new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean=new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
运行9001,8001,7001后进行测试
那个圈圈大,哪个流量就大
曲线:用来记录2分钟内流量的变化,可以通过它来观察流量的上升和下降趋势