使用Hystrix进行微服务降级管理

前言:目前咱们的项目是微服务架构,基于dubbo框架,服务之间的调用是经过rpc调用的。刚开始没有任何问题,项目运行健康、良好。但是过了一段时间,线上总有人反应查询订单失败,等过了一段时间才能查到。这是怎么回事呢?打开后台的日志一看出现了一些RpcException和TimeOutException,原来是远程调用超时了,可能某个服务在请求的高发期访问数据库异常,IO阻塞,返回接口异常了。后来这个问题愈来愈频繁,如何解决这个棘手的问题呢?前端

本篇博客的目录java

一:Hystrix是什么?spring

1.1:基本解释数据库

    Hystrix最开始由Netflix(看过美剧的都知道,它是一个美剧影视制做的巨头公司)开源的,后来由Spring Cloud Hystrix基于这款框架实现了断路器、线程隔离等一系列服务保护功能,该框架的目标在于经过控制访问远程系统、服务和第三方库的节点,从而延迟和故障提供更强大的容错能力。hystrix具有服务降级、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能。起到了微服务的保护机制,防止某个单元出现故障.从而引发依赖关系引起故障的蔓延,最终致使整个系统的瘫痪。缓存

1.2:断路器的概念springboot

    断路器自己是一个开关装置,用在电路上保护线路过载,当线路中有电器发生短路的时候。“断路器”可以及时切断故障,防止发生过载、发热甚至起火等严重后果。当分布式架构中,断路器模式起到的做用也是相似的。当某个服务发生故障的时候,经过断路器的故障监控向调用方返回一个错误响应,而不是长时间的线程挂机,无限等待。这样就不会使线程因故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。以下图是现实中的断路器,它是一个开关装置:服务器

二:Hystrix解决超时问题架构

  2.1:问题  app

      假设咱们前端提供了用户查询订单的功能,首先请求映射到OrderController,控制器经过调用服务orderService获取订单信息,前端传过来两个参数:一个是订单id,一个是用户id,orderService须要经过用户id调取用户服务来获取用户的相关信息返回给订单服务去组装信息,假设这里是经过http请求的,咱们有一个单独的工程叫作:userService部署在其余的服务器上。可是这个服务器宕机了,这时候订单服务调取用户信息就失败了,而后查询订单整个请求就失败了!由一个服务的宕机就致使整个查询都失败了,牵一发而动全身。流程见下图:框架

2.2:使用Hystrix进行服务降级

2.2.1:引入hystrix依赖 这里引入了spring-cloud-starter-netflix-hystrix,springboot的starter里面整合了hystrix

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.5.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

2.2.2:模拟订单请求 

 首先经过OrderController映射/order请求,获取前端传入的参数orderId和useId,而后调用orderDetailService方法,

@RestController
public class OrderController {

    @Resource
    private OrderService orderService;


    /**
     * 获取订单信息
     *
     * @param orderNo
     * @return
     */
    @PostMapping("/order")
    public ResultVo<OrderDetail> getOrderInfo(@RequestParam("orderId") Long orderNo, @RequestParam("userId") Long userId) {

        OrderDetail orderDetail = orderService.orderDetailService(orderNo, userId);
        ResultVo resultVo = new ResultVo<>();
        resultVo.setCode(100);
        resultVo.setMessage("请求成功");
        resultVo.setData(orderDetail);
        return resultVo;
    }
}

2.2.3:订单服务调取其余服务

    这里引入了RestTemplate,它是一个spring封装的http映射请求工具类,而后经过http请求访问url = "http://192.168.80.153:8070/user/getUser"获取用户名,将值给订单对象。不过假如在这其中发生了调用异常,请求用户服务异常的话,那么返回给前端就是一串空的订单信息,致使用户看到的订单为空。在使用hystrix以后,能够用@HystrixCommand(fallbackMethod = "orderFallBack")注解,在fallbackMethod中指定回退的方法,这里必须注意在@HystrixCommand上的方法其指定的回调方法必须和原方法的参数保持一致,这里包括参数类型、参数个数、参数顺序。咱们在回调用法中模拟去查询缓存数据,返回给订单。有人又要问了,若是查询缓存服务器再异常呢?不排除这种可能性。若是是这样的话,依然可使用@HystrixCommand注解在回调方法中,再指定其余的回调方法:

