基于 Hystrix 信号量机制实现资源隔离

基于 Hystrix 信号量机制实现资源隔离

Hystrix 里面核心的一项功能,其实就是所谓的资源隔离,要解决的最最核心的问题,就是将多个依赖服务的调用分别隔离到各自的资源池内。避免说对某一个依赖服务的调用,由于依赖服务的接口调用的延迟或者失败,致使服务全部的线程资源所有耗费在这个服务的接口调用上。一旦说某个服务的线程资源所有耗尽的话,就可能致使服务崩溃,甚至说这种故障会不断蔓延。java

Hystrix 实现资源隔离,主要有两种技术:web

  • 线程池
  • 信号量

默认状况下,Hystrix 使用线程池模式。缓存

前面已经说过线程池技术了,这一小节就来讲说信号量机制实现资源隔离,以及这两种技术的区别与具体应用场景。tomcat

信号量机制

信号量的资源隔离只是起到一个开关的做用,好比,服务 A 的信号量大小为 10,那么就是说它同时只容许有 10 个 tomcat 线程来访问服务 A,其它的请求都会被拒绝,从而达到资源隔离和限流保护的做用。网络

线程池与信号量区别

线程池隔离技术,并非说去控制相似 tomcat 这种 web 容器的线程。更加严格的意义上来讲,Hystrix 的线程池隔离技术,控制的是 tomcat 线程的执行。Hystrix 线程池满后,会确保说,tomcat 的线程不会由于依赖服务的接口调用延迟或故障而被 hang 住,tomcat 其它的线程不会卡死,能够快速返回,而后支撑其它的事情。app

线程池隔离技术,是用 Hystrix 本身的线程去执行调用;而信号量隔离技术,是直接让 tomcat 线程去调用依赖服务。信号量隔离,只是一道关卡,信号量有多少,就容许多少个 tomcat 线程经过它,而后去执行。ide

适用场景性能

  • 线程池技术,适合绝大多数场景,好比说咱们对依赖服务的网络请求的调用和访问、须要对调用的 timeout 进行控制(捕捉 timeout 超时异常)。
  • 信号量技术,适合说你的访问不是对外部依赖的访问,而是对内部的一些比较复杂的业务逻辑的访问,而且系统内部的代码,其实不涉及任何的网络请求,那么只要作信号量的普通限流就能够了,由于不须要去捕获 timeout 相似的问题。

信号量简单 Demo

业务背景里,比较适合信号量的是什么场景呢?this

好比说,咱们通常来讲,缓存服务,可能会将一些量特别少、访问又特别频繁的数据,放在本身的纯内存中。spa

举个栗子。通常咱们在获取到商品数据以后,都要去获取商品是属于哪一个地理位置、省、市、卖家等,可能在本身的纯内存中,好比就一个 Map 去获取。对于这种直接访问本地内存的逻辑,比较适合用信号量作一下简单的隔离。

优势在于,不用本身管理线程池啦,不用 care timeout 超时啦,也不须要进行线程的上下文切换啦。信号量作隔离的话,性能相对来讲会高一些。

假如这是本地缓存,咱们能够经过 cityId,拿到 cityName。

public class LocationCache {
    private static Map<Long, String> cityMap = new HashMap<>();

    static {
        cityMap.put(1L, "北京");
    }

    /** * 经过cityId 获取 cityName * * @param cityId 城市id * @return 城市名 */
    public static String getCityName(Long cityId) {
        return cityMap.get(cityId);
    }
}
复制代码

写一个 GetCityNameCommand,策略设置为信号量。run() 方法中获取本地缓存。咱们目的就是对获取本地缓存的代码进行资源隔离。

public class GetCityNameCommand extends HystrixCommand<String> {

    private Long cityId;

    public GetCityNameCommand(Long cityId) {
        // 设置信号量隔离策略
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GetCityNameGroup"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)));

        this.cityId = cityId;
    }

    @Override
    protected String run() {
        // 须要进行信号量隔离的代码
        return LocationCache.getCityName(cityId);
    }
}
复制代码

在接口层,经过建立 GetCityNameCommand,传入 cityId,执行 execute() 方法,那么获取本地 cityName 缓存的代码将会进行信号量的资源隔离。

@RequestMapping("/getProductInfo")
@ResponseBody
public String getProductInfo(Long productId) {
    HystrixCommand<ProductInfo> getProductInfoCommand = new GetProductInfoCommand(productId);

    // 经过command执行,获取最新商品数据
    ProductInfo productInfo = getProductInfoCommand.execute();

    Long cityId = productInfo.getCityId();

    GetCityNameCommand getCityNameCommand = new GetCityNameCommand(cityId);
    // 获取本地内存(cityName)的代码会被信号量进行资源隔离
    String cityName = getCityNameCommand.execute();

    productInfo.setCityName(cityName);

    System.out.println(productInfo);
    return "success";
}
复制代码
相关文章
相关标签/搜索