订单服务使用Hystrix

2020-04-07  本文已影响0人  换煤气哥哥

起因:某次抽奖活动,开发人员错误调用了用户服务遗弃的旧接口,旧接口有大对象操作非常慢。且抽奖接口活动期间有一定并发量,导致大量线程同步阻塞在调用处,差点导致整个抽奖服务不可用。这就是

服务雪崩效应 或者叫级联效应

一、文中为模拟代码,订单服务要依次调用库存服务、用户服务

    @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作

超时降级、不可用降级、调用报错熔断、限流熔断,降级返回缺省结果也还会异步执行方法逻辑,熔断直接返回缺省结果不执行方法逻辑

上一篇 下一篇

猜你喜欢

热点阅读