章节索引 :

实际业务场景下 Hystrix 资源隔离实战

1. 前言

我们知道,服务资源隔离分为两种实现方式,分别是基于线程池隔离的服务资源隔离、基于信号量隔离的服务资源隔离。在本节中,我将继续为大家介绍,在真实业务场景下的,服务资源隔离的应用方法及代码实现。

本节主要内容:

  • 服务资源隔离真实业务场景描述;

  • 业务场景实现思路分析与实操。

2. 服务资源隔离真实业务场景描述

业务场景描述

有这样一个真实的业务场景:在某大厂的订单与支付模块,当有用户下了订单之后,需要在支付模块进行支付,支付动作完成之后,支付模块会将支付完成的结果返回给订单模块来通知用户,该订单是否支付成功,即商品是否已经成功购买了。

在微服务分布式架构模式下,上述业务场景中出现了一种异常现象:当用户下了订单之后,在支付模块进行支付时,系统一直没有响应,无论是否成功支付,用户都收不到任何通知信息。

程序员在排查对应的业务实现代码时,证实了业务实现代码没有问题,这就导致无法定位问题所在。最终经过几名同事一起排查,发现是订单模块与支付模块之间进行数据传输时,支付模块收到了订单模块传递过来的数据,但是由于服务器高压工作,导致支付模块始终无法处理该支付请求,这就导致系统一直没有响应。

问题原因分析

在解决问题之前,我们首先来分析一下这种问题产生的原因。

在前面我们介绍什么是 Hystrix 资源隔离小节中,我为大家阐述了在我们的 Web 项目中,进程与线程之间的关系。我们知道,在一般情况下,一个 Web 项目中只有一个工作线程来负责处理用户调用的请求和服务,当该工作线程所负责的请求处理缓慢时,该线程就会一直处理当前的请求,导致后续请求只能等待处理,这就是我们说的雪崩现象。

雪崩效应产生原理

在微服务分布式架构模式下,由于我们没有对线程进行处理,至此在处理所有业务请求时,扔是只有一个工作线程,这就导致上述业务场出现了我们所说的雪崩现象,不过还好,这种雪崩现象比较轻微,只影响到了一个业务模块。

很多时候,当我们的项目架构演变为基于微服务的分布式架构时,服务器也需要同步进行更新,有很多企业为了节约成本,则只更新很少数量的服务器,或者压根就不更新服务器,这就导致经常会出现由于服务器高压工作而出现的请求处理缓慢,或请求无法继续处理的情况。

3. 业务场景实现思路分析与实操

实现思路分析

鉴于上述业务场景中所描述的问题,我们只要为每个业务模块分配不同的工作线程,使各模块间不再一同共用一个工作线程,就可以有效解决上述问题。

当我们通过技术手段为每一个业务模块都分配了不同的工作线程之后,各模块的业务处理操作都会有专门的工作线程来完成,不会再出现各模块共用一个工作线程的情况。如果一个模块中的请求处理出现问题而等待,由于我们分配了不同的工作线程,所以这种情况不会影响到其他模块,这就解决了上述业务场景中出现的问题。

Hystrix 提供了通过线程池或信号量隔离的方式来对服务资源进行隔离,以解决雪崩现象的发生,接下来让我们分别来看一下代码实现。

实操

以线程池隔离为例,我们先给订单服务配置资源隔离:

@RequestMapping("make_order.do")
@ResponseBody
@HystrixCommand(threadPoolKey = "userMakeOrderThread")
public CommonResponse<String> makeOrder(@RequestBody("order")Order order, @RequestBody("user")User user){
    return orderService.makeOrder(order, user);
}

代码解释

第 3 行,我们用户下订单服务中通过添加 HystrixCommand 注解的 threadPoolKey 属性来为用户下订单服务单独分配了一个名为 userMakeOrderThread 的线程池,当我们再有用户下订单的请求需要处理时,就会使用这个线程池中的线程。

同样的方法,我们为用户支付服务配置资源隔离:

@RequestMapping("aliPay.do")
@ResponseBody
@HystrixCommand(threadPoolKey = "userAliPayThread")
public CommonResponse<String> aliPay(@RequestBody("shipping")Shipping shipping, @RequestBody("user")User user){
    return payService.aliPay(shipping, user);
}
    

可以看到,我们为支付模块分配了一个名为 userAliPayThread 的线程池。

基于信号量隔离的服务隔离的配置,和上述基于线程池隔离的配置大同小异,下面我将关键代码放到下方,各位同学只需要替换掉上述的线程池配置即可。

以用户下订单服务为例:

@HystrixCommand(fallbackMethod="makeOrderFailed", commandProperties = {
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "50")
})

代码解释

第 3- 8 行,使用信号量隔离需要显式声明服务资源隔离策略,SEMAPHORE 表示使用信号量隔离策略。

Tips:
1. 在实际项目开发中,像这种订单服务和支付服务互相依赖的业务场景不在少数,各位同学在工作时,一定要在这种业务场景中配置好服务资源隔离,不要等在线上出现问题之后再解决,那就晚了;
2. 线程池隔离和信号量隔离之间的区别有很多,这里就不做介绍了,希望各位同学可以自行查阅,他们之间的区别可以帮助你确定不同业务场景下采用哪种隔离方式比较合适。

4. 小结

本节内容概览

本小节以一个真实业务场景下的不同业务模块间协作服务为背景,为大家介绍了如何在真实业务场景下配置 Hystrix 的服务资源隔离,并且做了代码实操,针对容易出现问题的地方,也做了注意事项的补充,希望同学们可以对真实业务场景下,服务资源隔离的应用方法有所了解。