雪崩
当山坡积雪内部的内聚力抗拒不了它所受到的重力拉引时,积雪便向下滑动,引起⼤量雪体崩塌,人们把这种自然现象称作雪崩
微服务中,一个请求可能需要多个微服务接口才能实现,会形成复杂的调用链路
服务雪崩效应,是一种因“服务提供者的不可用”(原因)导致“服务调用者不可用”(结果),并将不可用逐渐放大的现象
扇入:代表着该微服务被调用的次数,扇人大,说明该模块复用性好
扇出:该微服务调用其他微服务的个数,扇出大,说明业务逻辑复杂
扇入大是一个好事,扇出大不一定是好事
服务雪崩效应形成原因
1. 服务提供者不可用(硬件故障,程序BUG,缓存击穿,用户大量请求)
2. 重试加大请求流量(用户重试,代码逻辑重试)
3. 服务调用者不可用(同步等待造成的资源耗尽)
服务雪崩效应解决方案
1. 服务熔断:当扇出链路的某个微服务不可用或者响应时间太长时,熔断该节点微服务的调用,进行服务的降级,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路(和服务降级一起使用,重点在"断",切断对下游服务的调用)
2. 服务降级:把无关紧要的服务关掉,留给目前处于高峰的服务,每个服务都要在关停掉后能给用户返回一个默认值
3.服务限流:在一次性有大多数用户涌入时,限制用户的数量,均速的放行用户
1).限制总并发数(如数据库连接池、线程池)
2).限制瞬时并发数(如Nginx限制瞬时并发连接数)
3).限制时间窗口内的平均速率(如Guava的RateLimiter、Nginx的limit_req模块,限制每秒的平均速率)
4).限制远程接口调用速率、限制MQ的消费速率等
Hystrix
Hystrix:由Netfix开源的一个延迟和容错库,用于隔离访问远程系统,服务或者第三方库,防止级联失败,从而提升系统的可用性和容错性
包裹请求:使用HystrixCommand包裹对依赖的调用逻辑。页面静态化微服务方法(@HystrixCommand添加Hystrix控制)。
跳闸机制:当某服务的错误率超过一定的阈值时,Hystrix可以跳闸,停止请求该服务一段时间。
资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(舱壁模式)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等待,从而加速失败判定。
监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等
回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑由开发人员自行提供,例如返回一个缺省值。
自我修复:断路器打开一段时间后,会自动进入“半开”状态(探测服务是否可用,如还是不可用, 再次退回打开状态) .
Hystrix应用
1.在page微服务中导图熔断器的依赖
<!-- 熔断器Hystrix --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
2.在page的启动类上启动熔断器
@EnableCircuitBreaker//开启Hystrix熔断器功能
3.在page控制层,设置获取端口接口的熔断
//如果服务提供者请求等待时间过长,服务消费方通过Hytrix来控制 @HystrixCommand( threadPoolKey = "queryById",//线程池标识配置,默认情况下所有的请求共同维护一个线程池 threadPoolProperties = {//配置线程的详细内容 @HystrixProperty(name = "coreSize",value = "2"),//核心线程为2 @HystrixProperty(name = "maxQueueSize",value = "20")//等待队列的最大长度 }, commandProperties = {//配置熔断器的属性信息 @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "2000")//设置请求的超时时间(ms),一旦超过配置的时间会触发Hystrix的处理机制 } ) //获取服务的端口号 @RequestMapping("queryPort") public String queryProductServerPort(){ String url="http://leq-service-product/server/getPort"; return restTemplate.getForObject(url,String.class); }
4.为了演示熔断我们需要手动创建一个错误,在product900中的获取端口的接口中让线程睡眠5秒
import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("server") public class ServerConfigController { @Value("${server.port}") private String serverprot; @RequestMapping("getPort") public String findServerPort(){ try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return serverprot; } }
5.重启page和product的项目,eureka集群之后启动,重启后在浏览器访问该接口
http://localhost:9100/page/queryPort
可以看到一个正常一个又queryProductServerPort timed-out这个错误,说明我们的熔断配置成功,接下来就将该错误改为一个返回值就好
6自定义服务不可用时返回默认数据(降级处理)
fallbackMethod = "" 如果对应的服务提供者出现问题,则会调用fallbackMethod属性指向的方法,将该方法的返回值作为最终响应给客户端的数据,该方法和目标方法的格式(返回值,参数)一致
public String fallbackMethodgetPort(){ return "-1"; }
7.重启page的微服务项目,重新访问上面的端口
可以看到异常已经改为我们自定义的返回值
Hystrix舱壁模式:
如果不进行任何设置,所有熔断方法使用一个Hystrix线程池(10个线程),那么这样的话会导致问题,这个问题并不是扇出链路微服务不可用导致的,而是我们的线程机制导致的,如果方法A的请求把10个线程都用了,方法B请求处理的时候压根都没法被访问,因为没有线程可用,并不是方法B服务不可用。为了避免服务请求过多导致正常服务无法访问的问题,Hystrix不是采用增加线程数,而是单独的为每一个控制方法创建一个线程池的方式,这种模式叫做“舱壁模式”,也是线程隔离的手段
Hystrix工作流程与高级应用
Hystrix的工作流程图:
1.在调用出现问题时,开启一个时间窗
2.在这个时间窗内,统计调用次数是否达到最小请求数
如 果没有达到,则重置统计信息,回到第1步。
如果达到了,则统计失败的请求数占所有请求数的百分比,是否达到阈值。
(1)如果达到,则跳闸(不再请求对应服务)。
(2)如果没有达到,则重置统计信息,回到第1步。
3.如果跳闸,则会开启一个活动窗口(默认5S),每隔5秒,Hystrix会让一个请求通过,到达那个问题服务,看是否调用成功?如果成功,重置断路器回到第1步;如果失败,回到第3步高级应用
1.注解在@HystrixCommand下的commandProperties里面进行配置
//Hystrix高级配置,定制工作过程细节 @HystrixProperty(name="metrics.rollingStats.timeInMilliseconds",value = "8000"),//时间窗口的时长 @HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "2"),//时间窗口内的最小请求数 @HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50"),//错误数量的百分比阈值 @HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "3000")//自我修复时的活动窗口长度
配置完成后重启,只要我们之前的业务没影响就证明配置成功
2.将注解注释掉,确保是配置文件生效 在核心配置文件中配置(熔断器)
#时间窗口的时长 hystrix.command.default.metrics.rollingStats.timeInMilliseconds=8000 #强制打开熔断器 hystrix.command.default.circuitBreaker.forceOpen= false #触发熔断错误比例阈值,默认值50% hystrix.command.default.circuitBreaker.errorThresholdPercentage= 50 #熔断后休眠时间,默认值5秒 hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds= 3000 #熔断触发最大请求次数,默认值是20 hystrix.command.default.circuitBreaker.requestVolumeThreshold= 2 #熔断超时设置,默认为1秒 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds= 2000
在核心配置文件中配置(暴露接口)application.properties
#SpringBoot中暴露健康检查等断点接口 #management.endpoints.web.exposure.include="*" #暴露健康接口的细节 management.endpoint.health.show-details=always
不知道为什么我的SpringBoot中暴露健康检查等断点接口在application.properties中无法使用,只能在yml文件中生效
# Spring Boot中暴露健康检查等断点接口 management: endpoints: web: exposure: include: "*"
在核心配置文件中配置(配置线程池)application.properties
#配置线程池队列 #并发执行的最大线程数 hystrix.threadpool.default.coreSize=10 #队列的最大存储数量 hystrix.threadpool.default.maxQueueSize=1500 #队列的最大请求数量 hystrix.threadpool.default.queueSizeRejectionThreshold=800
配置完成后重启page的微服务项目,访问原来的接口也都有使用,然后我们配置了暴露接口,可以通过autuator/health来访问,通过这个接口来监控服务中心的健康状况
http://localhost:9100/actuator/health