In fact, spring, as the largest Java family, does not need to worry about one or two parts being abandoned. Hystrix has been discontinued. It’s just going to lead to more or better parts to replace it, so what we need to do is: ** know what Hystrix does and how it works so it’s easy to replace it.

Article outline:

  1. Why do we need Hystrix?
  2. How does Hystrix address dependency isolation
  3. How to use Hystrix

1. Why Hystrix?

In large and medium-sized distributed systems, the system usually has many dependencies (HTTP,hession,Netty,Dubbo, etc.), as shown below:


Under high concurrent access, the stability of these dependencies has a great impact on the system, but dependencies have many uncontrollable problems, such as slow network connection, busy resources, temporarily unavailable, offline services, etc.

Dependency I with QPS of 50 becomes unavailable, but other dependencies are still available.


When a dependency I blocks, most servers’ thread pools BLOCK, affecting the stability of the entire online service. The diagram below:



Applications in complex distributed architectures have many dependencies and will inevitably fail at some point. High concurrency dependencies fail without isolation, and the current application service is at risk of being dragged down.

1Example: a dependency30System of SOA services, per service99.99% is available.

299.99The % of30The power material99.7%  

30.3% means 100 million requests3.000.00failures

4That translates to about a month2The hour service is not stable.

5As the number of service dependencies increases, the probability of service instability increases exponentially.

Copy the code

Solution: To isolate dependencies,Hystrix is a framework for dealing with dependency isolation, as well as governance and monitoring of dependent services. Netflix developed and successfully used Hystrix on the following scale:

1he Netflix API processes 10+ billion HystrixCommand executions per day using thread isolation.   

2Each API instance has 40+ thread-pools with 5-20 threads in each (most are set to 10).  

3


Copy the code

2. How does Hystrix address dependency isolation

  1. Hystrix uses the Command pattern HystrixCommand(Command) to wrap the dependent call logic, with each Command executed in a separate thread/under signal authorization.

  2. You can configure the dependent call timeout period. The timeout period is generally set to slightly higher than 99.5% average time. When the call times out, the fallback logic is returned or executed directly.

  3. Provide a small thread pool (or signal) for each dependency, and the call will be rejected immediately if the thread pool is full, with no queuing by default. Speed up the failure determination time.

  4. Dependent call results: success, failure (throw exception), timeout, thread rejection, short circuit. Fallback logic is executed when the request fails (exception, rejection, timeout, short circuit).

  5. Provides fuse components that can be run automatically or manually called to stop the current dependence for a period of time (10 seconds). The fuse default error rate threshold is 50%, beyond which it will run automatically.

  6. Provides statistics and monitoring for near real-time dependency

Hystrix relies on the isolation architecture as shown below:

3. How to use Hystrix

  1. Use Maven to introduce Hystrix dependencies
 1<! -- Dependent version -->  

2<hystrix.version>1.3.16</hystrix.version>  

3<hystrix-metrics-event-stream.version>1.1.2</hystrix-metrics-event-stream.version>   

4

5<dependency>  

6     <groupId>com.netflix.hystrix</groupId>  

7     <artifactId>hystrix-core</artifactId>  

8     <version>${hystrix.version}</version>  

9 </dependency>  

10     <dependency>  

11     <groupId>com.netflix.hystrix</groupId>  

12     <artifactId>hystrix-metrics-event-stream</artifactId>  

13     <version>${hystrix-metrics-event-stream.version}</version>  

14 </dependency>  

15<! -- Warehouse address -->  

16<repository>  

17     <id>nexus</id>  

18     <name>local private nexus</name>  

19     <url>http://maven.oschina.net/content/groups/public/</url>  

20     <releases>  

21          <enabled>true</enabled>  

22     </releases>  

23     <snapshots>  

24          <enabled>false</enabled>  

25     </snapshots>  

26</repository>  

Copy the code
  1. Use command mode to encapsulate dependency logic
 1public class HelloWorldCommand extends HystrixCommand<String{  

2    private final String name;  

3    public HelloWorldCommand(String name) {  

4        // minimum configuration: specify the CommandGroup name (CommandGroup)

5        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));  

6        this.name = name;  

7    }  

8    @Override  

9    protected String run(a) {  

10        // The dependency logic is encapsulated in the run() method

11        return "Hello " + name +" thread:" + Thread.currentThread().getName();  

12    }  

13    // Invoke instance

