Spring Cloud (06) - Breaker Hystrix

Breaker Hystrix

Hystrix Netflix is implemented circuit breaker github address is https://github.com/Netflix/Hystrix . When the number of times a service call exceeds circuitBreaker.requestVolumeThreshold(default is 20), and at a specified time window metrics.rollingStats.timeInMilliseconds(default is 10 seconds) in the proportion of failures reached circuitBreaker.errorThresholdPercentage(the default is 50%), the circuit breaker is open, the circuit breaker after opening the next request will not call the real service, the default open time is 5 seconds (by the parameter circuitBreaker.sleepWindowInMillisecondscontrol). When the circuit breaker to open or a service call fails, developers can provide for this purpose a fallbackMethod, this time will be converted to call fallbackMethod. Spring Cloud provides support for Hystrix, adding in pom.xml application services in use spring-cloud-starter-netflix-hystrixrely on.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

After adding a dependent, you need to @EnableCircuitBreakerenable support for the circuit breaker.

@SpringBootApplication
@EnableCircuitBreaker
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

Consider the following services, at the request /hello/erroreach time being given, and every time a successful visit to error(), because at this time there is no need to declare the use of circuit breakers.

@RestController
@RequestMapping("hello")
public class HelloController {

    private int count;
    
    @GetMapping("error")
    public String error() {
        System.out.println(++count + " Error. " + LocalDateTime.now());
        throw new IllegalStateException();
    }
    
}

By error()adding @HystrixCommanddeclare that it requires the use of circuit breaker protection, in the case of continuous access to specified time window error()after a certain number of times, subsequent requests will not be invoked error(), because the circuit breaker has been opened up.

@RestController
@RequestMapping("hello")
public class HelloController {

    private int count;
    
    @HystrixCommand
    @GetMapping("error")
    public String error() {
        System.out.println(++count + " Error. " + LocalDateTime.now());
        throw new IllegalStateException();
    }
    
}

 

fallback

In the case of the circuit breaker to open or service call error, you can fall back to calling fallbackMethod. The following code specifies when error()to fall back to when you call the wrong call fallback()method, error()the method will count plus 1, fallback()will also count plus 1, so call error()when you will see when the circuit breaker is not turned on, each time the count is returned plus 2 because the error()added time, fallback()again and again once, and when the circuit breaker is open, each time the count will only return fallback()plus one.

private int count;

@HystrixCommand(fallbackMethod="fallback")
@GetMapping("error")
public String error() {
    String result = ++count + " Error. " + LocalDateTime.now();
    System.out.println(result);
    if (count % 5 != 0) {
        throw new IllegalStateException();
    }
    return result;
}

public String fallback() {
    return ++count + "result from fallback.";
}

In fallbackMethod you can make any logic you want, can make remote calls, access the database, etc., it can be returned from the static data for some fault-tolerant.

Specified fallbackMethod must have the same signature service method. The same signature means that the method return types and their methods are the same number and type of parameters, such as the following service method String error(String param)accepts a String parameter, return type is String, it specifies fallbackMethod fallbackWithParammust also receive a parameter of type String, The return type must be String. Parameter values are transmitted correctly, for the following services when accessing error/param1, the access error()parameter is transmitted param1, to access fallbackWithParam()the parameters passed when also param1.

@HystrixCommand(fallbackMethod="fallbackWithParam")
@GetMapping("error/{param}")
public String error(@PathVariable("param") String param) {
    throw new IllegalStateException();
}

public String fallbackWithParam(String param) {
    return "fallback with param : " + param;
}

@HystrixCommandIn addition to specifying fallbackMethod, you can also specify defaultFallback. The method defaultFallback corresponding argument must be free, and it must use the same return current methods. Its role and fallbackMethod is the same, namely the circuit breaker to open or method will be converted into the specified method defaultFallback call when calling error. It is the difference with the fallbackMethod fallbackMethod specified method signature must be consistent with the current method, and defaultFallback approach must not enter parameters, such defaultFallback corresponding method can simultaneously service multiple service methods that can be used as a generic fallback method. When specifying the fallbackMethod and defaultFallback, fallbackMethod will have a higher priority.

