Before HystrixCommand , you can use a request merger ( HystrixCollapser is an abstract parent class) to merge multiple requests into one and then initiate calls to the backend dependency system.
The figure below shows the number of threads and the number of network connections in two cases: the first is to not use a merger, and the second is to use a request merger (assuming that all links are parallel in a short time window, such as within 10ms).
Why use request merge?
Request merge is used to reduce the number of threads and network connections required to perform concurrent HystrixCommand executions. Request merges are performed automatically and do not force developers to manually coordinate batch requests.
Global context - global context (spanning all Tomcat threads)
This merge type is done at the global application level, so any user's request on any Tomcat thread can be merged together.
For example, if you configure a HystrixCommand to support any user request dependencies to retrieve movie ratings, when any user thread in the same JVM makes such a request, Hystrix adds its request along with any other request to the same collapsed network call.
User request context - request context (single Tomcat thread)
If you configure a HystrixCommand to handle batch requests only for a single user, Hystrix can merge requests in a Tomcat thread (request).
For example, if a user wants to load a bookmark of 300 video objects, instead of executing 300 network requests, Hystrix can merge them into one.
Hystrix is request-scope by default. To use the request-scoped function (request caching, request collapse, request log) you must manage the life cycle of HystrixRequestContext (or implement an alternative HystrixConcurrencyStrategy )
This means that you need to execute the following code before executing a request:
The code copy is as follows: HystrixRequestContext context=HystrixRequestContext.initializeContext();
And execute at the end of the request:
context.shutdown();
In standard JavaWeb applications, you can also use a Servlet filter to initialize this lifecycle
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(); } }}Then configure it in web.xml
<filter> <display-name>HystrixRequestContextServletFilter</display-name> <filter-name>HystrixRequestContextServletFilter</filter-name> <filter-class>com.netflix.hystrix.contrib.requestservlet.HystrixRequestContextServletFilter</filter-class> </filter> <filter-mapping> <filter-name>HystrixRequestContextServletFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
If you are developing springboot, the code is as follows:
@WebFilter(filterName = "hystrixRequestContextServletFilter",urlPatterns = "/*")public class HystrixRequestContextServletFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try{ filterChain.doFilter(servletRequest,servletResponse); } finally { context.shutdown(); } } @Override public void destroy() { }} @SpringBootApplication@EnableDiscoveryClient@EnableFeignClients@EnableHystrix//This is required, otherwise the filter is invalid @ServletComponentScanpublic class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); }}What is the cost of requesting a merge?
The cost of enabling request merge is the delay before the actual command is executed. The biggest cost is the size of the batch window, which is 10ms by default.
If you have a command that takes 5ms to execute and has a 10ms batch window, the worst case of execution time is 15ms. Generally, the request will not occur when the batch window is just opened, so the intermediate value of the time window is half of the time window, in this case it is 5ms.
Whether this cost is worth it depends on the command being executed, and high-latency commands are not affected by a small amount of additional average delay. Moreover, the amount of concurrency of a given command is also key: if fewer than 1 or 2 requests are combined, then the cost is not worth it. In fact, sequential iteration request merge in a single thread will be a major performance bottleneck, and each iteration will wait for the 10ms window waiting time.
However, if a particular command is used in large quantities at the same time and can make dozens or even hundreds of calls in batches at the same time, the cost is usually much more than the increase in the throughput achieved, because Hystrix reduces the number of threads it requires, dependencies. (This passage is not easy to understand. In fact, if concurrency is relatively high, the cost is worth it, because hystrix can save a lot of threads and connection resources).
The process of requesting merge (as shown below)
The theoretical knowledge has been explained. Let’s take a look at the examples below. The examples below integrate eureka+feign+hystrix. For the complete example, please check: https://github.com/jingangwang/micro-service
Entity Class
public class User { private Integer id; private String username; private Integer age; public User() { } public User(Integer id, String username, Integer age) { this.id = id; this.username = username; this.age = age; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { final StringBuffer sb = new StringBuffer("User{"); sb.append("id=").append(id); sb.append(", username='").append(username).append('/''); sb.append(", age=").append(age); sb.append('}'); return sb.toString(); }}Service Provider Code
@RestController@RequestMapping("user")public class UserController { @RequestMapping("getUser") public User getUser(Integer id) { return new User(id, "test", 29); } @RequestMapping("getAllUser") public List<User> getAllUser(String ids){ String[] split = ids.split(","); return Arrays.asList(split) .stream() .map(id -> new User(Integer.valueOf(id),"test"+id,30)) .collect(Collectors.toList()); }}Consumer Code
UserFeignClient
@FeignClient(name = "eureka-provider",configuration = FeignConfiguration.class)public interface UserFeignClient { /** * Find user by id* @param id User id * @return User */ @RequestMapping(value = "user/getUser.json",method = RequestMethod.GET) User findUserById(@RequestParam("id") Integer id); /** * Exceed user list* @param ids id list* @return User collection*/ @RequestMapping(value = "user/getAllUser.json",method = RequestMethod.GET) List<User> findAllUser(@RequestParam("ids") String ids);}UserService (set to global context)
@Servicepublic class UserService { @Autowired private UserFeignClient userFeignClient; /** * maxRequestsInBatch This property sets the maximum number of requests for batch processing, the default value is Integer.MAX_VALUE * timerDelayInMilliseconds This property sets how long it takes to count batch processing, the default is 10ms * @param id * @return */ @HystrixCollapses(collapserKey = "findCollapserKey",scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,batchMethod = "findAllUser",collapserProperties = { @HystrixProperty(name = "timerDelayInMilliseconds",value = "5000" ), @HystrixProperty(name = "maxRequestsInBatch",value = "5" ) }) public Future<User> find(Integer id){ return null; } @HystrixCommand(commandKey = "findAllUser") public List<User> findAllUser(List<Integer> ids){ return userFeignClient.findAllUser(StringUtils.join(ids,",")); }}FeignCollapserController
@RequestMapping("user")@RestControllerpublic class FeignCollapserController { @Autowired private UserService userService; @RequestMapping("findUser") public User getUser(Integer id) throws ExecutionException, InterruptedException { return userService.find(id).get(); } In the above code, we are in the global context (all requests from tomcat threads can be merged), the merge time window is 5s (every request has to wait 5s before the request is initiated), and the maximum number of merges is 5. In postman, we initiate two requests within 5s, but the user ids are different.
localhost:8082/user/findUser.json?id=123189891
localhost:8082/user/findUser.json?id=222222
The result is shown in the figure below, and the two requests are merged into one request batch request.
Let's test the request context (Request-Scope), add HystrixRequestContextServletFilter mentioned above, and modify the UserService
HystrixRequestContextServletFilter
/** * @author wjg * @date 2017/12/22 15:15 */@WebFilter(filterName = "hystrixRequestContextServletFilter",urlPatterns = "/*")public class HystrixRequestContextServletFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try{ filterChain.doFilter(servletRequest,servletResponse); } finally { context.shutdown(); } } @Override public void destroy() { }}UserService (set as request context)
@Servicepublic class UserService { @Autowired private UserFeignClient userFeignClient; /** * maxRequestsInBatch This property sets the maximum number of requests for batch processing, the default value is Integer.MAX_VALUE * timerDelayInMilliseconds This property sets how long it takes to count batch processing, the default is 10ms * @param id * @return */ @HystrixCollapses(collapserKey = "findCollapserKey",scope = com.netflix.hystrix.HystrixCollapser.Scope.REQUEST,batchMethod = "findAllUser",collapserProperties = { @HystrixProperty(name = "timerDelayInMilliseconds",value = "5000" ), @HystrixProperty(name = "maxRequestsInBatch",value = "5" ) }) public Future<User> find(Integer id){ return null; } @HystrixCommand(commandKey = "findAllUser") public List<User> findAllUser(List<Integer> ids){ return userFeignClient.findAllUser(StringUtils.join(ids,",")); }}FeignCollapser2Controller
@RequestMapping("user")@RestControllerpublic class FeignCollapser2Controller { @Autowired private UserService userService; @RequestMapping("findUser2") public List<User> getUser() throws ExecutionException, InterruptedException { Future<User> user1 = userService.find(1989); Future<User> user2= userService.find(1990); List<User> users = new ArrayList<>(); users.add(user1.get()); users.add(user2.get()); return users; }} We type in postman: localhost:8082/user/findUser2.json
You can see that two consecutive calls within a request are merged. Note that this is not possible to use userServer.find(1989).get() directly, otherwise the processing will be performed directly according to synchronization and will not be merged. If two tab pages call the above address at the same time, it is found that two batch requests have been initiated, which means that the scope is the request scope.
References are as follows:
https://github.com/Netflix/Hystrix/wiki/How-To-Use
//www.VeVB.COM/article/140530.htm
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.