14    public static void main(String[] args) throws Exception{  

15        // Each Command object can only be called once.

16        This instance can only be executed once. Please instantiate a new instance.

17        HelloWorldCommand helloWorldCommand = new HelloWorldCommand("Synchronous-hystrix");  

18        Execute (); helloWorldCommand.queue().get();

19        String result = helloWorldCommand.execute();  

20        System.out.println("result=" + result);  

21

22        helloWorldCommand = new HelloWorldCommand("Asynchronous-hystrix");  

23        // asynchronous call, can freely control the time to obtain results,

24        Future<String> future = helloWorldCommand.queue();  

25        // The get operation cannot exceed the timeout period defined by command. Default :1 second

26        result = future.get(100, TimeUnit.MILLISECONDS);  

27        System.out.println("result=" + result);  

28        System.out.println("mainThread=" + Thread.currentThread().getName());  

29    }  

30

31}  

32    // Result: The run() method executes on a different thread

33    // result=Hello Synchronous-hystrix thread:hystrix-HelloWorldGroup-1

34    // result=Hello Asynchronous-hystrix thread:hystrix-HelloWorldGroup-2

35    // mainThread=main

Copy the code

Queue ()get(timeout, timeunit.milliseconds); Synchronous calls using command-.execute () are equivalent to command-.queue ().get();

  1. Register asynchronous event callback execution
 1// Register observer event interception

2Observable<String> fs = new HelloWorldCommand("World").observe();  

3// Register the result callback event

4fs.subscribe(new Action1<String>() {  

5    @Override  

6    public void call(String result) {  

7         // Execute the result processing,result is the result returned by HelloWorldCommand

8        // The user performs a secondary process on the result.

9    }  

10});  

11// Register full execution lifecycle events

12fs.subscribe(new Observer<String>() {  

13            @Override  

14            public void onCompleted(a) {  

15                // onNext/onError completes the last callback

16                System.out.println("execute onCompleted");  

17            }  

18            @Override  

19            public void onError(Throwable e) {  

20                // Callback when an exception is generated

21                System.out.println("onError " + e.getMessage());  

22                e.printStackTrace();  

23            }  

24            @Override  

25            public void onNext(String v) {  

26                // Get the result and call back

27                System.out.println("onNext: " + v);  

28            }  

29        });  

30/* Run result

31call execute result=Hello observe-hystrix thread:hystrix-HelloWorldGroup-3 

32onNext: Hello observe-hystrix thread:hystrix-HelloWorldGroup-3 

33execute onCompleted 

34* /
  

Copy the code
  1. Use Fallback() to provide a downgrade strategy
 1// Override HystrixCommand's getFallback method to implement the logic

2public class HelloWorldCommand extends HystrixCommand<String{  

3    private final String name;  

4    public HelloWorldCommand(String name) {  

5        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup"))  

6                /* Configure the dependency timeout,500 ms */  

7                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionIsolationThreadTimeoutInMilliseconds(500)));  

8        this.name = name;  

9    }  

10    @Override  

11    protected String getFallback(a) {  

12        return "exeucute Falled";  

13    }  

14    @Override  

15    protected String run(a) throws Exception {  

16        //sleep 1 second, the call will timeout

17        TimeUnit.MILLISECONDS.sleep(1000);  

18        return "Hello " + name +" thread:" + Thread.currentThread().getName();  

19    }  

20    public static void main(String[] args) throws Exception{  

21        HelloWorldCommand command = new HelloWorldCommand("test-Fallback");  

22        String result = command.execute();  

23    }  

24}  

25/* Result: the getFallback() call runs

26getFallback executed 

27* /
  

Copy the code

NOTE: besides HystrixBadRequestException abnormalities, all from the run () method is the exception thrown all count failure, and triggers the relegation getFallback () and logic circuit breaker.

1HystrixBadRequestException used in illegal abnormal parameters or system failure, etc should not trigger the fallback logic.

Copy the code
  1. Dependency name :CommandKey
1public HelloWorldCommand(String name) {  

2        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))  

3                /* HystrixCommandKey factory defines the dependency name */  

4                .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld")));  

5        this.name = name;  

6    }  

Copy the code

NOTE: Each CommandKey represents a dependency abstraction, and the same dependency uses the same CommandKey name. The root of dependency isolation is to isolate dependencies of the same CommandKey.

  1. Dependency group: The CommandGroup command is used to group dependency operations for easy statistics and summary.