@GetMapping("error/default")
@HystrixCommand(defaultFallback="defaultFallback")
public String errorWithDefaultFallback() {
    throw new IllegalStateException();
}

public String defaultFallback() {
    return "default";
}

 

Configuration commandProperties

@HystrixCommandSome configuration parameters can also be specified, typically specified by commandProperties, and each parameter by a @HystrixPropertyspecified. The following example specifies such a circuit breaker to open the time window is measured 30 seconds ( metrics.rollingStats.timeInMilliseconds), a threshold number of requests is 5 ( circuitBreaker.requestVolumeThreshold), the circuit breaker opening stop service request method of real time window of 15 seconds ( circuitBreaker.sleepWindowInMillisecond).

@HystrixCommand(fallbackMethod = "fallback", commandProperties = {
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"),
        @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "30000"),
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "15000") })
@GetMapping("error")
public String error() {
    String result = ++count + " Error. " + LocalDateTime.now();
    System.out.println(result);
    if (count % 5 != 0) {
        throw new IllegalStateException();
    }
    return result;
}

public String fallback() {
    return count++ + "result from fallback." + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS"));
}

Circuit breaker protected, default timeout is 1 second, the parameter execution.isolation.thread.timeoutInMillisecondscontrol, the following configuration code service method timeout()timeout time is 3 seconds.

