The underlying core principle of Hystrix

1. Hystrix resource isolation technology

hystrix github official documentation: Home Netflix/Hystrix Wiki GitHub

Hystrix can complete common protection functions such as isolation, current limiting, fusing, and degradation.

The isolation of hystrix is divided into thread pool isolation and semaphore isolation

1.1, Semaphore isolation

  • Semaphore isolation is the current limiting function of hystrix. Although the name is isolation, it is actually implemented through a semaphore, and the semaphore is simply a counter. When the counter calculation reaches the set threshold, exception handling is performed directly.
  • The hystrix semaphore isolation limits the number of web container threads such as tomcat, which can only support so many for a period of time, and redundant requests for thread resources are rejected. Since it actually isolates some container thread resources, it can be regarded as an isolation method.
  • The semaphore isolation is synchronous, so it does not support calculating the timeout period (you need to implement it manually)
  • The semaphore isolation only plays a limiting role, and its protection capability is limited. If there is a problem with the downstream service and the result is not returned for a long time, it can only wait. Because the semaphore isolation itself has no effect on a single request, it can only limit the problem of too many requests (reject too many requests, and prevent the entire service from hanging)

In order to solve this problem, hystrix has generated thread pool isolation. This isolation method is through the introduction of additional threads (the extra here is relative to Tomcat’s threads, and the introduction of additional threads will cause additional overhead).

1.2, thread pool isolation

Hystrix uses additional threads to manage and control the original web container threads. If a thread does not return after timeout, it will be blown. Since the introduction of additional threads involves additional overhead such as thread pool management and thread context switching, thread pool isolation is more costly than semaphore isolation.

Hystrix can establish a thread pool for each dependency to isolate it from other dependent resources, while limiting their concurrent access and blocking expansion. Each dependency can allocate resources (mainly threads here) according to the weight, and if there is a problem with each part of the dependency, it will not affect the resources used by other dependencies.

1

  • Hystrix uses Command mode to encapsulate dependent calls. Each Command is a dependency mentioned above. Command can specify CommandKey
  • Command Group can group a group of Commands into one group. By default, Hystrix will create a thread pool for each Command Group
  • Each thread pool has a Key. This thread pool is the key to thread isolation. All monitoring, caching, calling, etc. come from this thread pool. The default Thread Pool key is the name of the command group
  • By default, each Command Group will automatically create a thread pool. However, there will still be such a situation: when there are some dependencies in a Command Group, but isolation is necessary (for example, a dependent timeout will Use all thread pool threads, which will affect other dependencies), you can specify Thread Pool Key for the Command in the group

1.3, Command mode

Command mode: Hystrix uses rxjava extensively to implement Command mode. All custom Commands, whether inherited from HystrixObservableCommand or HystrixCommand, ultimately inherit from AbstractCommand.

Regarding the initialization process of Command, we will talk about it through the source code later.

1.4. How to choose an isolation strategy

Semaphore isolation:

  • For those requests with relatively small delays, the overhead caused by the thread pool is very high, so it is better to use semaphore isolation
  • Applicable to accesses that are not external dependencies, because the length of access to external dependencies is difficult to control
  • For scenarios with a large amount of concurrency, if the thread pool is used at this time, it may not be able to support such a high concurrency. If it is hard to support, it may consume a lot of thread resources, so use semaphores for current limiting protection

2, Hystrix working principle

2.1, process

2.1.1, Construct HystrixCommand or HystrixObservableCommand object

returns a single response, then:

HystrixCommand command = new HystrixCommand(arg1, arg2);
</code>
 
 

Return an observable that emits a response:

HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2);
</code>
 
 

2.1.2, execute Command

There are four ways:

  • execute(): blocks, returns a single response (or throws an exception in case of error)
  • queue(): returns a Future containing a single response
  • observe(): Subscribe to an observable object that returns a response, and returns an Observable (copy source Observable)
  • toObservable(): returns an observable object, when you subscribe to it, it will execute the Hystrix command and emit its response

Note:

The first and second methods are only applicable to HystrixCommand, not to HystrixObservableCommand

