Hystrix service fault tolerance (a)

There are multiple services can be directly invoked in the micro-service architecture, these services if failure occurs when the call results in a chain reaction, which is likely to make the whole system becomes unavailable, in which case we call service avalanche effect.

How to avoid the avalanche effect service? It can be solved by Hystrix.

1.Hystrix

Hystrix is ​​Netflix fuse protection for distributed systems using middleware micro service, the equivalent circuit fuse. In the micro-service architecture, many services are interdependent, if you can not rely on the service isolation, then the service itself may be damaged, Hystrix to isolate calls by Hystrix Command, which can prevent a chain reaction of failures, allowing interface calls fast failure and quickly return to normal, or rollback and graceful degradation.

1.1 Hystrix simple and practical

Create an empty Maven project, increased reliance Hystrix:

<dependency>
    <groupId>com.netflix.hystrix</groupId>
    <artifactId>hystrix-core</artifactId>
    <version>1.5.18</version>
</dependency>

A first write HystrixCommand, as shown in the code:

public class MyHystrixCommand extends HystrixCommand<String>{
    private final String name;
    public MyHystrixCommand(String name) {
        super(HystrixCommandGroupKey.Factory.asKey("MyGroup"));
        this.name=name;
    }
    @Override
    protected String run() throws Exception {
        // TODO Auto-generated method stub
        return this.name+":"+Thread.currentThread().getName();
    }

}

First need to inherit HystrixCommand, a Groupkey set by the constructor. Specific logic in the run method, we return to the current value of a thread name, write a main method to invoke MyHystrixCommand programs written above, as shown in the code:

public static void main(String[] args) throws InterruptedException,ExecutionException{
        String result=new MyHystrixCommand("yinjihuan").execute();
        System.out.println(result);
    }

The output is: yinjihuan:. Hystrix-MyGroup-1 can be seen, the group name set in the constructor becomes the name of the thread.

The above is a synchronous call, if need be asynchronous call using the following code:

public static void main(String[] args) throws InterruptedException,ExecutionException{

        Future<String> future=new MyHystrixCommand("yinjihuan").queue();
        System.out.println(future.get());
    }

1.2 fallback support

Here we simulate call fails by increasing the execution time, the first transformation MyHystrixCommand, increase getFallback method returns fallback content, such as the code shown:

public class MyHystrixCommand extends HystrixCommand<String>{
    private final String name;
    public MyHystrixCommand(String name) {
        super(HystrixCommandGroupKey.Factory.asKey("MyGroup"));
        this.name=name;
    }
    @Override
    protected String run() throws Exception {
        // TODO Auto-generated method stub
        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return this.name+":"+Thread.currentThread().getName();
    }
    protected String getFallback() {
        return "失败了";
    }
    
    
    public static void main(String[] args) throws InterruptedException,ExecutionException{
//        String result=new MyHystrixCommand("yinjihuan").execute();
//        System.out.println(result);
        Future<String> future=new MyHystrixCommand("yinjihuan").queue();
        System.out.println(future.get());
    }

}

Code execution, return the contents of a "failed" to prove that triggered the rollback.

Semaphore 1.3 policy configuration

Semaphore policy configuration as shown in the code:

public MyHystrixCommand(String name) {
        super(HystrixCommand.Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("MyGroup"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(
                                HystrixCommandProperties
                                .ExecutionIsolationStrategy.SEMAPHORE)));
        this.name=name;
    }

Prior to the run method specifically output thread name, the name can be determined by the current thread is isolated or semaphore isolation.

1.4 thread quarantine policy configuration

The system defaults to thread isolation strategy, we can configure some parameters of the thread pool by andThreadPoolPropertiesDefaults, as shown in the code:

public MyHystrixCommand(String name) {
        super(HystrixCommand.Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("MyGroup"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(
                                HystrixCommandProperties
                                .ExecutionIsolationStrategy.THREAD)
                        ).andThreadPoolPropertiesDefaults(
                                HystrixThreadPoolProperties.Setter()
                                .withCoreSize(10)
                                .withMaxQueueSize(100)
                                .withMaximumSize(100)
                                )
                );
        this.name=name;
    }

1.5 Result Cache

Cache frequently used in development, we often use such third-party caching Redis database for caching data, Hystrix also provides us with a method-level cache. To determine whether the returned data is cached by overwriting getCacheKey, getCacheKey may be generated according to the parameters, such parameters can be the same cache are used.

