容错机制
如果服务提供者相应非常缓慢,那么消费者对提供者的请求就会被强制等待,知道提供者相应超时。在高负载场景下,如果不作任何处理,此类问题可能会导致服务消费者的资源耗尽甚至整个系统崩溃。
雪崩效应
微服务架构的应用系统通常包含多个服务层,微服务之间通过网络进行通信,从而支撑起整个应用系统,因此,微服务之间难免存在依赖关系。我们常把“基础服务故障”导致“级联故障”的现象成为雪崩效应。雪崩效应描述的是提供者不可用导致消费者不可用,并将不可用逐步扩大的过程。
如何容错
为了防止雪崩效应,必须有一个强大的容错机制。该容错机制需实现以下两点:
1.为网络请求设置超时
必须为网络请求设置超时。正常情况下,一个远程调用一般在及时毫秒内就能得到响应了。如果依赖的服务不可用或者网络有问题,那么响应时间就会变得特别长。
通常情况下,一次远程调用对应着一个线程/进程。如果响应太慢,这个线程/进程就得不到释放。而线程/进程又对应着系统资源,如果得不到释放的线程/进程约积越多,资源就会逐渐被耗尽,最终导致服务的不可用。
2.使用断路器模式
如果对某个微服务的请求有大量超时(常常说明该微服务不可用),再去让新的请求访问该服务已经没有任何意义,只会无所谓消耗资源。例如,设置了超时时间为1秒,如果短时间内有大量的请求无法在1秒内得到响应,就没有必要再去请求依赖的服务了。
短路器可理解为对容易导致错误的操作的代理。这种代理能够统计一段时间内调用失败的次数,并决定是正常请求依赖的服务还是直接返回。
断路器可以实现快速失败,如果它在一段时间内检测到许多类似的错误(例如超时),就会在之后的一段时间内,强迫对该服务的调用快速失败,即不再请求所依赖的服务。这样,应用程序就无需再浪费cpu时间去等待长时间的超时。
短路器也可自动诊断是否已经恢复正常。如果发现依赖的服务已经恢复正常,那么就会恢复请求该服务。使用这种方式,就可以实现微服务的“自我修复”——当依赖的服务不正常打开断路器时快速失败,从而防止雪崩效应;当发现依赖的服务恢复正常时,又会恢复请求。
断路器状态转换逻辑:
- 正常情况下,断路器关闭,可正常请求依赖的服务
- 当一段时间内,请求失败率达到一定阀值(例如错误率达到50%,或100次/分钟等),断路器就会打开。此时,不会再去请求依赖的服务。
- 断路器打开一段时间后,会自动进入“半开”状态。此时,断路器可允许一个请求访问依赖的服务。如果该请求能够调用成功,则关闭断路器;否则继续保持打开状态。
使用Hystrix实现容错
Hystrix简介
Hystrix是一个实现了超时机制和断路器模式的工具类库。是由Netfix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。
- 包裹请求:使用HystrixCommand(或者HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的“命令模式”。
- 跳闸机制:当某服务的错误率超过一定阀值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。
- 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判断
- 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。
- 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员自行提供,例如返回一个缺省值。
- 自我修复:断路器打开一段时间后,会自动进入“半开”状态。
通用方式整合Hystrix
在Springcloud中,整合Hystrix非常方便,我们以ribbon-consumer项目为例
添加如下依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
在启动类添加注解 @EnableHystrix //开启断路器功能
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix //开启断路器功能
public class YmkRibbonConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(YmkRibbonConsumerApplication.class, args);
}
// 开启客户端负载均衡
@LoadBalanced
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
修改控制器代码,让hello方法具有容错能力。
@RestController
public class HelloController {
@Autowired
RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
// HystrixCommand具有容错能力 fallbackMethod属性指定回退方法
@HystrixCommand(fallbackMethod = "helloFallBack")
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello(@RequestParam String name) {
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://provider/hello?name={1}", String.class,name);
return responseEntity.getBody();
}
// 回退方法有跟正常方法一样的参数
public String helloFallBack(String name){
return "helloFallBack "+name;
}
@GetMapping("/log-user-instance")
public void logUserInstance() {
ServiceInstance serviceInstance = this.loadBalancerClient.choose("provider");
// 打印当前选择的是哪个节点
System.out.println("{}:{}:{}," + serviceInstance.getServiceId()+","+ serviceInstance.getHost()+","+ serviceInstance.getPort());
}
}
以此启动项目: 访问 http://localhost:8010/hello?name=feign
以上是正常的访问结果,现在关掉提供者项目,模拟提供者服务不可用。停掉ymk-provider项目,
再次访问 http://localhost:8010/hello?name=feign
再次访问,直接进入了回退方法中。
很多场景下,我们需要知道造成回退的原因,只需在fallback方法上添加一个Throwable参数即可,
// 回退方法有跟正常方法一样的参数
public String helloFallBack(String name,Throwable throwable){
System.out.println(throwable);
return "helloFallBack "+name;
}
很多情况下,当发生业务异常时候,我们并不想触发fallback,怎么办呢? Hystrix有个HystrixBadRequestException类,这是一个特殊的异常类,当该异常发生时候,不会触发回退。因此,可将自定义的异常类继承该类,从而达到业务异常不 回退的效果。
Hystrix断路器的状态监控与深入理解
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
断路器的状态会暴露在Actuator提供的/health端点中,可以直观的看到断路器的状态。
依次启动项目,访问 http://localhost:8010/hello?name=feign 是正常的,再次访问 http://localhost:8010/health
此时Hsytrix的状态是UP,说明一切正常。此时断路器是关闭的。
现在停止提供者项目
再次访问 http://localhost:8010/hello?name=feign 结果进入了回退方法里了,再次访问 http://localhost:8010/health
我们发现尽管进入了回退方法,但是Hystrix的状态依然UP,这是因为我们的失败率还没达到阈值(默认5秒内失败20次),也就是说进入回退方法,并不代表断路器已经打开。
Feign使用Hystrix
使用ymk-feign-consumer项目,添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
接口通过fallback添加回退类,回退类要实现接口
@FeignClient(name = "provider" ,configuration = FeignLogConfiguration.class,fallback = FeignClientFallback.class)
public interface TestFeignClient {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
String hello(@RequestParam("name") String name);
}
@Component
class FeignClientFallback implements TestFeignClient {
@Override
public String hello(String name) {
return "FeignhelloFallBack "+name;
}
}
在application.yml中添加
feign:
hystrix:
enabled: true
# 说明:请务必注意,从Spring Cloud Dalston开始,Feign默认是不开启Hystrix的。
# 因此,如使用Dalston请务必额外设置属性:feign.hystrix.enabled=true,否则断路器不会生效。
# 而,Spring Cloud Angel/Brixton/Camden中,Feign默认都是开启Hystrix的。无需设置该属性。
完成!
通过Fallback Factroy检查回退原因
@FeignClient(name = "provider", fallbackFactory = FeignClientFallbackFactory.class)
public interface TestFeignClientFallBack {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
String hello( String name);
}
/**
* UserFeignClient的fallbackFactory类,该类需实现FallbackFactory接口,并覆写create方法
*/
@Component
class FeignClientFallbackFactory implements FallbackFactory<TestFeignClientFallBack> {
@Override
public TestFeignClientFallBack create(Throwable cause) {
return new TestFeignClientFallBack() {
@Override
public String hello(String name) {
// 日志最好放在各个fallback方法中,而不要直接放在create方法中。
// 否则在引用启动时,就会打印该日志。
System.out.println("fallback; reason was:"+ cause);
return name;
}
};
}
}
为Feign禁用Hystrix
为指定的客户端禁用Hystrix 新建配置类
@Configuration
public class FeignDisableHystrixConfiguration {
@Bean
@Scope("prototype")
public Feign.Builder feignBuider(){
return Feign.builder();
}
}
想要禁用ystrix的FeignClient引用此配置类即可
@FeignClient(name = "provider",configuration = FeignDisableHystrixConfiguration.class)
全局禁用,只需要在application.yml中配置feign.hystrix.enabled = false即可。
源码下载: https://download.csdn.net/download/u013083284/10755912