execute() actually calls queue().get(). And queue() actually calls toObservable().toblocking().tofuture(). That said, eventually every HystrixCommand is backed by an Observable implementation, even those intended to return a single simple value

</code>
 
 <ol class="hljs-ln"><li>
   
   
    
    
   
   
   
   
    
    
     
     K
     
     value
     
     = command. execute();
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
     Future<K> fValue = command. queue();
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
     Observable<K> ohValue = command. observe();
     
     // hot observable
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
     Observable<K> ocValue = command.toObservable();
     
     //cold observable
    
    
   
   </li></ol>
 
 

2.1.3, whether the response is cached

If this command enables request caching, and there is a response to the request in the cache, then the cached response will be returned immediately as an observable

The request cache is located before the thread executes, that is, before the construct() or run() method call

If Hystrix does not implement request caching, we need to manually implement request caching in the construct() or run() method, that is, after the thread is executed

2.1.4, whether the circuit breaker is open

When you execute the command, Hystrix checks the circuit breaker to see if the circuit is open. If the circuit is open (or triggered), then Hystrix will not execute the command, but will route the flow to (8) for a fallback.

If the circuit is closed, flow continues to (5) to check if there is capacity available to run the command.

2.1.5, whether the capacity of thread pool, queue, and semaphore is full

If thread pool isolation is used, check whether the thread pool and queue capacity are full

If using semaphore isolation, check whether the semaphore is full

2.1.6, execution request

  • HystrixCommand.run() – returns a single response or throws an exception, then emits an onCompleted notification
  • HystrixObservableCommand.construct() – returns an observable that emits a response or emits an onError event

2.1.7, calculate whether the circuit is healthy

Hystrix reports success, failure, rejection, and timeouts to the circuit breaker, and the circuit breaker maintains a set of rolling counters to calculate statistics.

It uses these statistics to determine when the circuit should “trip”, at which point it short-circuits any subsequent requests until the recovery period is over, at which point it closes the circuit again after the first check.

2.1.8, Get the fallback method

  • In the case of HystrixCommand, to provide fallback logic, you need to implement HystrixCommand.getfallback(), which returns a fallback value
  • In the case of HystrixObservableCommand, to provide fallback logic you need to implement HystrixObservableCommand.resumewithfallback() which returns an observable that emits a fallback value
  • If no fallback method is implemented for the Hystrix command, or the fallback itself throws an exception, then Hystrix will still return an observable, but it emits nothing and ends immediately with an onError notification

If no fallback is implemented, different execution methods will have different phenomena:

  • execute(): Throw an exception
  • queue(): Future will be returned successfully, but if its get() method is called, Future will throw an exception
  • observe() : Returns an observable object, when you subscribe to it, it will terminate immediately by calling the subscriber’s onError method
  • toObservable() : returns an observable object, when you subscribe to it, it will be terminated by calling the subscriber’s onError method

2.1.9, return a successful response

3. Hystrix API

</code>
 
 <ol class="hljs-ln"><li>
   
   
    
    
   
   
   
   
    
    
     
     <dependency>
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
        
     
     <groupId>com.netflix.hystrix
     
     </groupId>
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
        
     
     <artifactId>hystrix-core
     
     </artifactId>
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
        
     
     <version>1.5.18
     
     </version>
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
     </dependency>
    
    
   
   </li></ol>
 
 

Because HystrixCommand is an abstract class, we need to inherit it and override the run method

</code>
 
 <ol class="hljs-ln"><li>
   
   
    
    
   
   
   
   
    
    
     
     import com.netflix.hystrix.HystrixCommand;
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
     import com.netflix.hystrix.HystrixCommandGroupKey;
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
     
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
     public
     
     class
     
     CommandHelloWorld
     
     extends
     
     HystrixCommand<String> {
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
     
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
     
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
        
     
     private
     
     final String name;
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
     
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
        
     
     public
     
     CommandHelloWorld
     
     (String name) {
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     super(HystrixCommandGroupKey.Factory.asKey(
     
     "HelloGroup"));
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.name = name;
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
         }
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
     
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
        
     
     @Override
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
        
     
     protected String
     
     run
     
     () {
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     return
     
     "Hello " + name +
     
     "!";
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
         }
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
     
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
     
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
        
     
     public
     
     static
     
     void
     
     main
     
     (String[] args) {
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     String
     
     the s
     
     =
     
     new
     
     CommandHelloWorld(
     
     "bobo"). execute();
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
             System.out.println(s);
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
     
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
         }
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
     }
    
    
   
   </li></ol>
 
 
  
  <img class="look-more-preCode contentImg-no-view" src="//i2.wp.com/csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack.png" alt="" title = "">
 
 
 
 

