0.1分布式系统面临的问题
复杂的分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。
服务雪崩:多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩。
0.2Hystrix
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
"断路器"本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控,向调用方返回一个符合预期的、可处理的备选响应(fallback),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延。乃至雪崩。
主要功能:
服务降级
服务熔断
服务限流
服务监控
官网:https://github.com/Netflix/Hystrix/wiki/How-To-Use
Hystrix工作流程:https://github.com/Netflix/Hystrix/wiki/How-it-Works
重要概念:
服务降级(fallback):服务器忙,请稍后再试,不让客户端等待并返回一个友好提示。
哪些情况下会出现降级:程序运行异常、超时、服务熔断触发服务降级、线程池/信号量打满也会导致服务降级
服务熔断:类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示。
就是保险丝:服务的降级-->进而熔断-->恢复调用链路
服务限流:秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行。
0.3场景模拟
(1)创建maven工程为服务提供者(cloud-provider-hystrix-payment8001)
(2)application.yml
server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka/
(3)Service两个方法,一个是不耗时,一个耗时5秒钟
@Service
public class PaymentService {
//成功
public String paymentInfo_OK(Integer id){
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id+"\t"+"哈哈哈" ;
}
//失败
public String paymentInfo_TimeOut(Integer id) throws InterruptedException {
int timeNumber = 5;
try {
TimeUnit.SECONDS.sleep(timeNumber);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;
}
}
(4)Controller调用Service
@RestController
public class PaymentController {
@Resource
private PaymentService paymentService;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentService.paymentInfo_OK(id);
return result;
}
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentService.paymentInfo_TimeOut(id);
return result;
}
}
(5)启动7001Eureka server,cloud-provider-hystrix-payment8001
(6)高并发测试
1.开启Jmeter软件,来2万个并发压死8001,2万个请求都去访问那个耗时5秒的方法
2.这时去访问那个不耗时的,结果发现两个都在转圈圈(原因:SpringBoot内置的tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理)
3.假如此时外部的消费者也来访问,那消费者只能干等,最终导致消费端不满意,服务端8001直接被拖死
(7)新建Maven工程为服务消费者(cloud-consumer-feign-hystrix-order80)
(8)application.yml
server:
port: 80
spring:
application:
name: cloud-provider-hystrix-order
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka/
(9)使用OpenFeign服务调用:Controller
@RestController
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_OK(id);
return result;
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
}
(10)使用OpenFeign服务调用:Service接口,不需要实现类
@Service
@FeignClient(value = "cloud-provider-hystrix-payment")
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
(11)高并发测试
1.接着上面的测试,使用消费者访问,消费者也会转圈圈或者消费端超时报错
故障现象和导致原因:8001同一层次的其他接口服务被困死,因为tomcat线程里面的工作线程已经被挤占完毕,80此时调用8001,访问响应缓慢,转圈圈。
正因为有上述故障或不佳表现,才有降级/容错/限流等技术诞生。
0.4Hystrix服务降级
解决需求:
超时导致服务器变慢(转圈):超时不再等待
出错(宕机或程序运行出错):出错要有兜底
怎么解决:
对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级
对方服务(8001)宕机了,调用者(80)不能一直卡死等待,必须有服务降级
对方服务(8001)OK,调用者(80)自己出故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级
解决:服务降级
(1)8001提供者先从自己找问题:设置超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处理
(2)添加Hystrix依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
(3)Service添加一个作为服务降级兜底的方法
public String paymentInfo_TimeOutHandler(Integer id){
return "线程池:"+Thread.currentThread().getName()+" 8001系统繁忙或运行报错,请稍后再试,id: "+id+"\t"+"o(╥﹏╥)o";
}
(4)Service往会耗时的方法上添加@HystrixCommand
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")//3秒钟以内正常
})//paymentInfo_TimeOutHandler为兜底的方法
public String paymentInfo_TimeOut(Integer id) {
int timeNumber = 5;
try {
TimeUnit.SECONDS.sleep(timeNumber);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;
}
(5)提供者启动类上加@EnableCircuitBreaker,表示激活Hystrix
(6)80消费者服务降级
(7)application.yml
feign:
hystrix:
enabled: true #如果处理自身的容错就开启
(8)Controller添加一个作为服务降级兜底的方法
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
return "我是消费者80,对付支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,(┬_┬)";
}
(9)Controller层为OpenFeign服务调用会耗时的方法上加@HystrixCommand
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
(10)消费者启动类上加@EnableHystrix
0.5服务降级改造
出现的问题:
代码膨胀:每个业务代码对应一个兜底的方法,代码膨胀。
业务混乱:兜底方法和业务逻辑混在一起
解决代码膨胀:
(1)Controller类上加@DefaultProperties
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") //defaultFallback对应的是全局服务降级方法
(2)加了@HystrixCommand的方法如果没有配置fallbackMethod,那就该方法就会找defaultFallback对应的方法
解决业务混乱:
(1)80消费者解决业务混乱,只需要为service用作远程调用的接口添加一个实现类,在该类统一为接口里的方法进行兜底处理
@Service
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 , (┬_┬)";
}
}
(2)在Service接口的@FeignClient里添加fallback属性
@FeignClient(value = "cloud-provider-hystrix-payment",fallback = PaymentFallbackService.class)
//PaymentFallbackService就是兜底用的实现类
(3)测试:正常访问后,把8001关掉,这时我们做了服务降级处理,让客户端不可用时也会获得提示信息而不会挂起耗死服务器
0.6服务熔断
熔断机制是因对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
大神论文:https://martinfowler.com/bliki/CircuitBreaker.html
服务熔断也是服务降级使用到的@HystrixCommand
服务降级的使用:
(1)在8001的Service中
@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"), //失败率达到60%后跳闸(错误百分比阙值)
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
if (id < 0){
throw new RuntimeException("*****id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
}
//三个重要参数,还有其他参数
//快照时间窗:断路器确定是否打开需要统计请求和错误数据,而时间范围就是快照时间窗,默认10秒
//请求总数阙值:在快照时间窗内,必须满足请求总数阙值才有资格熔断,默认为20。意味着在20秒内,如果调用次数不足20次,即使所有请求都失败或超时,断路器都不会打开
//错误百分比阙值:在快照时间窗内满足了请求总数阙值。如果满足了默认50%的错误阙值,那么断路器就会打开
(2) 8001的Controller
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
String result = paymentService.paymentCircuitBreaker(id);
log.info("*******result:"+result);
return result;
}
(3)测试:多次错误,然后慢慢正确,发现刚开始不满足条件,就算是正确的访问地址也不能进行访问,需要慢慢的恢复链路
断路器打开之后:
0.7服务限流
在alibaba的Sentinel说明
0.8服务监控
Hystrix提供了准实时的调用监控,Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户。
(1)创建Maven工程作为监控平台(cloud-consumer-hystrix-dashboard9001)
(2)添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
(3)application.yml
server:
port: 9001
(4)启动类上加@EnableHystrixDashboard
(5)测试:http://localhost:9001/hystrix
(6)在8001服务提供者的启动类中
/**
* 此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
* ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
* 只要在自己项目里配置下面的servlet就可以了
* @return
*/
@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;
}
0.9监控平台的使用
输入监测地址监控8001:
http://localhost:8001/hystrix.stream(8001是服务提供者的端口号)
2000
T3
查看:
7色、
1圈、
1线:用来记录2分钟内流量的相对变化,可以通过它来观察到流量的上升和下降趋势
整图说明:
整图说明2: