〇、Hystrix的解决策略和预防措施:
熔断降级是解决远程调用已经出现问题的解决方案。
请求合并 请求缓存 线程池隔离 信号量隔离都是预防性措施
使用 Hystrix启动类上需要添加注解开启注解支持:
@EnableHystrix @EnableCircuitBreaker
测试Hystrix需要的依赖坐标如下:
java<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.12.RELEASE</version> </parent> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR12</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.22</version> <!-- 请使用最新的版本号 --> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 容灾处理依赖。 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> </dependencies>
一、熔断降级
熔断是在远程调用的服务器出现问题后,访问不会再进行远程调用甚至调用方法get()。
降级是访问不会再进行远程调用,而是转而调用getDownGrade()方法返回托底数据。
java
// 熔断 熔断熔断熔断熔断熔断熔断熔断 熔断 熔断 熔断
@HystrixCommand(fallbackMethod = "getDownGrade",commandProperties = {
@HystrixProperty(name = HystrixPropertiesManager
.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS,
value = "5000"), // 统计周期,默认10秒
@HystrixProperty(name = HystrixPropertiesManager
.CIRCUIT_BREAKER_ENABLED,
value = "true"), // 是否开启熔断,默认true
@HystrixProperty(name = HystrixPropertiesManager
.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,
value = "2"), // 统计周期内,错误几次,开启熔断, 默认20
@HystrixProperty(name = HystrixPropertiesManager
.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,
value = "50"), // 统计周期内,错误百分比达到多少,开启熔断, 默认50
@HystrixProperty(name = HystrixPropertiesManager
.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,
value = "3000"), // 开启熔断后,多少毫秒不访问远程服务,默认5000毫秒
@HystrixProperty(name = HystrixPropertiesManager
.CIRCUIT_BREAKER_FORCE_OPEN,
value = "false"), // 是否强制开启熔断器, 默认false
@HystrixProperty(name = HystrixPropertiesManager
.CIRCUIT_BREAKER_FORCE_CLOSED,
value = "false") // 是否强制关闭熔断器, 默认false
})
public String get(){
String forObject = restTemplate.getForObject("http://application-service/getNoParams", String.class);
return forObject;
}
public String getDownGrade(){
return "服务器繁忙";
}
二、请求合并
远程调用的服务器端c2有两个方法。一个是针对单个id的,还有一个是针对多个id的相同操作的合并
多个根据id查询user的请求发送至要进行 远程调用的服务器端c1,这时开启请求处理的多个线程阻塞,并将多个id合并为一个List发送一个请求到c2.然后返回的值放在相应线程的FutureTask中。
java//请求合并 请求合并 请求合并 请求合并请求合并 请求合并请求合并 请求合并请求合并 请求合并请求合并 请求合并请求合并 请求合并 @HystrixCollapser(batchMethod = "getUserByIds", scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL, collapserProperties = { @HystrixProperty(name = HystrixPropertiesManager.MAX_REQUESTS_IN_BATCH, value = "2"), // 最多合并多少个请求 @HystrixProperty(name = HystrixPropertiesManager.TIMER_DELAY_IN_MILLISECONDS, value = "2000") // 最多等待多少毫秒 } ) public Future<User> getUserById(Integer id){ System.out.println("正常请求,无请求合并:id="+id); restTemplate.postForObject("http://application-service/getUserById", id, User.class); return null; } @HystrixCommand public List<User> getUserByIds(List<Integer>ids) throws JsonProcessingException { List<LinkedHashMap> list = restTemplate.postForObject("http://application-service/getUserByIds", ids, List.class); System.out.println("执行合并方法"); List<User> list1 = new ArrayList<>(); for (LinkedHashMap linkedHashMap : list) { ObjectMapper objectMapper = new ObjectMapper(); String s = objectMapper.writeValueAsString(linkedHashMap); User user = objectMapper.readValue(s, User.class); list1.add(user); } return list1; }
三、请求缓存
这里使用spring cache技术连接redis数据库,在配置文件中配置redis的地址。
@Cacheable()注解自动将方法的返回值存储redis中。
启动类上添加@EnableCaching 开启相关注解支持
java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
java
//请求缓存 请求缓存 请求缓存请求缓存请求缓存请求缓存
@Cacheable(cacheNames = "getForObject",key = "#id")
public String cache(Integer id){
System.out.println("cache方法执行");
String forObject = restTemplate.getForObject("http://application-service/getNoParams", String.class);
return forObject;
}
四、线程池隔离
和船体底部的小船舱一样,底部破了仍然有隔板挡着。
线程池是事先针对远程调用准备的多个线程。
如果不准备线程池,一个远程调用崩了,导致线程阻塞不能释放最终会导致整个服务崩掉。
java
//线程池隔离 线程切换 线程池隔离 线程切换 线程池隔离 线程切换线程池隔离 线程切换
@HystrixCommand(groupKey = "TbItem",
commandKey = "selectItemService",
threadPoolKey = "pool-name",
threadPoolProperties = {
@HystrixProperty(name = HystrixPropertiesManager.CORE_SIZE,
value = "3"), // 线程池容量
@HystrixProperty(name = HystrixPropertiesManager.KEEP_ALIVE_TIME_MINUTES,
value = "5"), // 线程空闲时,最大存活时间是多少分钟
@HystrixProperty(name = HystrixPropertiesManager.MAX_QUEUE_SIZE,
value = "5"), // 线程池占满时,最多由多少个请求阻塞等待
@HystrixProperty(name = HystrixPropertiesManager
.QUEUE_SIZE_REJECTION_THRESHOLD,
value = "5") // 当阻塞队列MAX_QUEUE_SIZE占满时,可以由多少个
// 请求同时阻塞等待后续处理。
}
)
public String selectItemService() {
System.out.println("selectItemService---"+Thread.currentThread().getName());
String forObject = restTemplate.getForObject("http://application-service/getNoParams", String.class);
return forObject;
}
五、信号量隔离
在一定时间内只允许有限个线程远程调用,其他线程降级。
进行远程调用的线程会拿到许可证,线程结束释放许可证。
java
//信号量隔离 信号量隔离信号量隔离信号量隔离
//在一定时间内只允许2个线程远程调用,其他线程降级
@HystrixCommand(fallbackMethod = "getDownLoad",
commandProperties = {
@HystrixProperty(name = HystrixPropertiesManager
.EXECUTION_ISOLATION_STRATEGY,
value = "SEMAPHORE"), // 隔离方案。默认线程池隔离。 THREAD | SEMAPHORE
@HystrixProperty(name = HystrixPropertiesManager
.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS,
value = "1"), // 最大信号量
@HystrixProperty(name = HystrixPropertiesManager
.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value = "5000") // 超时时间。信号量隔离中无效,线程池隔离中有效。默认1秒
}
)
public String sign(){
String forObject = restTemplate.getForObject("http://application-service/getNoParams", String.class);
return forObject;
}
public String getDownLoad(){
return "服务器繁忙";
}
六、openfeign整合Hystrix
1)openfeign中使用Hystrix很简单,只需要修改配置文件即可,因为内置好了Hystrix的基础功能容灾处理。
可以看到如果需要Hystrix的其他预防措施还是需要引入Hystrix依赖坐标
2)修改配置文件:
javafeign: hystrix: enabled: true hystrix: # hystrix 容灾配置 command: # hystrix 命令配置,就是具体的容灾方案 default: # 默认环境,相当于全局范围,后续的配置,参考HystrixPropertiesManager中的常量配置 circuitBreaker: # 熔断配置, 常用。 其他配置不常用。 enabled: true requestVolumeThreshold: 2 sleepWindowInMilliseconds: 2000 errorThresholdPercentage: 50