@Service
public class OrderService {
    @Autowired
    private RestTemplate restTemplate;
    /**
     * 根据订单id获取订单详情
     *
     * @param orderId
     * @param userId
     * @return
     */
    @HystrixCommand(fallbackMethod = "orderFallBack")
    public OrderDetail orderDetailService(Long orderId, Long userId) {

        if (Objects.isNull(orderId)) {
            return null;
        }

        OrderDetail orderDetail = OrderDBSource.getOrderDB().get(orderId);
        //调用user服务
        final String url = "http://192.168.80.153:8070/user/getUser";
        String userName = "";
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, userId, String.class);
        String returnContent = responseEntity.getBody();
        if (Objects.nonNull(responseEntity) && StrUtil.isNotEmpty(returnContent)) {
            userName = returnContent;
        }
        if (ObjectUtil.isNotNull(orderDetail)){
            orderDetail.setUserName(userName);
        }
        return orderDetail;
    }
    /**
     * 异常调用的回调方法
     *
     * @return
     */
    public OrderDetail orderFallBack(Long orderId, Long userId) {
        OrderDetail orderDetail = OrderDBSource.getOrderCache().get(orderId);
        final String unknown = "未知用户";
        orderDetail.setUserName(unknown);
        return orderDetail;
    }
}

2.3.4:模拟测试

 为了方便测试,首先咱们将请求服务暂时先注释,而后用postman测试看正常的返回应该是这样的,这里使用了备注为数据库获取的订单,代表它没有走回调方法,由于这里没有访问用户url获取用户信息,程序能够正常访问。我再放开

 加上获取用户服务的连接,实际上用户服务是没法访问到的,访问的话就会超时,超时会被hystrix捕捉到,而后走fallBack指定的方法,咱们来测试一下,能够看到实际上走的是缓存中查询到的订单,能够看到用户服务已经成功的降级了,降级后的订单信息虽然是缓存获取到的,可能会存在延时等问题(固然只要维护好缓存就能够避免这个问题)。可是比没有任何数据带来的用户一点会更好!

三:Hystrix的流程

    Hystrix实际上的工做原理是这样的:经过command来解耦请求与返回操做,在具体的实例中就是,Hystrix会对依赖的服务进行观察,经过command.toObservable调用返回一个观察的对象,同时发起一个事件,而后用Subscriber对接受到的事件进行处理。在command命令发出请求后,它经过一系列的判断,顺序依次是缓存是否命中、断路器是否打开、线程池是否占满,而后它才会开始对咱们编写的代码进行实际的请求依赖服务的处理,也就是Hystrix.run方法,若是在这其中任一节点出现错误或者抛出异常,它都会返回到fallback方法进行服务降级处理,当降级处理完成以后,它会将结果返回给,际的调用者,通过一系列流程处理的,它的具体工做流程以下:

 

 

 

四:总结

    本篇博客讲述了Hystrix是什么?而后解释了Hystrix如何进行服务降级处理以及简单的处理流程,讲到的内容是最为经常使用的功能,还有一些关于Hystrix的缓存、线程池的隔离技术等因为篇幅的缘由,没有详细的讲解到,不过做为一篇入门级的Hystrix教程博客是基本够的。在实际的开发中,如何保持服务的健壮性、服务的可用性、尽可能的减小bug,提高用户体验都是咱们开发者的使命,这条优化和提高之路永远没有尽头,go ahead!

参考资料《spring cloud微服务实战》

*若是你在本篇博客中,有任何疑问,均可以添加java学习交流群:618626589

相关文章
相关标签/搜索