For more API usage methods, please click here: How To Use Netflix/Hystrix Wiki GitHub

4. Source code interpretation

About HystrixCommand, including HystrixObservableCommand, all inherit AbstractCommand, and AbstractCommand has a core construction method, which contains all configurations of the command

</code>
 
 <ol class="hljs-ln"><li>
   
   
    
    
   
   
   
   
    
    
     
     protected
     
     AbstractCommand
     
     (HystrixCommandGroupKey group, HystrixCommandKey key, HystrixThreadPoolKey threadPoolKey, HystrixCircuitBreaker circuitBreaker, HystrixThreadPool threadPool,
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
                 HystrixCommandProperties.Setter commandPropertiesDefaults, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults,
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
                 HystrixCommandMetrics metrics, TryableSemaphore fallbackSemaphore, TryableSemaphore executionSemaphore,
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
                 HystrixPropertiesStrategy propertiesStrategy, HystrixCommandExecutionHook executionHook) {
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
     
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.commandGroup = initGroupKey(group);
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.commandKey = initCommandKey(key, getClass());
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.properties = initCommandProperties(
     
     this.commandKey, propertiesStrategy, commandPropertiesDefaults);
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.threadPoolKey = initThreadPoolKey(threadPoolKey,
     
     this.commandGroup,
     
     this.properties.executionIsolationThreadPoolKeyOverride().get());
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.metrics = initMetrics(metrics,
     
     this.commandGroup,
     
     this. threadPoolKey,
     
     this.commandKey,
     
     this.properties);
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.circuitBreaker = initCircuitBreaker(
     
     this.properties.circuitBreakerEnabled().get(), circuitBreaker,
     
     this.commandGroup,
     
     this.commandKey,
     
     this.properties,
     
     this. metrics);
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.threadPool = initThreadPool(threadPool,
     
     this.threadPoolKey, threadPoolPropertiesDefaults);
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
     
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     //Strategies from plugins
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
             HystrixMetricsPublisherFactory.createOrRetrievePublisherForCommand(
     
     this.commandKey,
     
     this.commandGroup,
     
     this.metrics,
     
     this.circuitBreaker,
     
     this.properties);
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.executionHook = initExecutionHook(executionHook);
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
     
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.requestCache = HystrixRequestCache.getInstance(
     
     this.commandKey,
     
     this.concurrencyStrategy);
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.currentRequestLog = initRequestLog(
     
     this.properties.requestLogEnabled().get(),
     
     this.concurrencyStrategy);
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
     
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     /* fallback semaphore override if applicable */
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this.fallbackSemaphoreOverride = fallbackSemaphore;
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
     
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     /* execution semaphore override if applicable */
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
            
     
     this. executionSemaphoreOverride = executionSemaphore;
    
    
   
   </li><li>
   
   
    
    
   
   
   
   
    
    
     
         }
    
    
   
   </li></ol>
 
 
  
  <img class="look-more-preCode contentImg-no-view" src="//i2.wp.com/csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack.png" alt="" title = "">
 
 
 
 

As mentioned earlier, Thread Pool, Command Group, and Command Key are all implemented in AbstractCommand

And just in the above code, there is a line like this:

this.threadPool = initThreadPool(threadPool, this.threadPoolKey, threadPoolPropertiesDefaults);</code>
 
 

This line of code means to load the thread pool, click to see

click in again

Click into the construction method of HystrixThreadPoolDefault to see what has been done

Then click in to see, you can see the code that finally creates the thread pool