1. Introduce Hystrix
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
2. Enable Hystrix function on the startup class
@EnableHystrix
3. Request to merge the implementation code
import com.bzcst.bop.oms.orm.model.po.Product; import com.bzcst.bop.oms.orm.service.ProductService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; import java.util.concurrent.Future; /** * @author Xiang Zhenhua * @date 2023/10/26 14:37 */ @Slf4j @Service public class BatchService { @Resource private ProductService productService; @HystrixCollapser( batchMethod = "getByIds", scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL, collapserProperties = { @HystrixProperty(name = HystrixPropertiesManager.MAX_REQUESTS_IN_BATCH, value = "10"), @HystrixProperty(name = HystrixPropertiesManager.TIMER_DELAY_IN_MILLISECONDS, value = "20") } ) public Future<Product> getById(Long id) { return null; } @HystrixCommand public List<Product> getByIds(List<Long> ids) { log.info("Batch query ---> " + ids); return productService.listByIds(ids); } }
The getById(Long id) method will not enter.
Among them, ProductService is a query interface based on mybatisplus.
import com.bzcst.bop.oms.orm.model.po.Product; import com.baomidou.mybatisplus.extension.service.IService; public interface ProductService extends IService<Product> { }
4. Call
import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import com.bzcst.bop.oms.orm.model.po.Product; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * @author Xiang Zhenhua * @date 2023/10/26 14:32 */ @RestController public class TestController { @Resource private BatchService batchService; @GetMapping("/product/{id}") public Object product(@PathVariable Long id) throws Exception { Future<Product> productFuture = batchService.getById(id); return productFuture.get(); } public static void main(String[] args) { //Create thread pool ExecutorService executorService = Executors.newFixedThreadPool(20); // Simulate 20 concurrent requests CountDownLatch countDownLatch = new CountDownLatch(20); for (int i = 1; i <= 20; i + + ) { int fi = i; executorService.execute(() -> { try { countDownLatch.await(); HttpRequest httpRequest = HttpRequest.get("http://localhost:8080/bop-oms/product/" + fi); String body = httpRequest.execute().body(); System.out.println(fi + " ---> " + body); } catch (Exception e) { } }); countDownLatch.countDown(); } } }
Log records can be seen as batch processing
2023-10-26 17:54:31.708 INFO 10524 --- [-BatchService-9] c.bzcst.bop.oms.controller.BatchService: Batch query ---> [10, 5, 9, 15 , 4, 12, 11, 16, 8, 14] Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@58dfec5b] was not registered for synchronization because synchronization is not active parser sql: SELECT id, xxx FROM oms_product WHERE id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 2023-10-26 17:54:31.712 INFO 10524 --- [-BatchService-8] c.bzcst.bop.oms.controller.BatchService: Batch query ---> [3, 18, 1, 13, 7, 19, 20, 2, 6, 17] Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4235ea19] was not registered for synchronization because synchronization is not active parser sql: SELECT id, xxx FROM oms_product WHERE id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) JDBC Connection [HikariProxyConnection@935862209 wrapping com.mysql.cj.jdbc.ConnectionImpl@130542a5] will not be managed by Spring ==> Preparing: SELECT id, xxx FROM oms_product WHERE id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ==> Parameters: 10(Long), 5(Long), 9(Long), 15(Long), 4(Long), 12(Long), 11(Long), 16(Long), 8(Long), 14(Long) JDBC Connection [HikariProxyConnection@205926351 wrapping com.mysql.cj.jdbc.ConnectionImpl@67b600cc] will not be managed by Spring ==> Preparing: SELECT id, xxx FROM oms_product WHERE id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ==> Parameters: 3(Long), 18(Long), 1(Long), 13(Long), 7(Long), 19(Long), 20(Long), 2(Long), 6(Long), 17(Long) <== Columns: id, tenant_id, category_id, name, code, price, ladder_price, is_ladder_price, unit, is_enabled, description, version, create_by, create_time, update_by, update_time, delete_flag <== Row: 4, 1, 0, transfer power supply, 4, 4.00, , 0, , 1, , 1, , 2022-12-29 17:01:09, , 2022-12-29 17:01:09 , 0 <== Row: 5, 1, 0, sewer, XSD, 5.00, , 0, , 1, , 1, , 2023-05-23 18:24:30, , 2023-05-23 18:24:30 , 0 <== Row: 8, 1, 0, door card deposit, 0302, 8.00, , 0, , 1, , 1, , 2023-03-04 10:57:50, , 2023-04-14 09:53: 17,0 <== Row: 9, 1, 0, tiered unit price, 12321, 9.00, , 1, , 1, , 1, , 2023-10-13 16:39:09, , 2023-10-13 16:39:09 , 0 <== Row: 10, 1, 0, Assad, 123333, 10.00, , 0, , 1, , 1, , 2023-09-14 10:43:01, , 2023-09-14 10:43: 01, 0 <== Row: 11, 1, 0, subsidized vacancy fee, 444, 11.00, , 0, , 1, , 1, , 2023-03-02 17:05:10, , 2023-03-02 17:05: 10, 0 <== Row: 12, 1, 0, street light fee, B<N, 12.00, , 0, , 1, , 1, , 2023-03-03 10:55:23, , 2023-03-03 10:55 :23,0 <== Row: 14, 1, 0, scattered properties, 3, 14.00, , 0, , 1, , 1, , 2023-03-02 16:50:05, , 2023-03-02 16:50:05 , 0 <== Row: 15, 1, 0, sporadic electricity bill, 2, 15.00, , 0, , 1, , 1, , 2023-03-02 16:49:46, , 2023-03-02 16:49:46 , 0 <== Row: 16, 1, 0, non-domestic garbage removal fee, 16, 16.00, , 0, , 1, , 1, , 2022-12-29 17:01:09, , 2022-12-29 17: 01:09, 0 <== Total: 10 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@58dfec5b] <== Columns: id, tenant_id, category_id, name, code, price, ladder_price, is_ladder_price, unit, is_enabled, description, version, create_by, create_time, update_by, update_time, delete_flag <== Row: 1, 1, 0, special maintenance fund, 1, 1.00, , 0, , 1, , 1, , 2022-12-29 17:01:09, , 2022-12-29 17:01: 09,0 <== Row: 2, 1, 0, parking space vacancy fee, 2, 2.00, , 0, , 1, , 1, , 2022-12-29 17:01:09, , 2022-12-29 17:01: 09,0 <== Row: 3, 1, 0, parking fee, CWF01, 3.00, , 0, , 1, , 1, , 2023-10-10 09:57:07, , 2023-10-10 09:57:07 , 0 <== Row: 6, 1, 0, Service fee, 6, 6.00, , 0, , 1, , 1, , 2022-12-29 17:01:09, , 2022-12-29 17:01:09 , 0 <== Row: 7, 1, 0, sales matching fee, s, 7.00, , 0, , 1, , 1, , 2022-12-29 17:01:09, , 2022-12-29 17:01: 09,0 <== Row: 13, 1, 0, Odd water charges, 1, 13.00, , 0, , 1, , 1, , 2023-03-02 16:49:18, , 2023-03-02 16:49: 18,0 <== Row: 17, 1, 0, Consulting fee, 17, 17.00, , 0, , 1, , 1, , 2022-12-29 17:01:09, , 2022-12-29 17:01:09 , 0 <== Row: 18, 1, 0, Catering waste fee, 18, 18.00, , 0, , 1, , 1, , 2022-12-29 17:01:09, , 2022-12-29 17:01: 09,0 <== Row: 19, 1, 0, rental advertising fee, 1000123, 19.00, [], 0, , 1, , 1, , 2023-06-08 10:51:16, , 2023-06-08 10: 51:16, 0 <== Row: 20, 1, 0, Rent, ZJ002, 20.00, [], 0, , 1, , 1, , 2023-03-15 17:40:12, , 2023-03-15 17:40: 12,0 <== Total: 10 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4235ea19]
Or you can request like this
@GetMapping("/product") public void product() throws Exception { List<Future> list = new ArrayList<>(); for (long i = 1; i <= 20; i + + ) { Future<Product> productFuture = batchService.getById(i); list.add(productFuture); } for (Future future : list) { Object o = future.get(); System.out.println(o); } }
If the scope is REQUEST mode, you need to use HystrixRequestContext
@GetMapping("/product") public void product() throws Exception { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { List<Future> list = new ArrayList<>(); for (long i = 1; i <= 20; i + + ) { Future<Product> productFuture = batchService.getById(i); list.add(productFuture); } for (Future future : list) { Object o = future.get(); System.out.println(o); } } finally { context.shutdown(); } }
5.@HystrixCollapser parameter introduction
batchMethod, request merge method
scope, request merging method, REQUEST: request scope, GLOBAL: global scope, default com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL
collapserProperties
HystrixPropertiesManager.MAX_REQUESTS_IN_BATCH Maximum number of merge requests, default Integer.MAX_VALUE
HystrixPropertiesManager.TIMER_DELAY_IN_MILLISECONDS request merge interval millisecond value, default 10ms
Note that when the request merging interval millisecond value is reached and the maximum number of request merging is not reached, request merging will also be performed.
@HystrixCollapser is implemented based on Spring’s AOP
com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect