概述
Hystrix前置
- 分布式系统面临的问题:在复杂的分布式体系结构中,可能有十几个依赖关系,每个依赖关系在某些时候将不可避免的会失败,导致服务雪崩;
- 雪崩效应:多个微服务之间调用,微服务A调用B和C,B和C又调用其他微服务(称为“扇出”),如果扇出的链路某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃。
- 具体来说,对于高流量的应用来说,单一的后端依赖可能会导致所有的服务器上的所有资源在几秒钟就饱和。更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
- 通常一个模块下的某个实例失败后,这时候这个模块依然会接收流量,然后这个有问题的模块还调用其他的模块,这就发生了级联故障。
- Hystrix是一个用于处理分布式系统的延迟和容错的开源库。Hystrix能保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
- “断路器”作为一种开关装置,当某个服务单元发生故障后,通过断路器的故障监控,向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
- Hystrix能做的事:①服务降级;②服务熔断;③接近实时的监控;
- Hystrix停更,进入维护阶段;
- 涉及cloud-provider-hystrix-payment8001;cloud-consumer-feign-hystrix-order80
- Hystrix一般用在80消费端;
版本说明
- cloud Hoxton.SR1
- boot 2.2.2RELEASE
- cloud alibaba 2.1.0 RELEASE
- java java8
- Maven 3.5以上
- Mysql 5.7以上
Hystrix
- 先构建服务提供者cloud-provider-hystrix-payment8001:
- 创建、POM、YML、主启动类、业务类(PaymentService和PaymentController)、测试(启动eureka,启动该提供者、访问);
- 高并发测试:通过Jmeter实现高并发。
- 用JMETER创建线程组202002,然后添加HTTP请求,服务器为localhost,端口号8001,请求路径http://localhost:8001/payment/hystrix/timeout/1,测试(http://localhost:8001/payment/hystrix/ok/1)也会变慢。
- 这是因为timeout和ok是在一个微服务中的,微服务集中资源处理timeout的高并发请求,因此ok也会出现等待、卡顿、延时的现象(也可以解释为,tomcat的默认工作线程数被打满了,没有多余的线程来分解压力和处理)。
- 继续恶化高并发:新建消费者80,继续访问上述路径,观看情况:
- 新建cloud-consumer-feign-hystrix-order80;
- 新建、POM、YML、主启动类、业务类、正常测试、高并发测试;
- 结果是80此时调用8001,客户端访问响应缓慢,转圈圈;
- 如何解决:
- 超时导致服务器变慢(转圈),超时不再等待;
- 出错(宕机或者程序运行出错),出错要有兜底;
- 解决:
- 对方服务(8001)超时,调用者(80)不能一致卡死等待,必须有服务降级;
- 对方服务(8001)宕机,调用者(80)不能一直卡死等待,必须有服务降级;
- 对方服务(8001)ok,的调用者(80)自己有故障,或自我要求,必须服务降级;
服务降级
-
服务降级的注解:@HystrixCommand;
-
第一步,8001先从自身找问题,设置自身调用超时时间的峰值,峰值内可以正常运行,超时了要有兜底的方法处理,作服务降级fallback;
-
在cloud-provider-hystrix-payment8001中的业务层设置兜底方法,以及设置超时时间的峰值(这里设置的异常,可以是超时异常,也可以是程序异常),它都会执行兜底的方法:
//fallbackMethod,即兜底的方法,这里Hystrix是用单独线程池做处理,起到隔离的效果 @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000") //上面这句表示,在3秒中内都是正常的,即设置自身调用超时时间的峰值,峰值内可以正常运行 }) public String paymentInfo_TimeOut(Integer id){ try{ TimeUnit.SECONDS.sleep(5); }catch (InterruptedException e){ e.printStackTrace(); } //int age = 10/0; return "线程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOut, id:"+id+"t"+"timeout对对对对对,耗时5秒中"; } //即为兜底的方法 public String paymentInfo_TimeOutHandler(Integer id){ return "线程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOutHandler,id:" +id+"t"+"timeout,我来兜底错错错错错错错错错"; }
-
在主启动类中激活这个类,添加新注解@EnableCircuitBreaker;
-
-
第二步,80消费者(cloud-consumer-feign-hystrix-order80)调用微服务,也可以通过类似的方法进行客户端降级保护:
-
在主启动类添加@EnableHystrix注解,激活Hystrix
-
编写业务类,这里为了方便省事,直接跳过service的实现类,全写在控制层:
@GetMapping("/consumer/payment/hystrix/timeout/{id}") @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = { //设置超时峰值为1.5秒,但是现在服务提供者的服务时间是2s,会进入兜底方法 //但是注意:默认Feign客户端只等待1秒钟。 @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000") }) public String paymentInfo_TimeOut(@PathVariable("id") Integer id){ //int age = 10/0; String result = paymentHystrixService.paymentInfo_TimeOut(id); return result+"cusumer timeoutttttttttttttttttttttttttttttt"; } public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){ return "我是消费者80,对方支付系统繁忙,请10秒钟后再试,或者自己运行出错请检查自己!!!"; }
-
服务降级目前面临的问题
-
每个业务方法对应一个兜底的方法,导致代码膨胀冗余,同时还使得兜底的方法和业务逻辑的方法混在一起,现得混乱。同时对于这个问题,我们还希望将统一兜底方法的和自定义方法的分开。
-
注解@DefaultProperties(defaultFallback=""),配置在类名之上,表示普通的方法出问题了,可以跳到统一的兜底方法去。具体步骤如下:
- 第一步:在要配置全局兜底方法的类名上,添加注解@DefaultProperties(defaultFallback=“全局兜底方法名”);
- 第二步:在类中,添加对应全局兜底方法;
- 第三步:在需要调用全局兜底方法的其他方法上,添加@HystrixCommand即可;
-
为了解决代码混乱的问题(业务代码和兜底方法混在一起),可以按下面步骤解决(假设面临的问题是,客户端去调用服务端,碰上服务端宕机或者关闭):
-
注意:该案例的服务降级处理是在客户端80实现完成的,与服务端8001没有关系,只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦;另外,说明一下,未来我们常见的异常有,运行异常、超时异常、和宕机异常;
-
第一步:根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新建立一个类(PaymentFallbackService)实现该接口,统一为接口里的方法进行异常处理;这样一来,如果PaymentHystrixService接口,作为Feign客户端调用远程微服务,成功了就成功了,失败了则调用实现类(PaymentFallService)中的方法。
@Component public class PaymentFallbackService implements PaymentHystrixService { @Override public String paymentInfo_OK(Integer id) { return "------PaymentFallbackService fall back-paymentInfo_OK----大家好我是备胎"; } @Override public String paymentInfo_TimeOut(Integer id) { return "------PaymentFallbackService fall back-paymentInfo_TimeOut----大家好我是备胎"; } }
-
第二步:在PaymentHystrixService接口上,添加如下注解,确保该接口调用出问题时,能够找到我们这个兜底备胎的实现类。
@FeignClient(value="CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
-
第三步:在yml配置文件中添加如下配置:
feign: hystrix: enabled: true
-
第四步:一定记得在启动类上添加@EnableHystrix,以激活Hystrix;
-
从上面这几步可以看出,这样的方式,实际上是Feign整合了Hystrix后,提供的方式。
-
测试:访问http://localhost/consumer/payment/hystrix/ok/1,然后如果关闭8001服务提供者,再访问,是否能够成功找到兜底方法。
-
-
服务熔断(CircuitBreaker)
- 熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用,或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
-
在Spring Cloud框架中,熔断机制通过Hystrix实现,Hystrix会监控微服务间的调用情况,当失败的调用到一定阈值(默认为5秒20次调用失败),就会启动熔断机制。熔断机制的注解也是@HystrixCommand。
-
实操,在cloud-provider-hystrix-payment8001下进行修改:
-
在业务层添加如下代码:
//========服务熔断 @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = { @HystrixProperty(name = "circuitBreaker.enabled",value="true"),//是否开启断路器 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),//请求次数 @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value="10000"),//休眠窗口期 @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value="60")//失败率达到多少后跳闸,即十次请求中,有6次失败以上 }) public String paymentCircuitBreaker(@PathVariable("id") Integer id){ if(id<0){ throw new RuntimeException("*****id 不能为负数"); } String serialNumber = IdUtil.simpleUUID(); //这是Hutool工具类的唯一标识码 return Thread.currentThread().getName()+"t"+"调用成功,流水号:"+serialNumber; } public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){ return "id 不能为负数,请稍后再试,哭哭, id: "+ id; }
-
在控制层添加如下代码:
//=========服务熔断 @GetMapping("/payment/circuit/{id}") public String paymentCircuitBreaker(@PathVariable("id") Integer id){ String result = paymentService.paymentCircuitBreaker(id); log.info("*******result:"+result); return result; }
-
测试,连续十次访问http://localhost:8001/payment/circuit/-1 后,会熔断,再访问http://localhost:8001/payment/circuit/1 会失败,再过一会儿访问才会恢复。
-
-
总结:
- 熔断打开:请求不再进行调用当前服务,内部设置始终一般为MTTR(平均故障处理时间),当打开时长达到所设时钟,则进入半熔断状态;
- 熔断关闭:熔断关闭不会对服务进行熔断;
- 熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则,则认为当前服务恢复正常,关闭熔断;
服务监控hystrixDashboard(仪表盘)
-
Hystrix提供了准实时的调用监控程序(Hystrix Dashboard),以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功多少失败等。Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。
-
仪表盘9001,cloud-consumer-hystrix-dashboard9001。地址为localhost:9001/hystrix
-
步骤:
-
新建、POM、YML、主启动类HystrixDashboardMain9001+新注解@EnableHystrixDashboard;
-
所有Provider微服务提供类(8001/8002/8003)都需要监控依赖配置
-
启动9001,该微服务后续将监控微服务8001
-
作为被监控的微服务(cloud-provider-hystrix-payment8001),必须在POM中引入spring-boot-starter-actuator坐标。
-
还要再被监控的微服务的主启动类中添加如下内容:
/** * 此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑。 * ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream", * 所以只要在自己的项目里配置上下面的servlet就可以了。 */ @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,填写监控地址为:http://localhost:8001/hystrix.stream ,测试地址为
- http://localhost:8001/payment/circuit/1
- http://localhost:8001/payment/circuit/-1
-
各种标签的含义:
- 七色(豪猪网站上方有标明)、实心圈(颜色代表健康实例、大小代表请求流量发生的变化)、曲线(流量的上升和下降趋势)
-
需熟练掌握
知识点
-
什么是服务降级(fallback)?
- 当某个服务单元发生故障之后,通过断路器的故障监控,向调用方返回一个符合预期的、可处理的备选响应(FallBack),例如不让客户端等待并立刻返回一个友好提示。
- 哪些情况会发出服务降级:程序运行异常、超时、服务熔断出发服务降级、线程池/信号量打满也会导致服务降级;
-
什么是服务熔断(break)?
- 类似保险丝,在达到最大服务访问后,直接拒绝访问,拉闸断电,然后调用服务降级的方法,并返回友好提示;
- 服务的降级->进而熔断->恢复调用链路;
-
什么是服务限流(flowlimit)?
- 秒杀等高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行;
-
服务断路器开合状态的动态过程?
- step1:当满足一定的阈值的时候(默认10秒内超过20个请求次数);
- step2:当失败率达到一定的时候(默认10秒内超过50%的请求失败);
- step3:到达以上阈值,断路器将开启;
- step4:当开启的时候,所有请求都不会进行转发;
- step5:一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4,5。
-
涉及到断路器的三个重要参数:快照时间窗、请求总数阈值、错误百分比阈值。
- 快照时间窗:断路器确定是否打开,以统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒。
- 请求总数阈值:在快照时间窗内,必须满足请求总数阈值,才有资格熔断。默认为20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即时所有的请求都超时或其他原因失败,断路器都不会打开。
- 错误百分比阈值:当请求总数在快照时间窗内超过了总数阈值,比如发生了20次调用,如果在这20次调用中,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阈值的情况下,这时候就会将断路器打开。
-
断路器打开之后,会发生什么?
- 断路器打开之后,再有请求调用,将不会调用主逻辑,而是直接调用降级fallback,通过断路器,实现了自动地发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果。
-
原来的主逻辑要如何恢复?
- Hystrix为我们实现了自动恢复功能。当断路器打开,对主逻辑进行熔断之后,hystrix会启动一个休眠时间窗,在这个时间窗内,降级逻辑将临时成为主逻辑,当休眠时间窗到期后,断路器进入半开状态,释放一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。
tips
- Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试。它可以用于测试静态和动态资源,例如静态文件、Java 小服务程序、CGI 脚本、Java 对象、数据库、FTP 服务器, 等等。JMeter 可以用于对服务器、网络或对象模拟巨大的负载,来自不同压力类别下测试它们的强度和分析整体性能。另外,JMeter能够对应用程序做功能/回归测试,通过创建带有断言的脚本来验证你的程序返回了你期望的结果。
- 一般来说,服务降级的fallback是放在客户端,案例中是为了展示,Hystrix作用在服务端和客户端都可以,但是一般是放在客户端。
- 热部署方式,对java代码的改动会有作用,但是对@HystrixCommand内属性的修改,建议重启微服务;
- 默认Feign客户端只等待1秒钟。详情见上一章。
- hutool了解一下:一个Java基础工具类,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具类。
代码地址:https://github.com/AJ-Spade/cloud2020/tree/master
最后
以上就是呆萌马里奥为你收集整理的2020SpringCloud学习年终总结——第八章-HystrixHystrix前置版本说明Hystrix需熟练掌握知识点tips的全部内容,希望文章能够帮你解决2020SpringCloud学习年终总结——第八章-HystrixHystrix前置版本说明Hystrix需熟练掌握知识点tips所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复