文章目录
高并发下可能导致的问题
假设,我们现在有服务A,服务B和服务C,其中服务A调用了服务B,服务B调用了服务C,即:
如果,现在服务C中某个接口瞬间的并发量比较大,导致服务C的整个服务都在龟速运行,这就必然导致服务B中接收服务C数据的时候会很慢,这样就会导致请求的堆积,慢慢的直至服务B崩掉,然后服务A也会出现类似情况。这就是服务雪崩。
当然,也有可能是服务C的宕机,资源调度等等的问题,但是,一台机器出现的问题,在不做处理的情况下,必然会导致整个服务的不可用。
解决方案
线程池隔离
假如我是使用方法B对服务C进行调用的,那么,我就为方法A单独设置一个线程池,那么,方法A出现问题,也不会波及到其他的方法,也就不会导致请求的积压。
实现(Hystri的基础用法)
- 引入Hystrix依赖(只是Hystrix依赖,和springcloud没关系)
<dependency>
<groupId>com.netfilix.hystrix</groupId>
<artifactId>hystrix-metrics-event-stream</artifactId>
<version>1.5.12</version>
</dependency>
<dependency>
<groupId>com.netfilix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>1.5.12</version>
</dependency>
- 编写command对象
public class ProductCommand extends HystrixCommand<String> {
private RestTemplate restTemplate;
private Long id;
protected ProductCommand(RestTemplate restTemplate,Long id) {
super(setter());
this.restTemplate = restTemplate;
this.id = id;
}
private static Setter setter(){
//服务分组
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("product");
//服务标识
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("product_1");
//线程池名称
HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("product_1_pool");
/**
* 线程池配置
* withCoreSize: 线程池大小为10
* withKeepAliveTimeMinutes: 线程存活时间15秒
* wuthQueueSizeRejectionThreshold: 队列等待的阈值为100,超过100执行拒绝策略
* */
HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter().withCoreSize(10)
.withKeepAliveTimeMinutes(15).withQueueSizeRejectionThreshold(100);
//命令属性配置Hystrix开启超时
HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
//采用线程池方式实现服务隔离
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
//禁止
.withExecutionTimeoutEnabled(false);
return Setter.withGroupKey(groupKey).andCommandKey(commandKey).andThreadPoolKey(threadPoolKey)
.andThreadPoolPropertiesDefaults(threadPoolProperties).andCommandPropertiesDefaults(commandProperties);
}
@Override
protected String run() throws Exception {
return restTemplate.getForObject("http://product-service/product/getProduct/"+id,String.class);
}
/**
* 服务降级
* */
@Override
protected String getFallback(){
return "对不起,出错啦";
}
}
说明:
1.setter()里面的是配置
2.getFallback()方法是服务降级方法,即假如并发实在太高,会拒绝服务,但是又为了不让被服务端看起来太难看,一般会在这个方法中处理
3.run()方法是在你访问的时候,放到线程池中运行的方法
- 使用
new ProductCommand(restTemplate,10L).execute()
信号量隔离
信号量隔离,其实就是给访问服务的方法设置一个计数器,只要超过这个计数器的请求都直接拒绝。
应对雪崩效应
- 服务隔离
将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖。当有故障发生的时候,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其他模块,不影响整体的系统服务。 - 熔断降级
熔断这概念来源于电子工程中的断路器。在互联网系统中,当下游服务因访问压力过大而响应变慢或者失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用,这种牺牲局部,而保全整体的措施就叫熔断。
所谓降级,就是当某个服务熔断之后,服务器不再被调用,此时客户端可以自己准备一个本地fallback回调,返回一个缺省值。 - 服务限流
限流可以认为服务降级的一种,限流就是限制i同的输入和输出流量以达到保护系统的目的。一般来说,系统的吞吐量是可以被测算的,为了保护系统的稳固运行,一旦达到需求限制的阈值,就需要限制流量并采取少量的措施以完成限制流量的目的。比方:推迟解决,拒绝解决或者部分解决等等。
Hystrix介绍
Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要是通过以下几点实现延迟和容错。
- 包裹请求:使用HystrixCommand包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的"命令模式"
- 跳闸机制:当某服务的错误率超过一定的阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。
- 资源隔离:Hystrix为每个依赖都维护了一个小型线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等待,从而加速失败判定。
- 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。
- 回退机制:当请求失败、超时、被拒绝,或者当断路器打开时,执行回退逻辑。回退逻辑由开发人员自行提供,例如返回一个缺省值。
- 自我修复:断路器打开一段时间后,会自动进入"半开"状态
Hystrix使用
对RestTemplate的支持
- 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 激活hystrix
在启动类上加上@EnableCircuitBreaker - 降级方法
/**
* 降级方法:
* 和需要受到保护的方法返回值是一致的
* 方法参数一致
* */
public String getProductFallback(Long id){
return "对不起出错了";
}
- hystrix的配置
hystrix:
command:
default:
exxecution:
isolation:
thread:
timeoutInMilliseconds: 3000 #超时视为熔断
- 统一的降级方法
@RestController
@RequestMapping("/demo")
/**
* @DefaultProperties 指定此接口中公共的熔断设置
* 如果在@DefaultProperties指定了公共的降级方法,在@HystrixCommand就不用单独指定
* */
@DefaultProperties(defaultFallback = "defaultFallBack")
public class DemoController {
//注入restTemplate
@Autowired
private RestTemplate restTemplate;
/**
* 使用注解配置熔断保护
* fallbackmethod:配置熔断之后的降级方法
* */
@HystrixCommand
@RequestMapping(value = "/get/{id}",method = RequestMethod.GET)
public String get(@PathVariable Long id){
return restTemplate.getForObject("http://product-service/product/getProduct/"+id,String.class);
}
**
* 指定统一的降级方法
* 参数:没有参数
* */
public String defaultFallBack(){
return "公共的降级方法";
}
}
注意:
1.添加@DefaultProperties(defaultFallback = “defaultFallBack”)
2.defaultFallBack()没有参数
3.@HystrixCommand可以不用指定方法
4.返回值应该保持一致
对Feign的支持
- 引入依赖(Feign中已经集成了Hystrix)
引入Feign的依赖就好 - 在feign中配置开启Hystrix
feign:
hystrix:
enabled: true #开启histrix的支持
- 自定义一个接口的实现类,这个实现类就是熔断触发的降级逻辑
@Component
public class ProductFeignClientCallBack implements ProductFeignClient {
@Override
public String getProduct(Long id) {
return "抱歉,出错了";
}
}
ProductFeignClient 接口为FeignClient
- 修改feignClient接口添加降级方法的支持
/**
* 声明需要调用的微服务名称
* @FeignClient
* name:服务提供者的名称
* */
@FeignClient(name="product-service",fallback = ProductFeignClientCallBack.class)
public interface ProductFeignClient {
/**
* 配置需要调用的接口
* */
@RequestMapping(value = "/product/{id}",method = RequestMethod.GET)
String getProduct(@PathVariable("id") Long id);
}
重点关注 fallback = ProductFeignClientCallBack.class
Hystrix监控数据
- 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
- 配置信息(暴露监控的端点)
management:
endpoints:
web:
exposure:
include: "*" #暴露所有端点
- 在启动类中添加注解
@EnableCircuitBreaker - 在网页中输入网址
http://192.168.0.107:9002/actuator/hystrix.stream
现在是没有数据的,需要你去访问一次监控的端口,如:http://192.168.0.107:9002/demo/get/1
这个端口中,使用了Feign去访问数据,然后激活了hystrix,所以会有数据
搭建Hystrix DashBoard监控
上面已经获取到信息了,但是没有图形化界面可以看到,这个平台提供了这个功能
- 导入依赖(其实上面已经导入过)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
- 在启动类上添加注解
@EnableHystrixDashboard - 访问测试
http://192.168.0.107:9002/hystrix
在第一个框中输入你获取数据的网址
http://192.168.0.107:9002/actuator/hystrix.stream(之前一直出现ping的页面)
点击Moniter Stream进入监控页面
各个参数代表什么上面也有说明
断路器聚合监控Turbine
在DashBoard监控中,我们是直接输入消费者的ip以及端口进入相应的服务才能进行监控,在正式环境中,消费者和服务者错综复杂,而且很多,一个一个地去访问不切实际,因此,我们需要一个可以将所有的消费者聚合起来然后进行集体监控的一个工具 ----- Turbine
那么这个工具要怎么使用呢?
首先新建一个新的maven工程(一个新的微服务)
- 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
- 配置hystrix的配置信息
在application.yml中进行配置
server:
port: 8998
spring:
application:
name: hystrix-turbine
#eureka配置
eureka:
client:
service-url:
defaultZone: http://localhost:9000/eureka/,http://localhost:8999/eureka/
instance:
prefer-ip-address: true
turbine:
#要监控的微服务列表,多个用,分隔
app-config: consumer-service
cluster-name-expression: "'default'"
- 在启动类上添加注解
@EnableTurbine
@EnableHystrixDashboard - 启动工程
启动工程后,访问http://192.168.0.107:8998/hystrix,这个工程的ip以及这个工程的端口。然后按照DashBoard的操作就可以了。
注意:
1.这里第一个框里面输入的ip和端口是新的工程的ip和端口
2.被监控的微服务必须开启端点数据
断路器
- 介绍
熔断器有三个状态CLOSE、OPEN、HALF_OPEN熔断器默认关闭状态,当触发熔断后状态变更为OPEN,在等待到指定的时间,Hystrix会放请求检测服务是否开启,这期间熔断器会变为HALF_OPEN半开启状态,熔断探测服务可用则就变更为CLOSE关闭熔断器。
- CLOSE(关闭)
(1)所有的请求都可以正常访问
(2)请求次数大于20次,且存在50%的失败概率 - OPEN(开启)
(1)所有的请求会进入到降级方法中 - HAFT_OPEN(半开)
(1)维持open状态一段时间(5s),进入到半开状态(尝试释放一个请求到远程微服务发起调用)
如果释放的请求可以正常访问,就会关闭断路器
如果释放的请求不能访问,继续保持开启5s - 相关配置
hystrix:
command:
default:
circuitBreaker:
requestVolumeThreshold: 5 #触发熔断的最小请求次数,默认20 / 10秒
sleepWindowInMilliseconds: 10000 #熔断多少秒后去尝试请求,默认 5 打开状态的时间
errorThresholdPercentage: 50 #触发熔断的失败请求的最小占比,默认50%
熔断器的隔离策略
- 线程池隔离策略
使用一个线程池来存储当前的请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求堆积入线程池队列,这种方式需要为每个依赖的服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队列里慢慢处理) - 信号量隔离策略
使用一个原子计数器(或者信号量)来记录当前有多少线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃该类型的请求,若不超过则执行计数操作请求数,计数器+1,请求返回计数器-1,这种方式时严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务) - 修改隔离策略的配置
hystrix:
command:
default:
exxecution:
isolation:
strategy: ExecutionIsolationStrategy.SEMAPHPRE #信号量
# ExecutionIsolationStrategy.THREAD 线程池隔离