Tag: Ext group starts Netflix CTI Map stream via start
Hystrix was originally developed by Netflix's API team to improve the elasticity and performance of the API, which was widely acclaimed in 2012.
If your application is a separate application, it is almost free of the problem of open- circuit .
But in a distributed environment, where a complex dependency is applied to each application, an unstable service can slow down the services that depend on it.
In short, it is to isolate access between services, intercept errors (including timeouts) before they are propagated, and provide the appropriate processing logic to make this distributed application more resilient.
Hystrix is the Lib used to solve this problem, which helps developers to control communication between services more conveniently.
In a distributed system, the third-party RPC API that you use may provide the ability to intercept service traffic, but it usually does not involve all aspects, and cannot be taken out separately for use by other APIs.
And Hystrix will provide these:
- Provides protection, fault tolerance for service communications
- Prevent error propagation in complex dependency chains
- Fast failure
- To destroy according to a specific event.
- Support downgrade
- Near real-time monitoring
Assuming that you have dozens of or even hundreds of services in your distributed system, even though each service can guarantee 99.99 availability, the complexity of dependencies and the sheer number of individual services dependent on the number of requests will be disastrous.
If I set a time-out for the service itself, the service will not have the same weights for different relying parties.
Assume that both services A and B depend on service C. For a, it may depend on many services, but C cannot give up when it responds within 1 seconds. And for B,c is a vital service, unless the business data is abnormal, it must not stop halfway.
If the C setting has a time-out of 30s, then A and B will also need to wait for 30s, which is obviously unreasonable. While waiting for these requests will cost a lot of resources to see the specific situation, the worst case is to drag down the entire application.
Therefore, both latency (lagency) and failure (failure) need to be isolated.
How does Hystrix do this?
- The service is invoked through Hystrixcommand on a separate thread.
- The time-out is controlled by the caller.
- To maintain a small thread pool for each dependency, the line Cheng can reject the request instead of putting the request into the queue.
- Distinguish between events, such as Successe, failure, timeout, rejection, and corresponding callbacks for different events.
- When the failure ratio exceeds the specified threshold, the circuit breaker (Circuit-breaker) is started, blocking access to specific dependencies over time.
Here are a few simple examples to illustrate.
Getting Started
Through a few simple examples, there is a superficial understanding of hystrix.
First, add the following dependencies
compile group: ‘com.netflix.hystrix‘, name: ‘hystrix-core‘, version: ‘1.5.10‘
Refer to the following main
package com.kavlez.lab.hystrix;import com.netflix.hystrix.HystrixCommand;import com.netflix.hystrix.HystrixCommandGroupKey;/** * @author Kavlez */public class Hello { public static void main(String[] args) { HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("ExampleGroup"); HystrixCommand<String> hystrixCommand = new HystrixCommand<String>(groupKey) { @Override protected String run() throws Exception { return "hi"; } }; System.out.printf("exec command ... result = %s", hystrixCommand.execute()); }}
The overwritten run () is the abstract method defined in Hystrixcommand, which is also called in run when the dependent service is invoked.
Describe the two classes that appear in the example above.
Like group, command is also named. Default to Class name
getClass().getSimpleName();
But does not provide a corresponding setter, just provides a construction method
protected HystrixCommand(Setter setter)
Therefore, if you want to specify a command name, refer to the following
final HystrixCommand.Setter setter = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")) .andCommandKey(HystrixCommandKey.Factory.asKey("ExampleCommand"));HystrixCommand<String> hystrixCommand = new HystrixCommand<String>(setter) { //...};
Asynchronous execution
As in the above example, we can execute the command through Execute () , which is a synchronous execution method.
If you need to execute asynchronously, you only need to replace execute () with queue ( ).
Future<String> future = hystrixCommand.queue();try { System.out.printf("exec command ... result = %s\n", future.get());} catch (ExecutionException e) { e.printStackTrace();}
In fact, execute () is just a queue (). get ().
Observe
Try to execute gradle dependencies , print as follows
compile - Dependencies for source set ‘main‘.+--- org.slf4j:slf4j-api:1.7.21\--- com.netflix.hystrix:hystrix-core:1.5.10 +--- org.slf4j:slf4j-api:1.7.0 -> 1.7.21 +--- com.netflix.archaius:archaius-core:0.4.1 | +--- commons-configuration:commons-configuration:1.8 | | +--- commons-lang:commons-lang:2.6 | | \--- commons-logging:commons-logging:1.1.1 | \--- org.slf4j:slf4j-api:1.6.4 -> 1.7.21 +--- io.reactivex:rxjava:1.2.0 \--- org.hdrhistogram:HdrHistogram:2.1.9
What I want to say is that Hystrix relies on Rxjava.
Where observe is a more typical use, Hystrixcommand provides two methods observe and toobservable, which are described by the authorities as follows.
- Observe ()-returns a "hot" Observable that executes the command immediately, though because the Observable it filtered th Rough a replaysubject you aren't in the danger of losing any items that it emits before we have a chance to subscribe
- Toobservable ()-returns a "cold" Observable that won ' t execute the command and begin emitting it results until you SUBSC Ribe to the Observable
Obviously, if it is through toobservable, the same command instance cannot be subscribe multiple times.
Although both return Observable objects, there is a slight difference in behavior.
But essentially observe () is almost equivalent to toobservable (). Subscribe (subject)
Here is an example of a command that can have multiple subscriber because it is through observe ():
Package Com.kavlez.lab.hystrix;import Com.netflix.hystrix.hystrixcommand;import Com.netflix.hystrix.hystrixcommandgroupkey;import Rx. Observable;import Rx. observer;/** * @author Kavlez */public class Helloobservable {public static void main (string[] args) {HYSTRIXC Ommandgroupkey Groupkey = HystrixCommandGroupKey.Factory.asKey ("Examplegroup"); hystrixcommand<string> Hystrixcommand = new Hystrixcommand<string> (groupkey) {@Override Protected String Run () throws Exception {return "HI"; } }; Observable<string> observe = Hystrixcommand.observe (); Observe.subscribe (S-and {System.out.printf ("from action1...%s\n", s); }); Observe.subscribe (New observer<string> () {@Override public void oncompleted () { System.out.println ("Completed ..."); } @Override public void OnError (Throwable e) { System.out.printf ("error...%s\n", E.getmessage ()); } @Override public void OnNext (String s) {System.out.printf ("from next...%s\n", s); } }); }}
Fallback
Try writing a thread.sleepin Run, or add a breakpoint to allow the program to pause for a while.
J.u.c.timeoutexception and Hystrixruntimeexception appear, and the latter prompts
Timed-out and no fallback available.
This is because Hystrixcommand's getfallback () defaults to
protected R getFallback() { throw new UnsupportedOperationException("No fallback available."); }
That being the case, we just need to override this method to achieve a downgrade (degradation).
For example, replace the previous example with the following:
HystrixCommand<String> hystrixCommand = new HystrixCommand<String>(groupKey) { @Override protected String run() throws Exception { Thread.sleep(1000); return "hi"; } @Override protected String getFallback() { return "hi, sorry i am late..."; }};
Getfallback does not provide parameters, which means that fallback not only occurs in a timeout situation, failure, timeout, thread pool rejection can trigger fallback.
Circuit Breaker
Next talk about how command interacts with the circuit breaker (circuit breaker).
The Hystrixcommand property and the default value can refer to the abstract class hystrixcommandproperties, where Circuitbreaker begins with the breaker-related properties.
Here is a list of the 3 properties of the circuit breakers, respectively:
- Hystrixcommandproperties.circuitbreakerrequestvolumethreshold (): Request capacity threshold
- Hystrixcommandproperties.circuitbreakererrorthresholdpercentage (): Error ratio threshold
- Hystrixcommandproperties.circuitbreakersleepwindowinmilliseconds (): Status duration
The workflow is roughly as follows:
- Assume that the request volume threshold is reached, that is, Metrics.healthCounts.totalCount is greater than the item
- And the percentage of failures has also reached error percentage, which defaults to 50%
- At this point, the state of the circuit breaker changes from CLOSED to OPEN
- Once the circuit breaker status becomes open, the received request will be completely broken
- After the recovery time, which is the Sleep window in milliseconds (default is 5s), the circuit breaker changes from OPEN to half-open state.
- If the next request fails after the change to Half-open, it becomes back to the open state and vice versa to closed.
Request Cache
Not before. Hystrix also provides a feature where the command can be cached by overwrite getCacheKey the request.
The method returns null by default, that is, not cached.
If all n commands are in the same request scope, only one command is executed and the rest of the n-1 are cached.
The code reference is as follows
Package Com.kavlez.lab.hystrix;import Com.netflix.hystrix.hystrixcommand;import Com.netflix.hystrix.hystrixcommandgroupkey;import Com.netflix.hystrix.hystrixcommandkey;import com.netflix.hystrix.strategy.concurrency.hystrixrequestcontext;/** * @author Kavlez */public class ReqCache {static CL Hellocommand extends hystrixcommand<string> {private static final hystrixcommand.setter Setter = HystrixCommand.Setter.withGroupKey (HystrixCommandGroupKey.Factory.asKey ("Examplegroup")). A Ndcommandkey (HystrixCommandKey.Factory.asKey ("Examplecommand")); Private String Requestkey; Protected Hellocommand (String Requestkey) {super (setter); This.requestkey = Requestkey; } @Override protected String run () throws Exception {return null; } @Override protected String Getcachekey () {return this.requestkey; }} public static void Main (STring[] args) {hystrixrequestcontext.initializecontext (); Hellocommand hello1 = new Hellocommand ("Billy"); Hello1.execute (); System.out.println (Hello1.isresponsefromcache ()); Hello1 = new Hellocommand ("Billy"); Hello1.execute (); System.out.println (Hello1.isresponsefromcache ()); Hello1 = new Hellocommand ("Van"); Hello1.execute (); System.out.println (Hello1.isresponsefromcache ()); }}
Note this line
HystrixRequestContext.initializeContext();
Missing Hystrixrequestcontext will prompt illegal state.
Request collapsing
Hystrix provides a feature called collapse that merges multiple requests to make it easier to limit multiple requests to the same time windows.
The official example is to get 300 movies in the collection, similar scenes are really common.
Or a little more complicated, like I'm going to get staff information for 300 movies, and several different movies are likely to have the same staff.
Maybe I can ... First get a list of 300 movies, loop them, get a list of workers, and then loop them back, requesting the worker's rest API ... Whether there are multiple identical movies and workers in the list.
Or I can just design a set of APIs for this scenario, specifically to get information about the staff of each movie in the movie list.
But this is obviously a dumb way to acquiesce to such an approach that could lead to a growing number of inexplicable APIs.
Therefore, it is worthwhile to abstract a layer of collapsing layers in order to cope with such scenes.
As a result, REST APIs and entity classes can remain simple, and developers only need to use hystrixcollapser .
Assuming multiple commands make the same request at the same time, Collapser can merge requests, bulk requests, and distribute the results to individual commands.
Refer to the example below
Package Com.kavlez.lab.hystrix;import Com.google.common.collect.lists;import Com.google.common.collect.Maps; Import Com.netflix.hystrix.hystrixcollapser;import Com.netflix.hystrix.hystrixcollapserkey;import Com.netflix.hystrix.hystrixcommand;import Com.netflix.hystrix.hystrixcommandgroupkey;import Com.netflix.hystrix.strategy.concurrency.hystrixrequestcontext;import Java.util.collection;import Java.util.List ; Import Java.util.map;import Java.util.concurrent.executionexception;import Java.util.concurrent.future;import java.util.stream.collectors;/** * @author Kavlez * @since 5/12/17. */public class Collapserexample {private static final string[] names = {"Protos", "Terran", "Hulk", "Anderson", "Uriah "," Gegard "," Velasquez "," Mcgregor "," Jose "}; Static class User {private int id; private int code; private String name; public User (int id, int code, String name) {this.id = ID; This.code = code; THIS.name = name; } public int getId () {return id; } public void setId (int id) {this.id = ID; } public int GetCode () {return code; } public void Setcode (int code) {This.code = code; } public String GetName () {return name; } public void SetName (String name) {this.name = name; }} static Class Usercollapser extends Hystrixcollapser<map<integer, User>, User, integer> {priv ate int userId; Private static final Setter setter = Setter.withcollapserkey (HystrixCollapserKey.Factory.asKey ("Usercollapser")); Public usercollapser (int userId) {super (setter); This.userid = userId; } @Override Public Integer getrequestargument () {return this.userid; } @Override protected Hystrixcommand<map<integer, user>> CreateCommand (CollecTion<collapsedrequest<user, integer>> collapsedrequests) {return new Userbatchcommand (CollapsedReq Uests.stream (). Map (Request, {System.out.println ("arg mapped ..."); return Request.getargument (); }). Collect (Collectors.tolist ())); } @Override protected void Mapresponsetorequests (Map<integer, user> batchresponse, Col Lection<collapsedrequest<user, integer>> collapsedrequests) {for (Collapsedrequest<user, Intege R> request:collapsedrequests) {Integer userId = request.getargument (); Request.setresponse (Batchresponse.get (userId)); }}} static class Userbatchcommand extends Hystrixcommand<map<integer, user>> {private L ist<integer> IDs; Private final static Setter setter = Setter.withgroupkey (HystrixCommandGroupKey.Factory.asKey ("Userbatchgroup")); Public useRbatchcommand (list<integer> IDs) {super (setter); This.ids = IDs; } @Override protected Map<integer, user> Run () throws Exception {return this.getusers (); } Map<integer, User> getusers () {map<integer, user> users = Maps.newhashmap (); for (Integer id:ids) {int randomcode = (int) (Math.random () * 100); Users.put (ID, new User (ID, randomcode, names[randomcode% names.length])); } return users; }} public static void Main (string[] args) throws Executionexception, interruptedexception {Hystrixrequestco Ntext.initializecontext (); List<future<user>> futures = lists.newarraylist (1, 1, 1, 1, 2, 2, 2, 3, 3, 4). Stream (). Map (use RId, New Usercollapser (userId). Queue ()). Collect (Collectors.tolist ()); for (future<user> future:futures) {future.get (); } }}
The 3 generic types specified when the Hystrixcollapser is overwrite, in turn
- Batch command return type
- Response type
- Request parameter Type
Inheritance Hystrixcollapser need to overwrite 3 methods, respectively
Protected abstract Hystrixcommand CreateCommand (Collection<collapsedrequest<responsetype, Requestargumenttype>> requests); The factory method, which is used to create a Hystrixcommand object, or a command that is dedicated to processing bulk requests.
In most cases, the command created by the method is useless once it is executed once, so a new instance is usually returned.
Because it is used to process bulk requests, the Collapsedrequest collection is generally passed to the command.
Public abstract Requestargumenttype getrequestargument (); This method provides the arguments passed to Hystrixcommand, and if you need to pass multiple arguments, encapsulate to an object.
protected abstract void Mapresponsetorequests (Batchreturntype batchresponse, collection<collapsedrequest< Responsetype, requestargumenttype>> requests); CreateCommand created the corresponding command, The command ends with a call to Mapresponsetorequests, which maps batchreturntype to Collection<collapsedrequest<responsetype, Requestargumenttype>> requests.
Request Context Setup
The content mentioned above involves the request context.
In fact, some of Hystrix's functions require the request context, which is request-scoped features.
For example, in the example above, there is a line in the Main method
HystrixRequestContext context = HystrixRequestContext.initializeContext();
This requires the developer to manage the Hystrixrequestcontext lifecycle on demand.
The request is more common in Web applications, such as implementing a servlet filter, which is managed in the Dofilter method.
public class HystrixRequestContextServletFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { chain.doFilter(request, response); } finally { context.shutdown(); } }}
Netflix Hystrix-Quick Start