Before rewriting MyHystrixCommand, wherein an increase in rewriting getCacheKey implemented, as shown in the code:

//返回缓存key
protected String getCacheKey() {
        return String.valueOf(this.name);
    }

The above code we create the object passed in as a parameter name cache key.

To prove able to use the cache, add a line of output in the run method, in the case of multiple calls, if the console output only once, you can know is to go back caching logic, as shown in the code:

protected String run()  {
        // TODO Auto-generated method stub
        System.err.println("gat data");
        return this.name+":"+Thread.currentThread().getName();
    }

Execute the main method found error: Request caching is not available Maybe you need to initialize the HystrixRequestContext.?

According error may know, the cache processing requests depends on the context of the request, we must initialize Hystrix-RequestContext.

Transformation calling code in the main method, initialize HystrixRequestContext, as shown in the code:

public static void main(String[] args) throws InterruptedException,ExecutionException{
        HystrixRequestContext context=HystrixRequestContext.initializeContext();
        String result=new MyHystrixCommand("yinjihuan").execute();
        System.out.println(result);
        Future<String> future=new MyHystrixCommand("yinjihuan").queue();
        System.out.println(future.get());
        context.shutdown();
    }

Re-executed after completion of the transformation of the main method, be a normal operation, the output is structured as follows:

get data 
yinjihuan: hystrix -MyGroup-1 
yinjihuan: hystrix -MyGroup-1

Output can only see once get data, the cache effect.

1.6 Clear Cache

In the section on learning how to use hystrix to implement data caching feature. There will inevitably have to clear the cache of cache operation, when the data is changed, you must also update the data in the cache off, otherwise it will cause problems of dirty data. Similarly, Hystrix also clear the cache function.

Adding a support to clear the cache type, as shown in the code:

public class ClearCacheHystrixCommand extends HystrixCommand<String>{
    private final String name;
    private static final HystrixCommandKey GETTER_KEY=HystrixCommandKey.Factory.asKey("MyKey");
    public ClearCacheHystrixCommand(String name) {
        super(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.
                Factory.asKey("MyGroup")).andCommandKey(GETTER_KEY));
        this.name=name;
    }
    
    public static void flushCache(String name) {
        HystrixRequestCache.getInstance(
                GETTER_KEY, HystrixConcurrencyStrategyDefault.getInstance()).clear(name);
    }
    
    
    protected String getCacheKey() {
        return String.valueOf(this.name);
    }
    @Override
    protected String run() throws Exception {
        System.err.println("get data");
        return this.name+":"+Thread.currentThread().getName();
    }
    
    protected String getFallback() {
        return "失败了";
    }

}
flushCache is to clear the cache method is performed cleared by HystrixRequestCache, according to key getCacheKey returned to clear.

Modifying the calling code to verify whether a clear effect, as shown in the code:

HystrixRequestContext context=HystrixRequestContext.initializeContext();
        String result=new ClearCacheHystrixCommand("yinjihuan").execute();
        System.out.println(result);
        ClearCacheHystrixCommand.flushCache("yinjihuan");
        Future<String> future=new ClearCacheHystrixCommand("yinjihuan").queue();
        System.out.println(future.get());

The same execution twice key, before calling a second time to clear the cache method, that is less than the second cache, output results are as follows:

get data 
yinjihuan: hystrix -MyGroup-1 
get data 
yinjihuan: hystrix -MyGroup-2
1.7 merge request

Hystrix support multiple requests automatically merged into a single request, take advantage of this feature can save network overhead, such as each request to access remote resources over the network. If multiple requests into one performed together, it will become a multiple network interaction will greatly save money. As shown in the code:

public class MyHystrixCollapser extends HystrixCollapser<List<String>,String,String>{

    private final String name;
    public MyHystrixCollapser(String name) {
        this.name=name;
    }
    @Override
    public String getRequestArgument() {
        // TODO Auto-generated method stub
        return name;
    }

    @Override
    protected HystrixCommand<List<String>> createCommand(final Collection<CollapsedRequest<String, String>> requests) {
        // TODO Auto-generated method stub
        return new BatchCommand(requests);
    }

    @Override
    protected void mapResponseToRequests(List<String> batchResponse,
            Collection<CollapsedRequest<String, String>> requests) {
        int count=0;
        for(CollapsedRequest<String, String>request:requests) {
            request.setResponse(batchResponse.get(count++));
        }
    }
    
