1.实现容错的手段
如果服务提供者响应非常缓慢,那么消费者对提供者的请求就会被强制等待,直到提供这响应超时。在高负载的场景下,如果不错处理容易导致雪崩效应
1.1:雪崩效应
我们常把"基础服务故障"导致"级联故障"的现象称为雪崩效应。雪崩小樱的描述的 是提供者不可用导致消费者不可用,并将不可用逐渐放大的过程。
1.2:如何容错
想要防止雪崩效应,必须有一个强大的容错机制。该机制需要实现以下两点。
*为网络请求设置超时
*使用断路器模式
1.3:断路器转换的逻辑图
正常情况下,断路器关闭,可正常请求依赖的服务
当一段时间内,请求失败率达到一定阈值,断路器就会打开,此时,不会再请求依赖的服务
断路器打开一段时间后,会自动进入半开状态。此时,断路器可允许一个请求访问依赖的服务,如果请求能够调用成功,则关闭断路器,否则就继续保持打开状态
2.使用Hystrix实现容错
2.1:通用方式整合Hystrix
2.1.1:复制microservice-consumer-movie-ribbon,将ArtifactId修改为microservice-consumer-movie-ribbon-hystrix
2.1.2:添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itmuch.cloud</groupId>
<artifactId>microservice-consumer-movie-ribbon-hystrix</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>microservice-consumer-movie-ribbon-hystrix</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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-eureka</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-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<!--引入spring cloud依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在启动类上添加注解@EnableHystrix,启动断路器支持
package com.itmuch.cloud.microserviceconsumermovieribbon;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
public class MicroserviceConsumerMovieRibbonHystrixApplication {
/**
* 实例化一个Bean并使用该方法的名字命名
* @return
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(MicroserviceConsumerMovieRibbonHystrixApplication.class, args);
}
}
修改MovieController,让其中的findById方法具备容错能力
package com.itmuch.cloud.microserviceconsumermovieribbon.controller;
import com.itmuch.cloud.microserviceconsumermovieribbon.entity.User;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class MovieController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
private static final Logger logger = LoggerFactory.getLogger(MovieController.class);
/**
* microservice-provider-user:虚拟主机名
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "findByIdFallback",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "10000")
},
threadPoolProperties = {
@HystrixProperty(name = "coreSize",value = "1"),
@HystrixProperty(name = "maxQueueSize",value = "10")
})
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id){
return this.restTemplate.getForObject("http://microservice-provider-user/" + id,User.class);
}
public User findByIdFallback(Long id){
User user = new User();
user.setId(-1L);
user.setName("默认用户");
return user;
}
@GetMapping("/log-instance")
public void logUserInstance(){
ServiceInstance serviceInstance = this.loadBalancerClient.choose("microservice-provider-user");
//打印当前选择的那个节点
MovieController.logger.info("{}:{}:{}",serviceInstance.getServiceId(),serviceInstance.getHost(),serviceInstance.getPort());
}
}
里面添加了回退方法
启动测试
正常状况下
我们关闭服务的提供者
hystrix:使用hystrix入门
附录:各种策略配置
- HystrixCommandProperties
/* --------------统计相关------------------*/
// 统计滚动的时间窗口,默认:5000毫秒(取自circuitBreakerSleepWindowInMilliseconds)
private final HystrixProperty metricsRollingStatisticalWindowInMilliseconds;
// 统计窗口的Buckets的数量,默认:10个,每秒一个Buckets统计
private final HystrixProperty metricsRollingStatisticalWindowBuckets; // number of buckets in the statisticalWindow
// 是否开启监控统计功能,默认:true
private final HystrixProperty metricsRollingPercentileEnabled;
/* --------------熔断器相关------------------*/
// 熔断器在整个统计时间内是否开启的阀值,默认20。也就是在metricsRollingStatisticalWindowInMilliseconds(默认10s)内至少请求20次,熔断器才发挥起作用
private final HystrixProperty circuitBreakerRequestVolumeThreshold;
// 熔断时间窗口,默认:5秒.熔断器中断请求5秒后会进入半打开状态,放下一个请求进来重试,如果该请求成功就关闭熔断器,否则继续等待一个熔断时间窗口
private final HystrixProperty circuitBreakerSleepWindowInMilliseconds;
//是否启用熔断器,默认true. 启动
private final HystrixProperty circuitBreakerEnabled;
//默认:50%。当出错率超过50%后熔断器启动
private final HystrixProperty circuitBreakerErrorThresholdPercentage;
//是否强制开启熔断器阻断所有请求,默认:false,不开启。置为true时,所有请求都将被拒绝,直接到fallback
private final HystrixProperty circuitBreakerForceOpen;
//是否允许熔断器忽略错误,默认false, 不开启
private final HystrixProperty circuitBreakerForceClosed;
/* --------------信号量相关------------------*/
//使用信号量隔离时,命令调用最大的并发数,默认:10
private final HystrixProperty executionIsolationSemaphoreMaxConcurrentRequests;
//使用信号量隔离时,命令fallback(降级)调用最大的并发数,默认:10
private final HystrixProperty fallbackIsolationSemaphoreMaxConcurrentRequests;
/* --------------其他------------------*/
//使用命令调用隔离方式,默认:采用线程隔离,ExecutionIsolationStrategy.THREAD
private final HystrixProperty executionIsolationStrategy;
//使用线程隔离时,调用超时时间,默认:1秒
private final HystrixProperty executionIsolationThreadTimeoutInMilliseconds;
//线程池的key,用于决定命令在哪个线程池执行
private final HystrixProperty executionIsolationThreadPoolKeyOverride;
//是否开启fallback降级策略 默认:true
private final HystrixProperty fallbackEnabled;
// 使用线程隔离时,是否对命令执行超时的线程调用中断(Thread.interrupt())操作.默认:true
private final HystrixProperty executionIsolationThreadInterruptOnTimeout;
// 是否开启请求日志,默认:true
private final HystrixProperty requestLogEnabled;
//是否开启请求缓存,默认:true
private final HystrixProperty requestCacheEnabled; // Whether request caching is enabled.
- HystrixCollapserProperties
//请求合并是允许的最大请求数,默认: Integer.MAX_VALUE
private final HystrixProperty maxRequestsInBatch;
//批处理过程中每个命令延迟的时间,默认:10毫秒
private final HystrixProperty timerDelayInMilliseconds;
//批处理过程中是否开启请求缓存,默认:开启
private final HystrixProperty requestCacheEnabled;
- HystrixThreadPoolProperties
/* 配置线程池大小,默认值10个 */
private final HystrixProperty corePoolSize;
/* 配置线程值等待队列长度,默认值:-1 建议值:-1表示不等待直接拒绝,测试表明线程池使用直接决绝策略+ 合适大小的非回缩线程池效率最高.所以不建议修改此值。 当使用非回缩线程池时,queueSizeRejectionThreshold,keepAliveTimeMinutes 参数无效 */
private final HystrixProperty maxQueueSize;
2.2:Hystrix线程隔离策略与传播上下文
Hystrix的隔离策略有两种:分别是线程隔离和信号量隔离
THREAD(线程隔离):使用方式,HystrixCommand将会在单独的线程上执行,并发请求受线程池中的线程数量的限制
SEMAPHORE(信号量隔离):使用该方式,HystrixCommand将会在调用线程上执行,开销相对较小,并发请求受到信号量个数的限制
Hystrix中默认使用并推荐使用线程隔离,因为这种方式有一个除网络超时以外的额外保护层
2.3:Feign使用Hystrix
Spring Cloud默认已为Feign整合了Hystrix,只要Hystrix在项目中的classpath中,Feign默认就会使用断路器包裹所有方法
2.3.1:为Feign添加回退
复制项目microservice-consumer-movie-feign,将ArtifactId修改为microservice-consumer-movie-feign-hystrix-fallback
编写Feign接口
package com.itmuch.cloud.microserviceconsumermovie.client;
import com.itmuch.cloud.microserviceconsumermovie.entity.FeignClientFallback;
import com.itmuch.cloud.microserviceconsumermovie.entity.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @FeignClient注解中的microservice-provider-user是一个任意的客户端名称,用于创建Ribbon负载均衡器
* 还可以使用url属性指定请求的URL(URL可以是完整的URL或者主机名)例如
* @FeignClient(name = "microservice-provider-user",url="http://localhost:8000/")
* fallback指定回退类
*/
@FeignClient(name = "microservice-provider-user",fallback = FeignClientFallback.class)
public interface UserFeignClient {
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
public User findById(@PathVariable("id")Long id);
}
回退类
package com.itmuch.cloud.microserviceconsumermovie.entity;
import com.itmuch.cloud.microserviceconsumermovie.client.UserFeignClient;
import org.springframework.stereotype.Component;
@Component
public class FeignClientFallback implements UserFeignClient {
@Override
public User findById(Long id) {
User user = new User();
user.setId(-1L);
user.setUsername("默认用户");
return user;
}
}
启动测试
正常
关闭提供者
2.3.3:通过Fallback Factory检查回退原因
1.复制项目microservice-consumer-movie-feign,将ArtifactId修改为microservice-consumer-movie-feign-hystrix-fallback-factory
2.UserFeignClient改为如下内容
package com.itmuch.cloud.microserviceconsumermovie.client;
import com.itmuch.cloud.microserviceconsumermovie.entity.FeignClientFallbackFactory;
import com.itmuch.cloud.microserviceconsumermovie.entity.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @FeignClient注解中的microservice-provider-user是一个任意的客户端名称,用于创建Ribbon负载均衡器
* 还可以使用url属性指定请求的URL(URL可以是完整的URL或者主机名)例如
* @FeignClient(name = "microservice-provider-user",url="http://localhost:8000/")
*/
@FeignClient(name = "microservice-provider-user",fallbackFactory = FeignClientFallbackFactory.class)
public interface UserFeignClient {
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
public User findById(@PathVariable("id")Long id);
}
package com.itmuch.cloud.microserviceconsumermovie.entity;
import com.itmuch.cloud.microserviceconsumermovie.client.UserFeignClient;
import feign.hystrix.FallbackFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class FeignClientFallbackFactory implements FallbackFactory<UserFeignClient> {
private static final Logger logger = LoggerFactory.getLogger(FeignClientFallbackFactory.class);
@Override
public UserFeignClient create(Throwable cause) {
return new UserFeignClient() {
@Override
public User findById(Long id) {
//日志最好放在各个fallback方法中。而不要直接放在create方法中
//否则在引起启东时,就会打印该日志
FeignClientFallbackFactory.logger.info("fallback;reason was:",cause);
User user = new User();
user.setId(-1L);
user.setUsername("默认用户");
return user;
}
};
}
}
启动测试:
不知道为什么,没测出想要的结果
2019-10-28 15:18:38.658 INFO 19832 --- [ HystrixTimer-3] c.i.c.m.e.FeignClientFallbackFactory : fallback;reason was:
2.3.4:为Feign禁用Hystrix
在Spring cloud中,只要Hystrix在项目的classpath中,Feign就会使用断路器包裹Feign客户端的所有方法,这样虽然方便,但很多场场景下并不需要,所以如何让Feign禁用Hystrix
为指定Feign客户端禁用Hystrix
借助Feign的自定义配置,可轻松为指定名称的Feign客户端禁用Hystrix
@Configuration
public class FeignDisableHystrixConfiguration{
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder(){
return Feign.builder();
}
}
想要禁用Hystrix的@FeignClient引用该配置类即可
@FeignClient(name = "user",configuration = FeignDisableHystrixConfiguration.class)
public interface UserFeignClient {
}
全局禁用Hystrix
也可以为Feign全局禁用Hystrix.只需在application.yml中配置feign.hystrix.enabled = false即可