• Spring Cloud Hystrix熔断器隔离方案


      Hystrix组件提供了两种隔离的解决方案:线程池隔离和信号量隔离。两种隔离方式都是限制对共享资源的并发访问量,线程在就绪状态、运行状态、阻塞状态、终止状态间转变时需要由操作系统调度,占用很大的性能消耗;而信号量是在访问共享资源时,进行tryAcquire,tryAcquire成功才允许访问共享资源。
     
    线程池隔离
         不同的业务线之间选择用线程池隔离,降低互相影响的概率。设置隔离策略为线程池隔离:
    .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD));在Hystrix内部,是根据properties.executionIsolationStrategy().get()这个字段判断隔离级别。如在getRunObservableDecoratedForMetricsAndErrorHandling这个方法中会先判断是不是线程池隔离,如果是就获取线程池,如果不是则进行信号量隔离的操作。
         如果是线程池隔离,还需要设置线程池的相关参数如:线程池名字andThreadPoolKey , coreSize(核心线程池大小) , KeepAliveTimeMinutes(线程存存活时间),MaxQueueSize(最大队列长度),QueueSizeRejectionThreshold(拒绝执行的阀值)等等。
    .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getCoreSize())
                                        .withKeepAliveTimeMinutes(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getKeepAliveSeconds())
                                        .withMaxQueueSize(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getMaxQueueSize())
                                        .withQueueSizeRejectionThreshold(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getQueueSizeRejectionThreshold()))
    threadPoolKey 也是线程池的名字的前缀,默认前缀是 hystrix 。在Hystrix中,核心线程数和最大线程数是一致的,减少线程临时创建和销毁带来的性能开销。线程池的默认参数都在HystrixThreadPoolProperties中,重点讲解一下参数queueSizeRejectionThreshold 和maxQueueSize 。queueSizeRejectionThreshold默认值是5,允许在队列中的等待的任务数量。maxQueueSize默认值是-1,队列大小。如果是Fast Fail 应用,建议使用默认值。线程池饱满后直接拒绝后续的任务,不再进行等待。代码如下HystrixThreadPool类中:
    @Override
            public boolean isQueueSpaceAvailable() {
                if (queueSize <= 0) {
                    // we don't have a queue so we won't look for space but instead
                    // let the thread-pool reject or not
                    return true;
                } else {
                    return threadPool.getQueue().size() < properties.queueSizeRejectionThreshold().get();
                }
            }
     
    线程池一旦创建完成,相关参数就不会更改,存放在静态的ConcurrentHashMap中,key是对应的commandKey 。而queueSizeRejectionThreshold是每个命令都是设置的。
     
     
         
         线程池的相关参数都保存在HystrixThreadPool这个类文件中,线程池的创建方法getThreadPool则在HystrixConcurrencyStrategy类文件中。从getThreadPool方法可以看出线程池的名字就是hystrix-threadPoolKey-threadNumber.
    @Override
                public Thread newThread(Runnable r) {
                    Thread thread = new Thread(r, "hystrix-" + threadPoolKey.name() + "-" + threadNumber.incrementAndGet());
                    thread.setDaemon(true);
                    return thread;
                }
         
         在HystrixThreadPool实现类的构造方法中,并发HystrixConcurrencyStrategy实例是通过HystrixPlugins获取的,所以可以通过HystrixPlugins设置自定义插件。具体的HystrixPlugins如何使用,会在后面章节中讲解。
     
    线程池的创建     
         前面说了,在Hystrix内部大部分类都是单实例,同样ThreadPool也不例外,也是单实例。并且相同commandKey的依赖还必须是使用同一个线程池。这就需要把ThreadPool保存在一个静态的map中,key是commandKey,同时要保证线程安全,Hytstrix使用了ConcurrentHashMap。关于为什么不适用HashTable保证线程安全问题的疑问请自行Google。线程池的创建在HystrixThreadPool这个类文件中的内部类Factory中的getInstance方法。
     
    /* package */final static ConcurrentHashMap<String, HystrixThreadPool> threadPools = new ConcurrentHashMap<String, HystrixThreadPool>();
         String key = threadPoolKey.name();

                // this should find it for all but the first time
                HystrixThreadPool previouslyCached = threadPools.get(key);
                if (previouslyCached != null) {
                    return previouslyCached;
                }

                // if we get here this is the first time so we need to initialize
                synchronized (HystrixThreadPool.class) {
                    if (!threadPools.containsKey(key)) {
                        threadPools.put(key, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder));
                    }
                }
                return threadPools.get(key);
         
    线程池的使用
         HystrixCommand类的execute()内部调用了queue() ,queue又调用了父类AbstractCommand的toObservable方法,toObservable方法处理了是否可缓存问题后,交给了getRunObservableDecoratedForMetricsAndErrorHandling方法,这个方法设置了一系列的executionHook之后,交给了getExecutionObservableWithLifecycle,这个方法通过getExecutionObservable()获取了执行器。getExecutionObservable()是个抽象方法,具体实现放在了子类:HystrixCommand和HystrixObservableCommand类中。下面是HystrixCommand类中的getExecutionObservable方法实现:
    final protected Observable<R> getExecutionObservable() {
            return Observable.create(new OnSubscribe<R>() {

                @Override
                public void call(Subscriber<? super R> s) {
                    try {
                        s.onNext(run());
                        s.onCompleted();
                    } catch (Throwable e) {
                        s.onError(e);
                    }
                }

            });
        }
    在这个Call方法中执行了具体的业务逻辑run() ;
     
    转自
     
  • 相关阅读:
    暑假集训Day1 整数划分
    暑假集训day1 水题 乘法最大
    暑假集训Day0
    【不知道什么专题】——历年几道难题的分析。
    开发语言之我见
    选择器IDEA Maven不见了
    js 里函数调用之call
    js 闭包 匿名函数 JavaScript的IIFE(即时执行方法)immediately-invoked function expression
    ideal-check项目
    浏览器内部工作原理
  • 原文地址:https://www.cnblogs.com/nunuAction/p/7821690.html
Copyright © 2020-2023  润新知