    private static final class BatchCommand extends HystrixCommand<List<String>>{
        private final Collection<CollapsedRequest<String,String>> requests;
        private BatchCommand(Collection<CollapsedRequest<String, String>>requests) {
            super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
                    .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueForKey")));
                this.requests=requests;
        }
        @Override
        protected List<String> run() throws Exception {
            System.out.println("真正执行请求......");
            ArrayList<String> response=new ArrayList<String>();
            for(CollapsedRequest<String, String>request:requests) {
                response.add("返回结果:"+request.getArgument());
            }
            return response;
        }
    }

}

Next, write test code, as shown in the code:

HystrixRequestContext context=HystrixRequestContext.initializeContext();
        Future<String> f1=new MyHystrixCollapser("yinjihuan").queue();
        Future<String> f2=new MyHystrixCollapser("yinjihuan333").queue();
        System.out.println(f1.get()+"="+f2.get());
        context.shutdown();
by

MyHystrixCollapser create two tasks, two tasks must be executed separately according to the normal logic, it can be merged by HystrixCollapser multiple tasks to be performed together. As can be seen from the output, to do tasks that run in the process, the output results are as follows:

Real execution request ...... 
returns the result: yinjihuan = returns the result: yinjihuan333


2. In the Spring Cloud in Hystrix

2.1 simple to use

Hystrix dependent increase in fsh-substitution service:

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

Add @EnableHystrix or @EnableCircuitBreaker on startup class. Note, @ EnableHystrix included @EnableCircuitBreaker.

Then callHello before transformation methods, the above increase in a @HystrixCommand annotation methods to be invoked dependent on the specified service call for delay or failure, as shown in the code:

@GetMapping("/callHello")
    @HystrixCommand(fallbackMethod="defaultCallHello")
    public String callHello() {

String result=restTemplate.getForObject("http://fsh-house/house/hello", String.class);

        return result;
    }
when

When the call back will fail fuse triggered by specific content defaultCallHello forth method, the code defaultCallHello method defined as follows:

public String defaultCallHello() {
        return "fail";
    }

Only start fsh-substitution service without starting fsh-house service, call / callHello interface, you can see the contents of return is "fail".

Will start classes on @EnableHystrix removed, restart the service, called again / callHello interfaces can see 500 error message is returned, this time it did not use the rollback feature.

2.2 Detailed configuration

In addition, there are many HystrixCommand fallbackMethod configuration, let's look at these configurations:

Official configuration information refer to the documentation: https://github.com/Netflix/Hystrix/wiki/Configuration


2.3 Feign fault-tolerant service integration Hystrix

First, we need to perform in Section 2.1 of integration steps, then turned Feign support for Hystrix in the properties file:

feign.hystrix.enabled=true

1.Fallback way

Specifies the fallback in @FeignClient comment on the client class Feign's rollback, the transformation of fsh-housed client class HouseRemoteClient, configure fallback. code show as below:

@FeignClient(value = "fsh-house", path = "/house", configuration = FeignConfiguration.class
        , fallback = HouseRemoteClientHystrix.class)
public interface HouseRemoteClient {
    
    @GetMapping("/{houseId}")
    HouseInfoDto hosueInfo@PathVariable("houseId")Long houseId);
}

All methods HouseRemoteClient class HouseRemoteClientHystrix need to achieve, to return the contents backoff, as shown in the code:

@Component
public class HouseRemoteClientHystrix implements HouseRemoteClient{
    
    public HouseInfoDto houseInfo(Long houseId){
        return new HouseInfoDto();
}

}

Start fsh-substitution service, stopped all fsh-house services, then visit http: // localhost: 8082 / substitution / 1 interfaces, this time fsh-house service is unavailable, the inevitable trigger fallback content returns to normal format, but the house is empty objects, which proves fallback into effect. In this case, if you call interface interface multiple services, only fsh-house services are not data, it will not affect the level of service, if not Hystrix rollback process, the entire request will fail.

2.FallbackFactory way

fallback service can not be rolled back if it has been used function can be achieved by, if you want to know the reason for triggering a fallback, you can use FallbackFactory to achieve rollback function, as shown in the code:

@Component
public class HouseRemoteClientFallbackFactory implements FallbackFactory<HouseRemoteClient> {
    @Override
    public HouseRemoteClient create(final Throwable cause) {
        return new HouseRemoteClient() {

            @Override
            public HouseInfoDto hosueInfo(Long houseId) {
                HouseInfoDto info = new HouseInfoDto();
                info.setData(new HouseInfo(1L, "", "", cause.getMessage()));
                return info;
            }
};
}
}

FallbackFactory in use is specified using fallbackFactory @FeignClient fallback handler class, as shown in the code:

@FeignClient(value = "fsh-house", path = "/house", configuration = FeignConfiguration.class
        , fallbackFactory = HouseRemoteClientFallbackFactory.class)

At this time rollback process, the exception information is set to HouseInfo in the name of the property. We restart fsh-substitution, call interface, you can see the result in the return exception information inside, FallbackFactory and the only difference Fallback here.

In 2.4 Feign disabled Hystrix

Disable Hystrix is ​​relatively simple, there are two ways to disable, one is disabled in all properties file:

feign.hystrix.enabled=false

Another is to disable a client by way of the code, the code follows the increase in the configuration class Feign shown:

@Configuration
public class FeignConfiguration{

    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder(){
        return Feign.builder();
    }
}
3.Hystrix monitoring

In the micro-service architecture, Hystrix addition to fault tolerance, but also provides real-time monitoring. When service calls, Hystrix real time information about HystrixCommand cumulative execution, such as the number of requests per second, the number of success and so on. For more information, please see the official documentation index address:

https://github.com/Netflix/Hystrix/wiki/Metrics-and-Monitoring.

Hystrix monitoring requires two prerequisites:

(1) There must rely Actuator, as shown in the code:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artigactId>
</dependency>

(2) There must Hystrix dependence, Spring Cloud must be added @EnableHystrix Hystrix turned, as shown in the code in the boot class:

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

We visited fsh-substitution in the address ( HTTP: // localhost: 8082 / hystrix.sream ) can be seen in the output has been "ping:", this situation is because there is no data, after HystrixCommand executed until you can see to the specific data.

4. Integration Dashboard View Monitoring Data

We mentioned above to get through hystrix.stream endpoint monitoring data, but the data in the form of a string of show, the actual use is not convenient to view, we can Hystrix-dashboard for monitoring graphically display means.

Source Reference: https://github.com/yinjihuan/spring-cloud/tree/master/fangjia-hystrix-dashboard

Create a maven project, adding a dependency in the pom.xml dashboard, such as code:

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

Create a startup class, adding @EnableHystrixDashboard notes on startup class, as shown in the code:

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication{
    public static void main(String[] args){
        SpringApplication.run(HystrixDashboardApplication.class,args);
}
}

In the configuration file properties only you need to configure the service name and service port:

spring.application.name=fangjia-hystrix-dashboard
server.port=9011

5.Turbine polymerization cluster data

5.1 Turbine use

Turbine polymerization is a tool server transmits streaming data of the event. Hystrix can only monitor a single node, then on display through dashboard. Actual production are clustered, this time we can monitor metrics Hystrix case of a clustered by Turbine, to discover Hystrix services through Eureka.

First, the increased reliance turbine:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-turbine</artifactId>
        </dependency>
Increase @EnableTurbine and @EnableDiscoveryClient on startup. In the property file configuration is as follows:
eureka.client.serviceUrl.defaultZone=http://yinjihuan:123456@master:8761/eureka/
# Configuration requires aggregated service name 
turbine.appConfig = Substitution-FSH, FSH- House
#Turbine need to aggregate cluster name 
turbine.aggregator.clusterConfig = default
# Cluster name expression 
turbine.clusterNameExpression = new new String ( "default")

Restart the service, you can use http: // localhost: 9011 / turbine.stream to access the monitoring data of the cluster.

5.2 context-path leading to monitoring failure

If a monitored service is set up context-path, it will lead to Turbine unable to obtain monitoring data. Then you need to specify turbine.instanceUrlSuffix to solve this problem in the Turbine: turbine.instanceUrlSuffix = / sub / hystrix.stream

sub for context-path monitoring service. This approach is global configuration, if each service context-path is different, this time it will have some problems, then we need to do a service for each cluster, and then configure the cluster corresponding context-path:

turbine.instanceUrlSuffix. Cluster name = / sub / hystrix.stream

More details can be found in the official documentation: https://github.com/Netflix/Turbine/wiki/Configuration-(1.x ).

Guess you like

Origin www.cnblogs.com/xc-xinxue/p/12459861.html