@GetMapping("timeout")
@HystrixCommand(defaultFallback = "defaultFallback", commandProperties = @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"))
public String timeout() {
    try {
        TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "timeout";
}

You can also specify parameters execution.timeout.enabledfor the falsedisable time-out, so that no matter how much time to perform the service method will not be timed out.

 

Concurrency Control

@HystrixCommandThere are two methods of tagging service implementation at the time of execution, thread-based and based on SEMAPHORE, and thrown into a thread pool thread of execution based on the implementation of the policy will service every method that is independent of the request thread thread, based SEMAPHORE will perform the service method in the same thread . The default is thread-based, the default thread pool size is 10, and use buffer queue is SynchronousQueue, equivalent to no buffer queue, so the default number of concurrent support is 10, the number of concurrent over 10 will be rejected. The following code example, only 30 seconds 10 times successful request.

@GetMapping("concurrent")
@HystrixCommand(defaultFallback = "defaultFallback", commandProperties = @HystrixProperty(name = "execution.timeout.enabled", value = "false"))
public String concurrent() {
    System.out.println(LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(30);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "concurrent";
}

If you need support more concurrent, you can adjust the size of the thread pool, thread pool size specified by threadPoolProperties, each thread pool parameters are also through @HystrixPropertydesignated. For example, the following code specifies the core number of threads in the thread pool is 15, then the following methods appropriate service can support up to 15 concurrent up.

@GetMapping("concurrent")
@HystrixCommand(defaultFallback = "defaultFallback", 
    commandProperties = @HystrixProperty(name = "execution.timeout.enabled", value = "false"), 
    threadPoolProperties = @HystrixProperty(name = "coreSize", value = "15"))
public String concurrent() {
    System.out.println(LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(30);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "concurrent";
}

You may also specify the size of the queue buffer after a specified buffer queue size using LinkedBlockingQueue. The following service method specified buffer queue thread pool is 2, the core thread pool thread pool 10 is the default, then it can receive a maximum of 12 simultaneous requests.

@GetMapping("concurrent")
@HystrixCommand(defaultFallback = "defaultFallback", 
    commandProperties = @HystrixProperty(name = "execution.timeout.enabled", value = "false"), 
    threadPoolProperties = @HystrixProperty(name = "maxQueueSize", value = "2"))
public String concurrent() {
    System.out.println(LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "concurrent";
}

When you specify maxQueueSizethe time for the 100, your service method can only accommodate 15 requests. This is because the default queue capacity of more than 5 is rejected, that is by default maxQueueSizemore than 5, which can only receive queue requests five times. This parameter is queueSizeRejectionThresholdcontrolled, together with the reasons for the control parameter is the queue capacity is not changed dynamically add this parameter can be controlled after the queue capacity by this parameter. The following code specifies the size of the queue 20, the capacity threshold reject queue element is added 20, it can be a maximum queue inside the support element 20, together with the default request thread pool 10 inside, and can accommodate up to 30 requests.

@GetMapping("concurrent")
@HystrixCommand(defaultFallback = "defaultFallback", commandProperties = @HystrixProperty(name = "execution.timeout.enabled", value = "false"), 
    threadPoolProperties = {
        @HystrixProperty(name = "maxQueueSize", value = "20"),
        @HystrixProperty(name = "queueSizeRejectionThreshold", value = "20") })
public String concurrent() {
    System.out.println(LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "concurrent";
}

This thread pool is also possible via the parameter maximumSize, the configuration parameters individually but the maximum number of threads the thread pool configuration, the maximum number of threads will not take effect, with the parameters allowMaximumSizeToDivergeFromCoreSizeused together. Its default value is false, the maximum number of threads configured to true will come into force.

@GetMapping("concurrent")
@HystrixCommand(defaultFallback = "defaultFallback", commandProperties = @HystrixProperty(name = "execution.timeout.enabled", value = "false"), 
    threadPoolProperties = {
        @HystrixProperty(name = "maximumSize", value = "20"),
        @HystrixProperty(name = "allowMaximumSizeToDivergeFromCoreSize", value = "true")})
public String concurrent() {
    System.out.println(LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "concurrent";
}

When the maximum number of threads into force, more than the number of threads core threads default maximum idle time of 1 minute, parameters can keepAliveTimeMinutesbe adjusted in minutes.

If you need to specify the queue size and the maximum number of threads at the same time, you need to specify queueSizeRejectionThresholdmore than maxQueueSizelarge, otherwise the maximum number of threads will not grow.

@GetMapping("concurrent")
@HystrixCommand(defaultFallback = "defaultFallback", commandProperties = @HystrixProperty(name = "execution.timeout.enabled", value = "false"), 
    threadPoolProperties = {
        @HystrixProperty(name = "maximumSize", value = "15"),
        @HystrixProperty(name = "allowMaximumSizeToDivergeFromCoreSize", value = "true"), 
        @HystrixProperty(name = "maxQueueSize", value = "15"),
        @HystrixProperty(name = "queueSizeRejectionThreshold", value = "16")})
public String concurrent() {
    System.out.println(LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "concurrent";
}

Need service method implementation strategy is based on SEMAPHORE, you need to specify parameter execution.isolation.strategyvalues SEMAPHORE, which is the default number of concurrent 10. Parameters can be execution.isolation.semaphore.maxConcurrentRequestsadjusted corresponding to the number of concurrent.

@GetMapping("concurrent/semaphore")
@HystrixCommand(defaultFallback = "defaultFallback", commandProperties = {
        @HystrixProperty(name = "execution.timeout.enabled", value = "false"),
        @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
        @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "18") })
public String concurrentSemaphore() {
    System.out.println(LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "concurrent";
}

For in the number of concurrent fallback also be specified by the parameters fallback.isolation.semaphore.maxConcurrentRequestsspecified. Although the parameter name contains the semaphore, but the parameters for the service method of execution strategies are effective, the default is 10 not specified. If your fallback is relatively time-consuming, it would need to carefully consider the default value is able to meet the demand.

The above are just some common hystrix enumerated parameters to be configured, the configuration parameters may refer to a complete https://github.com/Netflix/Hystrix/wiki/Configuration .

 

DefaultProperties

When the Controller there are many service methods require the same configuration, we can not put these configurations are applied to respective @HystrixCommandupper, Hystrix provides an @DefaultPropertiesargument for the same configuration. The following code example specifies all the services in the current methods Controller timeout time is 10 seconds, the maximum number of concurrent fallback is 5.

@RestController
@RequestMapping("hystrix/properties")
@DefaultProperties(defaultFallback = "defaultFallback", commandProperties = {
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "10000"),
        @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "5") })
public class DefaultPropertiesController {

    @GetMapping("error")
    @HystrixCommand
    public String error() {
        throw new IllegalStateException();
    }
    
    //...
    
    public String defaultFallback() {
        return "result from default fallback.";
    }

}

 

The configuration parameters application.properties

For Spring Cloud applications, Hystrix configuration parameters can be configured in application.properties in. Hystrix configuration including default configuration and instance configuration categories. For @HystrixCommandthe commandProperties, the default configuration parameters are based on the original parameters plus the hystrix.command.default.prefix, such as control parameters is performed timeout execution.isolation.thread.timeoutInMilliseconds, that is the default configuration parameters hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds. For @HystrixCommandthe threadPoolProperties, the default configuration parameters are based on the original parameters plus the hystrix.threadpool.default.prefix, such as the number of core threads in the thread pool by the parameter coreSizecontrol, is that the default configuration parameters hystrix.threadpool.default.coreSize. If our application.properties are defined as follows, it indicates that @HystrixCommandthe method of execution marked default timeout is 5 seconds, the core number of threads in the thread pool is 5.

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
hystrix.threadpool.default.coreSize=5

The default values ​​are the parameters that can be covered on the example, the default parameters for the above configuration, if such a configuration has the following Examples, the following method is performed timeout will use the default five seconds, and the maximum number (i.e., core concurrency number of threads) 6 is disposed inside the code.

@GetMapping("timeout/{timeout}")
@HystrixCommand(threadPoolProperties=@HystrixProperty(name="coreSize", value="6"))
public String timeoutConfigureWithSpringCloud(@PathVariable("timeout") long timeout) {
    System.out.println("Hellooooooooooooo");
    try {
        TimeUnit.SECONDS.sleep(timeout);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "timeoutConfigureWithSpringCloud";
}

Examples of configuration parameters also can be configured in application.properties, for commandProperties, just need to replace the default instance of the default parameter names can commandKey, commandKey default method is to take the current name if you do not intentionally configuration. So for the above timeoutConfigureWithSpringCloud method, if the instance need to specify the timeout time is 3 seconds, the arrangement may be made in the application.properties.

hystrix.command.timeoutConfigureWithSpringCloud.execution.isolation.thread.timeoutInMilliseconds=3000

Method name is very easy to duplicate, if you do not want to use the default commandKey, you can pass @HystrixCommandthe property commandKey specified. Such as the following code as it is specified commandKey springcloud.

@GetMapping("timeout/{timeout}")
@HystrixCommand(commandKey="springcloud")
public String timeoutConfigureWithSpringCloud(@PathVariable("timeout") long timeout) {
    System.out.println("Hellooooooooooooo");
    try {
        TimeUnit.SECONDS.sleep(timeout);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "timeoutConfigureWithSpringCloud";
}

If you need to specify the method that the timeout time is 3 seconds, as necessary to adjust the corresponding commandKey springcloud, i.e., in the following manner:

hystrix.command.springcloud.execution.isolation.thread.timeoutInMilliseconds=3000

For the thread pool, the example of the configuration parameters need to replace the default parameters as threadPoolKey default, @HystrixCommanddefault value is the name of the current method threadPoolKey Class belongs, such as the current @HystrixCommandname of the method is that belongs to Class HelloController in, you can be configured to run as specified the core number of threads in the thread pool is used when the method is 8.

hystrix.threadpool.HelloController.coreSize=8

The following code is manually specified threadPoolKey springcloud.

@GetMapping("timeout/{timeout}")
@HystrixCommand(commandKey="springcloud", threadPoolKey="springcloud")
public String timeoutConfigureWithSpringCloud(@PathVariable("timeout") long timeout) {
    System.out.println("Hellooooooooooooo");
    try {
        TimeUnit.SECONDS.sleep(timeout);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "timeoutConfigureWithSpringCloud";
}

If you need to specify the core number of threads in the thread pool it was 3, only you need to configure the parameters of hystrix.threadpool.springcloud.coreSizea value of 3.

hystrix.threadpool.springcloud.coreSize=3

More details on Hystrix configuration parameter default parameter names and instance parameter name can refer to https://github.com/Netflix/Hystrix/wiki/Configuration .

More about Hystrix introduction of wiki can refer to its GitHub address https://github.com/netflix/hystrix/wiki .

 

Reference Documents

https://github.com/netflix/hystrix/wiki https://github.com/Netflix/Hystrix/wiki/Configuration http://cloud.spring.io/spring-cloud-static/Finchley.SR1/multi/multi__circuit_breaker_hystrix_clients.html

(Note: This article is written based on Spring cloud Finchley.SR1)

Guess you like

Origin www.iteye.com/blog/elim-2443923