1// Use HystrixCommandGroupKey factory definition

2public HelloWorldCommand(String name) {  

3    Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup"))  

4}  

Copy the code

NOTE: CommandGroup is a minimum configured parameter for each command. Without specifying ThreadPoolKey, the literal is used to distinguish between different dependent thread pools/signals.

  1. Thread pool/signal :ThreadPoolKey
1public HelloWorldCommand(String name) {  

2        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))  

3                .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"))  

4                /* Use HystrixThreadPoolKey factory to define the thread pool name */  

5                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")));  

6        this.name = name;  

7    }  

Copy the code

CommandGroup is used when isolating the same business dependency, but HystrixThreadPoolKey is used when isolating different remote calls to the same dependency, such as redis and HTTP.

1HystrixThreadPoolKey can be used for resource isolation when the services are the same group.

Copy the code
  1. Request Cache request-cache
 1public class RequestCacheCommand extends HystrixCommand<String{  

2    private final int id;  

3    public RequestCacheCommandint id) {  

4        super(HystrixCommandGroupKey.Factory.asKey("RequestCacheCommand"));  

5        this.id = id;  

6    }  

7    @Override  

8    protected String run(a) throws Exception {  

9        System.out.println(Thread.currentThread().getName() + " execute id=" + id);  

10        return "executed=" + id;  

11    }  

12    // Rewrite the getCacheKey method to implement the logic that distinguishes different requests

13    @Override  

14    protected String getCacheKey(a) {  

15        return String.valueOf(id);  

16    }  

17

18    public static void main(String[] args){  

19        HystrixRequestContext context = HystrixRequestContext.initializeContext();  

20        try {  

21            RequestCacheCommand command2a = new RequestCacheCommand(2);  

22            RequestCacheCommand command2b = new RequestCacheCommand(2);  

23            Assert.assertTrue(command2a.execute());  

24            //isResponseFromCache determines if the result is in the cache

25            Assert.assertFalse(command2a.isResponseFromCache());  

26            Assert.assertTrue(command2b.execute());  

27            Assert.assertTrue(command2b.isResponseFromCache());  

28        } finally {  

29            context.shutdown();  

30        }  

31        context = HystrixRequestContext.initializeContext();  

32        try {  

33            RequestCacheCommand command3b = new RequestCacheCommand(2);  

34            Assert.assertTrue(command3b.execute());  

35            Assert.assertFalse(command3b.isResponseFromCache());  

36        } finally {  

37            context.shutdown();  

38        }  

39    }  

40}  

Copy the code

NOTE: Request caching enables (CommandKey/CommandGroup) results to be directly shared in the same case, reducing dependency calls, and improving performance in high concurrency and high CacheKey collision rates.

In the Servlet container, you can directly apply the Filter mechanism Hystrix request context

 1public class HystrixRequestContextServletFilter implements Filter {  

2    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)   

3     throws IOException, ServletException 
{  

4        HystrixRequestContext context = HystrixRequestContext.initializeContext();  

5        try {  

6            chain.doFilter(request, response);  

7        } finally {  

8            context.shutdown();  

9        }  

10    }  

11}  

12<filter>  

13      <display-name>HystrixRequestContextServletFilter</display-name>  

14      <filter-name>HystrixRequestContextServletFilter</filter-name>  

15      <filter-class>com.netflix.hystrix.contrib.requestservlet.HystrixRequestContextServletFilter</filter-class>  

16    </filter>  

17    <filter-mapping>  

18      <filter-name>HystrixRequestContextServletFilter</filter-name>  

19      <url-pattern/ * < / >url-pattern>  

20   </filter-mapping>  

21


Copy the code
  1. SEMAPHORE isolation :SEMAPHORE isolates local code or can quickly return remote calls (such as memcached,redis) directly using SEMAPHORE isolation, reducing thread isolation overhead.
 1public class HelloWorldCommand extends HystrixCommand<String{  

2    private final String name;  

3    public HelloWorldCommand(String name) {  

4        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup"))  

5                /* Configure the semaphore isolation mode, default thread pool isolation */  

6.andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionIsolationStrategy(HystrixCommandProperties. ExecutionIsolationStrategy.SEMAPHORE)));

7        this.name = name;  

8    }  

9    @Override  

10    protected String run(a) throws Exception {  

11        return "HystrixThread:" + Thread.currentThread().getName();  

12    }  

