修订日期 | 内容 |
---|---|
2021-2-22 | 初稿 |
4-Spring Cloud微服务快速搭建-熔断降级(解决熔断雪崩)-Hystrix,Sentinel使用
简述
什么是雪崩?
在微服务架构中服务之间会相互调用,如果一个服务不能及时响应,将会导致其他服务阻塞,若此时大量请求涌入将会导致容器的线程资源耗尽,导致服务瘫痪。服务与服务之前的依赖性将会造成连锁反应,造成严重后果,这种故障称之为”雪崩“。
什么时熔断降级?
类似家庭电路中的保险丝,当电流过大时,保险丝会熔化(这就是我们说到的跳闸,小时候电压不稳,家里用到大功率电器时常常会遇到),而不会影响到其他家庭的用电。这种为了保护整体服务可用性而做出的局部牺牲叫做”熔断降级“。
Hystrix如何实现熔断降级?
主要通过如下几点实现熔断降级
- 跳闸机制:当错误率达到一定的阈值时,自动跳闸,停止该请求一段时间。
- 回退机制:当断路时,执行回退逻辑,回退逻辑由开发人员自己定义,如返回一个默认值。
- 自我修复:当断路一段时间后,自动”半开"状态,尝试让一部分请求进入。
Hystrix项目整合
在RestTemplate中使用Hystrix
整合步骤
1.引入maven依赖
<!-- 引入hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.注解激活hystrix@EnableCircuitBreaker
@SpringBootApplication
// 激活客户端 ,新版本可以省略
//@EnableEurekaClient,@EnableDiscoveryClient
// 激活feign调用
@EnableFeignClients
@EnableCircuitBreaker
public class OrderServiceApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(OrderServiceApplication.class).run(args);
}
//创建restTemplate,注入springbean容器
@Bean
@LoadBalanced //开启负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
3.开发回退代码逻辑,注意方法参数与返回值与正常方法保持一致
//回退代码逻辑,方法参数与返回值保持一致
public Object queryRollback(Long id){
return "触发熔断回退";
}
4.在正常调用的方法中使用注解@HystrixCommand
指定回退方法
@RequestMapping(value = "/query/{id}")
@HystrixCommand(fallbackMethod = "queryRollback")
public Object query(@PathVariable Long id){
Object obj = restTemplate.getForObject("http://express-service/express/query/"+1, String.class);
return "查询成功订单ID:"+id+"物流信息:"+obj;
}
5.其他设置
-
修改熔断超时时间
通过以上4步即完成,我们还可以修改hystrix的超时时间,(默认1s)
#指定hystrix熔断方法超时时间,默认1s
hystrix:
command.default.execution.isolation.thread.timeoutInMilliseconds: 2000
# feign调用超时时间
ribbon:
ReadTimeout: 3000
SocketTimeout: 3000
- 配置统一的降级方法
- 删除
@HystrixCommand(fallbackMethod = "queryRollback")
中指定的fallbackMethod方法,直接使用@HystrixCommand
即可 - 创建一个统一的降级方法(方法参数与返回值无要求)
public Object defaultFallBack(){
return "指定的统一默认defaultFallback方法";
}
- 在类中使用注解
@DefaultProperties(defaultFallback = "defaultFallBack")
@RequestMapping("/order2")
@RestController
// 指定统一的降级方法,@HystrixCommand不需要指定降级方法,如果指定将会覆盖默认的降级方法
@DefaultProperties(defaultFallback = "defaultFallBack")
public class Order2Controller {
@RequestMapping(value = "/query/{id}/{time}")
@HystrixCommand
public Object queryTime(@PathVariable Long id,@PathVariable Integer time){
logger.info("queryTime-id:{}",id);
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "查询成功订单ID:"+id+"本地物流信息:";
}
}
在Feign中使用Hystrix
关于feign的整合请参考上一篇文章:3-Spring Cloud微服务快速搭建-Feign<OpenFeign>整合
整合步骤
- 不需要单独引入Hystrix依赖,因为feign中已经引入hystrix
- 配置文件中开启feign对Hystrix的支持,默认关闭
feign:
# 开启hystrix,默认关闭
hystrix:
enabled: true
- 在feign接口中实现一个降级的方法
@Component
public class ExpressFeignFallBack implements ExpressFeign {
private Logger logger = LoggerFactory.getLogger(ExpressFeignFallBack.class);
@Override
public String query(Long id) {
logger.info("ExpressFeign-query降级方法id:{}",id);
return "触发降级方法";
}
}
4.在feign接口指定降级类fallback的类,指向刚刚创建的类即可
@FeignClient(name = "express-service",fallback = ExpressFeignFallBack.class)
@RequestMapping("/express")
public interface ExpressFeign {
@RequestMapping(value = "/query/{id}")
String query(@PathVariable Long id);
}
测试启动报错(坑点)
测试,启动发现出现如下错误类似这个方法已存在的错误
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘requestMappingHandlerMapping’ defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration E n a b l e W e b M v c C o n f i g u r a t i o n . c l a s s ] : I n v o c a t i o n o f i n i t m e t h o d f a i l e d ; n e s t e d e x c e p t i o n i s j a v a . l a n g . I l l e g a l S t a t e E x c e p t i o n : A m b i g u o u s m a p p i n g . C a n n o t m a p ′ o r g . c h e n g l j . o r d e r . r e s t . f e i g n . E x p r e s s F e i g n ′ m e t h o d c o m . s u n . p r o x y . EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'org.chenglj.order.rest.feign.ExpressFeign' method com.sun.proxy. EnableWebMvcConfiguration.class]:Invocationofinitmethodfailed;nestedexceptionisjava.lang.IllegalStateException:Ambiguousmapping.Cannotmap′org.chenglj.order.rest.feign.ExpressFeign′methodcom.sun.proxy.Proxy80#query(Long)
to { /express/query/{id}}: There is already ‘expressFeignFallBack’ bean method
坑点解决方案
注意上面的feign接口类上使用了@RequestMapping("/express")
注解,
在由熔断降级指定时不能再类上使用该注解,需要做如下调整:
@FeignClient(name = "express-service",fallback = ExpressFeignFallBack.class)
//@RequestMapping("/express") 使用熔断降级时不能子类上使用该注解,完整的注解请再方法中指定
public interface ExpressFeign {
//将完整路径url写在方法上
@RequestMapping(value = "/express/query/{id}")
String query(@PathVariable Long id);
}
Hystrix替换方案Alibaba Sentinel
由于Sentinel已经不在积极维护
整合步骤
- I. Sentinel 提供了两种配置限流规则的方式:代码配置 和 控制台配置。如采用控制台配置
直接下载:官方下载 Sentinel 控制台- II.启动控制台,执行 Java 命令 java -jar sentinel-dashboard.jar完成 Sentinel 控制台的启动。 控制台默认的监听端口为 8080。Sentinel 控制台使用 Spring Boot 编程模型开发,如果需要指定其他端口,请使用 Spring Boot 容器配置的标准方式,详情请参考 Spring Boot 文档。
启动控制台后localhost:8080访问即可,默认没有用户名密码
更多sentinel详细内容请参考官方文档
- 在父工程中引入alibaba的相关依赖
如果需要使用 Spring Cloud Hoxton 版本,请在 dependencyManagement 中添加如下内容
<!-- 引入alibaba相关依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
版本依赖关系请查看官方文档:spring cloud alibaba 依赖关系
- 在子工程中引入sentinel依赖
<!--引入alibaba sentinel依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- 在配置文件中配置sentinel的控制台地址
cloud:
# alibaba sentinel 配置控制台地址
sentinel:
transport:
dashboard: localhost://8080
- 在调用方法上使用注解
@SentinelResource(blockHandler = "",fallback = "")
- blockHanler:熔断时的降级方法
- fallback:抛出异常时的降级方法
对Feign的支持
# 开启对sentinel的支持
feign:
sentinel:
enabled: true
其他方式与Hystrix保持一致即可
启动报错
Requested bean is currently in creation: Is there an unresolvable circular reference?] with root cause
spring-cloud-starter-alibaba-sentinel:2.2.5.RELEASE
spring cloud:Hoxton.SR10(Hoxton.SR9以下都正常,Hoxton.SR10与2020.x版本都不行)存在这个Bug,看到有人已经提出issue,并且已经确认,到目前为止暂未修复(着急的可以降一下spring cloud版本)
https://github.com/alibaba/spring-cloud-alibaba/issues/1974
对restTemplate的支持
- 使用
@SentinelRestTemplate
@Bean
@LoadBalanced //开启负载均衡
/**
* 开启sentinel对RestTemplate的支持
* blockHandler: 熔断限流
* fallback:异常降级
*/
@SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtils.class)
public RestTemplate restTemplate(){
return new RestTemplate();
}
- 创建异常类
import com.alibaba.cloud.sentinel.rest.SentinelClientHttpResponse;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpResponse;
public final class ExceptionUtils {
//方法必须是静态,且参数一定要对应
public static ClientHttpResponse handleException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException exception) {
return new SentinelClientHttpResponse("熔断限流降级");
}
}