[Hystrix authoritative guide three] Hystrix isolation strategy source code analysis one

      
          Before starting the source code analysis of the isolation strategy, first understand the class organization of Hystrix. Beans configured in Spring have a scop attribute, which is singleton by default. It is guaranteed that in an IOC container, there is only one instance of a class, which reduces memory usage and utilizes GC at the same time. In addition: The singleton pattern in Spring is different from the ordinary singleton pattern. In Spring, there is only one instance in an IOC container, but a JVM can have multiple IOC containers. An ordinary singleton means that there can only be one instance in a JVM.
Hystrix class organization
          Also inside Hystrix, the singleton pattern is also used. Each interface class file has a Factory and an implementation class of the interface. The specific implementation object of the interface is obtained through the Factory in the interface file, so as to operate the related methods of the object. The Factory holds a static constant of type ConCurrentHash . When an instance of an interface is required, if there is no such instance in the Map, a new putIfAbsent is put into the map.
          Here is a little trick, the new object is placed in the putIfAbsent method, so it is a local variable, and there is no thread safety problem. putIfAbsent is a method provided by ConcurrentHashMap. Internal synchronization is optimized through Segment, while thread safety is ensured, so Synchornized is not required when initializing objects. If there are two threads accessing the putIfAbsent method at the same time, only one can return instance, and the other returns null. You only need to make a non-null judgment on the access result. If it returns null, it means that there is already an object in the map, and you can get it. Such as:
     HystrixCircuitBreaker cbForCommand = circuitBreakersByCommand.putIfAbsent(key.name(), new HystrixCircuitBreakerImpl(key, group, properties, metrics));
            if (cbForCommand == null) {
                // this means the putIfAbsent step just created a new one so let's retrieve and return it
                return circuitBreakersByCommand.get(key.name());
            } else {
                // this means a race occurred and while attempting to 'put' another one got there before
                // and we instead retrieved it and will now return it
                return cbForCommand;
            }
          
             When setting the CommandGroupKey in the previous chapter, you first need to convert the String into a HystrixCommandGroupKey object, which is through withGroupKey(HystrixCommandGroupKey.Factory.asKey("hello")), and other objects are also operated through XXX.Factory.xx.
          All objects inside Hystrix are operated through xxx(interface).Factory.getInstance().
 
Hystrix thread pool isolation source code analysis
          The execution process of Hystrix commands is relatively logical and clear as a whole, responsive programming. First check whether the cache is enabled, if so, read directly from the cache, otherwise execute normally. According to the isolation strategy set when constructing the command.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)  , choose whether to obtain resources from the thread pool or obtain the permission from the signal pool.
           HystrixCommand class inheritance diagram:

          
          It is worth noting that if the loop is closed, a permit is obtained. At this time, it is not judged whether it is signal isolation or not. That is to say, even if the isolation strategy is Thread, a Permit will also be obtained here. Then the question arises: In this case, wouldn't Thread isolation also occupy Permit? the answer is negative. This place decides whether to return TryableSemaphoreNoOp or TryableSemaphore according to the isolation policy when obtaining Permit. TryableSemaphoreNoOp does not execute any logic, and always returns true when tryAcquire. The one returned here is obviously TryableSemaphoreNoOp.
          下面先看下整体的流程图,对执行流程有一个整体的把握:
 
          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() ;

http://blog.csdn.net/a_fengzi_code_110/article/details/53643488

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327106417&siteId=291194637