13    public static void main(String[] args) throws Exception{  

14        HelloWorldCommand command = new HelloWorldCommand("semaphore");  

15        String result = command.execute();  

16        System.out.println(result);  

17        System.out.println("MainThread:" + Thread.currentThread().getName());  

18    }  

19}  

20/** Run result

21 HystrixThread:main 

22 MainThread:main 

23* /
  

Copy the code
  1. Fallback Logical command nesting


Use scenario: Used when fallback logic involves network access, such as cache access.

 1public class CommandWithFallbackViaNetwork extends HystrixCommand<String{  

2    private final int id;  

3

4    protected CommandWithFallbackViaNetwork(int id) {  

5        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceX"))  

6                .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueCommand")));  

7        this.id = id;  

8    }  

9

10    @Override  

11    protected String run(a) {  

12        // RemoteService.getValue(id);

13        throw new RuntimeException("force failure for example");  

14    }  

15

16    @Override  

17    protected String getFallback(a) {  

18        return new FallbackViaNetwork(id).execute();  

19    }  

20

21    private static class FallbackViaNetwork extends HystrixCommand<String{  

22        private final int id;  

23        public FallbackViaNetwork(int id) {  

24            super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceX"))  

25                    .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueFallbackCommand"))  

26                    // Use different thread pools for isolation to prevent the upper thread pool from running full and affecting the degradation logic.

27                    .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("RemoteServiceXFallback")));  

28            this.id = id;  

29        }  

30        @Override  

31        protected String run(a) {  

32            MemCacheClient.getValue(id);  

33        }  

34

35        @Override  

36        protected String getFallback(a) {  

37            return null;  

38        }  

39    }  

40}  

Copy the code

NOTE: Dependent and degraded calls are separated by different thread pools, preventing the upper thread pool from running full and affecting the secondary degraded logical calls.

  1. Shows the fallback logic to be invoked for special business processing
 1public class CommandFacadeWithPrimarySecondary extends HystrixCommand<String{  

2    private final static DynamicBooleanProperty usePrimary = DynamicPropertyFactory.getInstance().getBooleanProperty("primarySecondary.usePrimary".true);  

3    private final int id;  

4    public CommandFacadeWithPrimarySecondary(int id) {  

5        super(Setter  

6                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX"))  

7                .andCommandKey(HystrixCommandKey.Factory.asKey("PrimarySecondaryCommand"))  

8                .andCommandPropertiesDefaults(  

9                        HystrixCommandProperties.Setter()  

10                                .withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)));  

11        this.id = id;  

12    }  

13    @Override  

14    protected String run(a) {  

15        if (usePrimary.get()) {  

16            return new PrimaryCommand(id).execute();  

17        } else {  

18            return new SecondaryCommand(id).execute();  

19        }  

20    }  

21    @Override  

22    protected String getFallback(a) {  

23        return "static-fallback-" + id;  

24    }  

25    @Override  

26    protected String getCacheKey(a) {  

27        return String.valueOf(id);  

28    }  

29    private static class PrimaryCommand extends HystrixCommand<String{  

30        private final int id;  

31        private PrimaryCommand(int id) {  

32            super(Setter  

33                    .withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX"))  

34                    .andCommandKey(HystrixCommandKey.Factory.asKey("PrimaryCommand"))  

35                    .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("PrimaryCommand"))  

36                    .andCommandPropertiesDefaults(  

37                            // we default to a 600ms timeout for primary

38                            HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(600)));  

39            this.id = id;  

40        }  

41        @Override  

42        protected String run(a) {  

43            // perform expensive 'primary' service call

44            return "responseFromPrimary-" + id;  

45        }  

46    }  

47    private static class SecondaryCommand extends HystrixCommand<String{  

48        private final int id;  

49        private SecondaryCommand(int id) {  

50            super(Setter  

51                    .withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX"))  

52                    .andCommandKey(HystrixCommandKey.Factory.asKey("SecondaryCommand"))  

53                    .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("SecondaryCommand"))  

54                    .andCommandPropertiesDefaults(  

55                            // we default to a 100ms timeout for secondary

56                            HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(100)));  

57            this.id = id;  

58        }  

59        @Override  

60        protected String run(a) {  

61            // perform fast 'secondary' service call

62            return "responseFromSecondary-" + id;  

63        }  

64    }  

65    public static class UnitTest {  

66        @Test  

67        public void testPrimary(a) {  

68            HystrixRequestContext context = HystrixRequestContext.initializeContext();  

69            try {  

70                ConfigurationManager.getConfigInstance().setProperty("primarySecondary.usePrimary".true);  

71                assertEquals("responseFromPrimary-20".new CommandFacadeWithPrimarySecondary(20).execute());  

72            } finally {  

73                context.shutdown();  

74                ConfigurationManager.getConfigInstance().clear();  

75            }  

76        }  

77        @Test  

78        public void testSecondary(a) {  

79            HystrixRequestContext context = HystrixRequestContext.initializeContext();  

80            try {  

81                ConfigurationManager.getConfigInstance().setProperty("primarySecondary.usePrimary".false);  

82                assertEquals("responseFromSecondary-20".new CommandFacadeWithPrimarySecondary(20).execute());  

83            } finally {  

84                context.shutdown();  

85                ConfigurationManager.getConfigInstance().clear();  

86            }  

87        }  

88    }  

89}  

Copy the code

NOTE: fallback is applicable to scenarios with special requirements. Fallback is used for business processing. Fallback is no longer responsible for fallback.

  1. Command call merge :HystrixCollapser

Command call merge allows multiple requests to be merged into one thread/signal for batch execution.

The execution flow chart is as follows:

 1public class CommandCollapserGetValueForKey extends HystrixCollapser<List<String>, String.Integer{  

2    private final Integer key;  

3    public CommandCollapserGetValueForKey(Integer key) {  

4        this.key = key;  

5    }  

6    @Override  

7    public Integer getRequestArgument(a) {  

8        return key;  

9    }  

10    @Override  

11    protected HystrixCommand<List<String>> createCommand(final Collection<CollapsedRequest<String, Integer>> requests) {  

12        // Create a return command object

13        return new BatchCommand(requests);  

14    }  

15    @Override  

16    protected void mapResponseToRequests(List<String> batchResponse, Collection<CollapsedRequest<String, Integer>> requests) {  

17        int count = 0;  

18        for (CollapsedRequest<String, Integer> request : requests) {  

19            // Manually match request and response

20            request.setResponse(batchResponse.get(count++));  

21        }  

22    }  

23    private static final class BatchCommand extends HystrixCommand<List<String>> {  

24        private final Collection<CollapsedRequest<String, Integer>> requests;  

25        private BatchCommand(Collection<CollapsedRequest<String, Integer>> requests) {  

26                super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))  

27                    .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueForKey")));  

28            this.requests = requests;  

29        }  

30        @Override  

31        protected List<String> run(a) {  

32            ArrayList<String> response = new ArrayList<String>();  

33            for (CollapsedRequest<String, Integer> request : requests) {  

34                response.add("ValueForKey: " + request.getArgument());  

35            }  

36            return response;  

37        }  

38    }  

39    public static class UnitTest {  

40        HystrixRequestContext context = HystrixRequestContext.initializeContext();  

41        try {  

42            Future<String> f1 = new CommandCollapserGetValueForKey(1).queue();  

43            Future<String> f2 = new CommandCollapserGetValueForKey(2).queue();  

44            Future<String> f3 = new CommandCollapserGetValueForKey(3).queue();  

45            Future<String> f4 = new CommandCollapserGetValueForKey(4).queue();  

46            assertEquals("ValueForKey: 1", f1.get());  

47            assertEquals("ValueForKey: 2", f2.get());  

48            assertEquals("ValueForKey: 3", f3.get());  

49            assertEquals("ValueForKey: 4", f4.get());  

50            assertEquals(1, HystrixRequestLog.getCurrentRequest().getExecutedCommands().size());  

51HystrixCommand<? > command = HystrixRequestLog.getCurrentRequest().getExecutedCommands().toArray(newHystrixCommand<? > [1[])0];  

52            assertEquals("GetValueForKey", command.getCommandKey().name());  

53            assertTrue(command.getExecutionEvents().contains(HystrixEventType.COLLAPSED));  

54            assertTrue(command.getExecutionEvents().contains(HystrixEventType.SUCCESS));  

55        } finally {  

56         context.shutdown();  

57        }     

58    }  

59}  

Copy the code

HystrixCollapser can be used to merge multiple requests into a thread or even into a connection, reducing the number of thread interactions and IO counts, but ensure that they belong to the same dependency.

The original source: http://hot66hot.iteye.com/blog/2155036

Find this article helpful? Please share with more people to pay attention to “programming without boundaries”, improve the installation force skills