概念
Hystrix
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
服务雪崩
多个微服务之间调用的时候,假设服务A调用服务B和服务C,微服务B和C又调用其他微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,从而引起系统崩溃,这就是所谓的“雪崩效应”。
服务降级
服务降级是指当服务器压力剧增的情况下,为了预防某些功能(业务场景)出现负荷过载或者响应慢的情况,在其内部暂时舍弃对一些非核心的接口和服务的请求,而直接返回一个提前准备好的 fallback(退路)错误处理信息。这样,虽然提供的是一个有损的服务,但释放了服务器资源以保证核心业务正常运作或高效运作,保证了整个系统的稳定性和可用性。
说白了,就是尽可能的把系统资源让给优先级高的服务。
服务熔断
在微服务架构中,微服务之间的数据交互通过远程调用完成,微服务A调用微服务 B 和微服务 C,微服务B和微服务 C 又调用其它的微服务,此时如果链路上某个微服务的调用响应时间过长或者不可用,那么对微服务 A 的调用就会占用越来越多的系统资源,进而引起系统崩溃,导致“雪崩效应”。
服务熔断是应对雪崩效应的一种微服务链路保护机制。例如在高压电路中,如果某个地方的电压过高,熔断器就会熔断,对电路进行保护。同样,在微服务架构中,熔断机制也是起着类似的作用。当调用链路的某个微服务不可用或者响应时间太长时,会进行服务熔断,不再有该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
服务限流
系统规定了多少承受能力,只允许这么些请求能过来,其他的请求不对其进行处理。
请求缓存
服务A调用服务B,如果在A中添加请求缓存,第一次请求后走缓存,不在访问微服务B,即使出现大量请求,不会对B产生高负荷。请求缓存可以使用spring cache实现。
请求合并
当服务A调用服务B时,设定在5毫秒内所有请求合并到一起,对于服务B的负荷就会减少。使用@HystrixCollapser。方法返回值必须为Future
隔离
离分为线程池隔离合信号量隔离。通过判断线程池或信号量是否满,超过容量的请求直接降级,从而达到限流。
应用
- 其中注册中心使用前面创建的eureka-server
- 新建一个spring-boot工程,取名为hystrix-provider,在pom文件引入Hystrix需要的依赖。
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
server:
port: 8083
spring:
application:
name: hystrix-provider
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
# 自定义服务名称信息
instance-id: hystrix-provider:8083
#访问路径可以显示Ip地址
prefer-ip-address: true
Hystrix 进行服务降级
服务降级就是指服务器忙,请稍候再试,不让客户端等待并立刻返回一个友好提示 fallback。
哪些情况会触发降级:
1)程序运行异常;
2)超时;
3)服务熔断触发服务降级;
4)线程池/信号量打满也会导致服务降级;
服务端降级
@RestController
@RequestMapping("/api")
public class HystrixController {
@Value("${server.port}")
private String serverPort;
@PostMapping("/testProviderHystrix")
//一但调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod中的指定方法
@HystrixCommand(fallbackMethod = "processHystrixGet",
commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500")})
public String testProviderHystrix() throws InterruptedException {
System.out.println("testHystrixCommand被调用了");
Thread.sleep(1000);
return "我是服务端" + serverPort;
}
public String processHystrixGet() {
System.out.println("processHystrixGet被调用了");
return serverPort + "服务超时,服务降级啦";
}
}
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class HystrixProviderApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixProviderApplication.class, args);
}
}
在消费端调用接口(继续使用前面使用的eureka-consumer),测试效果
@RestController
@RequestMapping("/api")
public class ConsumerController {
@Resource
private RestTemplate restTemplate;
@PostMapping("/testHystrixCommand")
public String testHystrixCommand(){
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("name", "consumer8090");
ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://HYSTRIX-PROVIDER/api/testHystrixCommand", body, String.class);
return responseEntity.getBody();
}
}
客户端降级
使用前面的搭建好的consumer-feign模块
- yml 中配置启动 feign 的 hystrix 支持
feign:
hystrix:
#如果处理自身的容错就开启。
enabled: true
- 服务端设置睡眠时间
@RestController
@RequestMapping("/api")
public class ProviderController implements ConsumerProviderService {
@Value("${server.port}")
private String serverPort;
@Override
@PostMapping("/sayHello")
public String sayHello(@RequestBody String name) {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "我是服务端" + serverPort + ",你好" + name;
}
@Override
@PostMapping("/getOne")
public Person getOne(){
return new Person("张三", 22);
}
}
- Feign客户端配置fallback 降级方法
@FeignClient(value = "EUREKA-PROVIDER", fallbackFactory = ConsumerProviderServiceBack.class)
public interface ConsumerProviderService {
/**
* 获取人员信息
*
* @return
*/
@PostMapping("/api/getOne")
Person getOne();
@PostMapping("/api/sayHello")
String sayHello(@RequestBody String name);
}
@Component
public class ConsumerProviderServiceBack implements FallbackFactory<ConsumerProviderService> {
@Override
public ConsumerProviderService create(Throwable throwable) {
return new ConsumerProviderService() {
@Override
public Person getOne() {
return null;
}
@Override
public String sayHello(String name) {
return "服务超时,降级啦";
}
};
}
}
//扫描com.xx下的所有包,如果fallback不在同一级或子级包下面需要添加其共同父级包路径
@SpringBootApplication(scanBasePackages = "com.xx")
@EnableEurekaClient
//默认扫描当前注解所在包的同级和子级,若不在同一级或子级,需要申明其Feign客户端所在包路径
@EnableFeignClients(basePackages = "com.xx.springcloudapi")
@EnableCircuitBreaker
public class ConsumerFeignApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerFeignApplication.class, args);
}
}
Hystrix 进行服务熔断
熔断机制是应对雪崩效应的一种微服务链路保护机制。
当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路(自我恢复)。
这里我们修改hystrix-provider模块,实现熔断功能
@RestController
@RequestMapping("/api")
public class HystrixController {
@Value("${server.port}")
private String serverPort;
@PostMapping("/testProviderHystrix")
//一但调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod中的指定方法
@HystrixCommand(fallbackMethod = "processHystrixGet", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500"), //超时时间
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),//请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000"),//时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "20")//失败率达到多少跳闸
})
public String testProviderHystrix() throws InterruptedException {
System.out.println("testHystrixCommand被调用了");
Thread.sleep(1000);
return "我是服务端" + serverPort;
}
public String processHystrixGet() {
System.out.println("processHystrixGet被调用了");
return serverPort + "服务超时,服务降级啦";
}
}
使用Jmeter进行测试:
从结果来看,服务被熔断了,后续不在进入业务代码块,而是直接返回了fallbackMethod的结果
commandProperties配置
Execution
用来控制HystrixCommand.run()的执行
- execution.isolation.strategy:该属性用来设置HystrixCommand.run()执行的隔离策略。默认为THREAD。
- execution.isolation.thread.timeoutInMilliseconds:该属性用来配置HystrixCommand执行的超时时间,单位为毫秒。
- execution.timeout.enabled:该属性用来配置HystrixCommand.run()的执行是否启用超时时间。默认为true。
- execution.isolation.thread.interruptOnTimeout:该属性用来配置当HystrixCommand.run()执行超时的时候是否要它中断。
- execution.isolation.thread.interruptOnCancel:该属性用来配置当HystrixCommand.run()执行取消时是否要它中断。
- execution.isolation.semaphore.maxConcurrentRequests:当HystrixCommand命令的隔离策略使用信号量时,该属性用来配置信号量的大小。当最大并发请求达到该设置值时,后续的请求将被拒绝。
Fallback
用来控制HystrixCommand.getFallback()的执行
- fallback.isolation.semaphore.maxConcurrentRequests:该属性用来设置从调用线程中允许HystrixCommand.getFallback()方法执行的最大并发请求数。当达到最大并发请求时,后续的请求将会被拒绝并抛出异常。
- fallback.enabled:该属性用来设置服务降级策略是否启用,默认是true。如果设置为false,当请求失败或者拒绝发生时,将不会调用HystrixCommand.getFallback()来执行服务降级逻辑。
Circuit Breaker
用来控制HystrixCircuitBreaker的行为。
- circuitBreaker.enabled:确定当服务请求命令失败时,是否使用断路器来跟踪其健康指标和熔断请求。默认为true。
- circuitBreaker.requestVolumeThreshold:用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为20的时候,如果滚动时间窗(默认10秒)内仅收到19个请求,即使这19个请求都失败了,断路器也不会打开。
- circuitBreaker.sleepWindowInMilliseconds:用来设置当断路器打开之后的休眠时间窗。休眠时间窗结束之后,会将断路器设置为“半开”状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为“打开”状态,如果成功,就设置为“关闭”状态。
- circuitBreaker.errorThresholdPercentage:该属性用来设置断路器打开的错误百分比条件。默认值为50,表示在滚动时间窗中,在请求值超过requestVolumeThreshold阈值的前提下,如果错误请求数百分比超过50,就把断路器设置为“打开”状态,否则就设置为“关闭”状态。
- circuitBreaker.forceOpen:该属性默认为false。如果该属性设置为true,断路器将强制进入“打开”状态,它会拒绝所有请求。该属性优于forceClosed属性。
- circuitBreaker.forceClosed:该属性默认为false。如果该属性设置为true,断路器强制进入“关闭”状态,它会接收所有请求。如果forceOpen属性为true,该属性不生效。
Metrics
该属性与HystrixCommand和HystrixObservableCommand执行中捕获的指标相关。
- metrics.rollingStats.timeInMilliseconds:该属性用来设置滚动时间窗的长度,单位为毫秒。该时间用于断路器判断健康度时需要收集信息的持续时间。断路器在收集指标信息时会根据设置的时间窗长度拆分成多个桶来累计各度量值,每个桶记录了一段时间的采集指标。例如,当为默认值10000毫秒时,断路器默认将其分成10个桶,每个桶记录1000毫秒内的指标信息。
- metrics.rollingStats.numBuckets:用来设置滚动时间窗统计指标信息时划分“桶”的数量。默认值为10。
- metrics.rollingPercentile.enabled:用来设置对命令执行延迟是否使用百分位数来跟踪和计算。默认为true,如果设置为false,那么所有的概要统计都将返回-1。
- metrics.rollingPercentile.timeInMilliseconds:用来设置百分位统计的滚动窗口的持续时间,单位为毫秒。
- metrics.rollingPercentile.numBuckets:用来设置百分位统计滚动窗口中使用桶的数量。
- metrics.rollingPercentile.bucketSize:用来设置每个“桶”中保留的最大执行数。
- metrics.healthSnapshot.intervalInMilliseconds:用来设置采集影响断路器状态的健康快照的间隔等待时间。
Request Context
涉及HystrixCommand使用HystrixRequestContext的设置。
- requestCache.enabled:用来配置是否开启请求缓存。
- requestLog.enabled:用来设置HystrixCommand的执行和事件是否打印到日志的HystrixRequestLog中。
Hystrix Dashboard
在微服务架构中为例保证程序的可用性,防止程序出错导致网络阻塞,出现了断路器模型。断路器的状况反应了一个程序的可用性和健壮性,它是一个重要指标。Hystrix Dashboard是作为断路器状态的一个组件,提供了数据监控和友好的图形化界面。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
启动图形化监控
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardApplication.class, args);
}
}
启动hystrix-provider8083,并使用Hystrix Dashboard来进行监控,先请求带有断路器的接口,然后再浏览器访问:http://localhost:8083/hystrix.stream,就会出现如下结果
观察监控窗口
填入需要监控的服务
右上角对应是图像化的解释