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:
- Why do we need Hystrix?
- How does Hystrix address dependency isolation
- 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
Hystrix uses the Command pattern HystrixCommand(Command) to wrap the dependent call logic, with each Command executed in a separate thread/under signal authorization.
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.
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.
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).
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.
Provides statistics and monitoring for near real-time dependency
Hystrix relies on the isolation architecture as shown below:
3. How to use Hystrix
- 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
- 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();
- 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
- 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
- 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.
- 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.
- 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
- Request Cache request-cache
1public class RequestCacheCommand extends HystrixCommand<String> {
2 private final int id;
3 public RequestCacheCommand( int 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
- 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
- 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.
- 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.
- 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