起因:某次抽奖活动,开发人员错误调用了用户服务遗弃的旧接口,旧接口有大对象操作非常慢。且抽奖接口活动期间有一定并发量,导致大量线程同步阻塞在调用处,差点导致整个抽奖服务不可用。这就是
服务雪崩效应 或者叫级联效应
一、文中为模拟代码,订单服务要依次调用库存服务、用户服务
@ApiOperation(value = "下单")
@GetMapping("/create/{userId}")
public CommonResponse<Void> createOrder(@PathVariable(value = "userId") Long userId) {
logger.info("---调用库存服务,校验库存---");
logger.info("---调用用户服务,查询用户信息---");
UserDTO userInfo = accountService.getUserInfo(userId);
logger.info("---获得用户信息,并校验");
logger.info("---进行扣款,下单");
return ResultBuilder.buildSuccessResult(null);
}
将用户服务查询接口模拟成耗时5秒,订单服务设置ribbon.ReadTimeout=1000。显然会报错
Caused by: java.net.SocketTimeoutException: Read timed out
但是光靠超时报错,不止打断了正常业务,而且会导致大量下单线程阻塞住!!!
二、使用hystrix
还是有必要引入下熔断机制,为了不影响测试先把ribbon.ReadTimeout改为10秒吧
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
订单服务启动类加上@EnableCircuitBreaker或者@EnableHystrix
@Service
public class AccountService {
Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private AccountClient accountClient;
public UserDTO getUserInfo(Long userId) {
UserIdDTO dto = new UserIdDTO();
dto.setUserId(userId);
CommonResponse<UserDTO> response = accountClient.getUserInfo(dto);
logger.info("---调用用户服务,查询用户信息---{}", response);
if (response == null) {
throw new BizException(StatusCodeConstant.ACCOUNT_SERVICE_CALL_ERROR.getCode(),
StatusCodeConstant.ACCOUNT_SERVICE_CALL_ERROR.getDesc());
}
return response.getData();
}
}
@ApiOperation(value = "下单")
@GetMapping("/create/{userId}")
@HystrixCommand(fallbackMethod = "getUserInfoFallback", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "6000")
})
public CommonResponse<UserDTO> createOrder(@PathVariable(value = "userId") Long userId) {
logger.info("---调用库存服务,校验库存---");
logger.info("---调用用户服务,查询用户信息---");
UserDTO userInfo = accountService.getUserInfo(userId);
logger.info("---获得用户信息,并校验。-> userInfo {}", userInfo);
logger.info("---进行扣款,下单");
return ResultBuilder.buildSuccessResult(userInfo);
}
// 注意降级方法必须和调用方法的入参、返回参数一致
public CommonResponse<UserDTO> getUserInfoFallback(Long userId) {
UserDTO userDTO = new UserDTO();
userDTO.setUserId(-1L);
userDTO.setNickname("游客");
return ResultBuilder.buildSuccessResult(userDTO);
}
## Hystrix调用接口默认1秒超时,超时后会自动执行降级方法
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
三、测试
(1)服务不可用降级
kill掉用户服务
---调用库存服务,校验库存---
YWW-ACCOUNT using LB returned Server: yww.account.cn:9220 for request http:///user/find
The request for /v2/orders/create/100000551 takes 2007 ms.
2020-04-07 16:56:52,699 [hystrix-OrderV2Controller-3] DEBUG c.n.l.reactive.LoadBalancerCommand 314 - Got error java.net.ConnectException: Connection refused: connect when executed on server yww.account.cn:9220
(2)超时降级
设置超时时间为2秒
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
---调用库存服务,校验库存---
The request for /v2/orders/create/100000551 takes 2297 ms.
---调用用户服务,查询用户信息---CommonReponse [code=200, message=success, data=UserDTO{userId=100000551, avatar='/avatar/default_avatar.png', nickname='富豪100000551', showVoice='null', sex='null', accountId='test_100000551', token='f733008ba897d2409a1ec8afa997583c'}]
---获得用户信息,并校验。-> userInfo UserDTO{userId=100000551, avatar='/avatar/default_avatar.png', nickname='富豪100000551', showVoice='null', sex='null', accountId='test_100000551', token='f733008ba897d2409a1ec8afa997583c'}
---进行扣款,下单
(3)调用报错熔断
降级是还会执行logger.info并返回fallbackMethod结果(甚至连accountService.getUserInfo后的逻辑也会异步去继续执行),熔断是直接createOrder方法都不执行了。
不过两者都会返回缺省结果。
## 当在配置时间窗口内达到此数量的失败后,进行短路。默认20个
hystrix.command.default.circuitBreaker.requestVolumeThreshold=3
## 短路多久以后开始尝试是否恢复,默认5s
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5
在用户服务设置报错代码,5秒内连续调用3次,触发熔断直接返回fallbackMethod结果。过完5秒,进入半开状态,调用成功一次,则关闭熔断器,还是调用失败则继续打开熔断器
(4)限流熔断(线程隔离或者使用信号量)
只有2个线程同步执行createOrder方法
如果不配置maxQueueSize、queueSizeRejectionThreshold 则如果同时打过来3个线程,必然有一个调用降级方法。和超不超时无关
如果配置了maxQueueSize、queueSizeRejectionThreshold 则取其中较小的参数作为队列大小。如下同时打过去13个线程,必然有一个调用降级方法(直接熔断,不会调用createOrder方法),队列里的10个线程则会慢慢消费
@HystrixCommand(fallbackMethod = "getUserInfoFallback",
groupKey = "orderGroup",
threadPoolKey = "orderThreadPool",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "2"),
@HystrixProperty(name = "maxQueueSize", value = "10"),
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "11")
}
)
总结:
服务雪崩效应 或者叫级联效应,可以使用hystrix作
超时降级、不可用降级、调用报错熔断、限流熔断,降级返回缺省结果也还会异步执行方法逻辑,熔断直接返回缺省结果不执行方法逻辑
网友评论