美文网首页
订单服务使用Hystrix

订单服务使用Hystrix

作者: 换煤气哥哥 | 来源:发表于2020-04-07 17:41 被阅读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作

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

    相关文章

      网友评论

          本文标题:订单服务使用Hystrix

          本文链接:https://www.haomeiwen.com/subject/emnnphtx.html