background

In the high concurrency scenario of the Internet, there will be a lot of requests, but the database connection pool is smaller, or you need to reduce the CPU stress, reduce the processing logic, you need to change the single query, in some way, to batch query multiple and return. For example: In Alipay, to query “personal information”, users will only trigger a request to query their own information, but multiple people doing so at the same time will generate multiple database connections. In order to reduce the connection, it is necessary to merge the request on the JAVA server side, merge multiple “personal information” query interfaces into multiple “personal information” query interfaces in batches, and then return the id of personal information in the database as the Key to the upstream system or page URL and other callers.

purpose

  1. Reduce the number of database accesses
  2. Multiple requests per unit of time are combined into one request. Let the business logic layer change the SQL of a single query to the SQL of a batch query. Or logic inside need to call REDis, that batch logic inside can use Redis pipeline to achieve.

The author

Hid in Kelvin

Public account: Dizang Thinking

Main solution

  1. Hystrix custom HystrixCollapse and HystrixCommand for SpringCloud
  2. Hystrix annotations for SpringCloud.
  3. When there is no service governance framework, use JDK queues and scheduled task thread pools for processing.

Since most spring Clouds are available now, the second annotation method will be used first, and the third annotation method will be used later. The first annotation method is not used because it is more convenient.

The interaction process

  1. The main idea is to wait a total of 200ms from the last count after receiving the request
  2. Process interface input parameters within 200ms at a time
  3. Then, query the results of multiple ids in batches using ids as the key
  4. After a batch query is complete, the upstream system returns a single query using the ID as the key

The test method

  1. Postman
  2. Create unit tests on the local system to invoke the service you started
  3. Set up upstream systems engineering to call
  4. Manually request multiple times on the page
  5. Jmeter generates multithreaded requests

Choose one or the other. Suggestions 1, 4 and 5

The development of

This article mainly uses Hystrix annotations to implement, there is another way to implement is to code custom HystrixCollapser, that method is to create two classes, one inheriting HystrixCollapser, the other inheriting HystrixCommand, This approach is easier to understand than explicit encoding declarations, but not as convenient as the Hystrix approach.

The pros and cons of customizing HystrixCollapser and Hystrix annotations for request consolidation

  1. Hystrix annotations are faster, but you can’t change the unit time you wait in real time. That timeout is in the annotations, and to change the unit time you actually need to restart the service or recompile the package.

  2. The nice thing about custom HystrixCollapser is that you can read the dictionary table to change the unit time while running, so you don’t have to reboot if something goes wrong on the line.

  3. However, the custom HystrixCollapser approach does have some drawbacks, because binding to a batch method creates a HystrixCommand class. If multiple requests are merged, you can only create multiple HystrixCommand classes.

1. Add the POM

Declare springBoot and SpringCloud versions

My previous projects used 1.4.7.release, Camden.sr2.

In fact, you can use the new version, but the new version of Eureka, Feign rely on artifactId changed, but the way to use the same.

    <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.4.7.RELEASE</version>
	</parent>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Camden.SR2</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
Copy the code

Adding key dependencies

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> < artifactId > springfox - swagger2 < / artifactId > < version > 2.2.2 < / version > < / dependency > < the dependency > < the groupId > IO. Springfox < / groupId > < artifactId > springfox swagger - UI < / artifactId > < version > 2.2.2 < / version > < / dependency >Copy the code

2. Enable annotations

In addition to the basic @SpringBootApplication @Enableeurekaclient required by the SpringCloud client, the main @Enablecircuitbreaker is added. Since all users of Hystrix must declare this note in order to activate the meaning of the circuit breaker, it is also used when fusing, which is also done by Hystrix. This is more critical, if you do not start, how do the subsequent code will not take effect

@SpringBootApplication
@EnableDiscoveryClient
// Use hystrix must be added
@EnableCircuitBreaker
@EnableEurekaClient
@EnableSwagger2
public class ProviderRequestMergeApplication {

  public static void main(String[] args) { SpringApplication.run(ProviderRequestMergeApplication.class, args); }}Copy the code

3. Request interface Controller

Write two interfaces. The user method is an example without a merge request. It has no actual effect in this case, but is just used to verify the merge and unmerge effect. The userbyMerge method is used to merge requests. It acts as an interface entry for requests. The logic of merging requests does not need to be implemented in Controller, so that Controller is only used as the request layer, and other functions are not coupled.

/ * * * *@author kelvin.cai
 *
 */
@RestController
public class UserController {

    @Autowired
    private UserBatchServiceImpl userBatchServiceImpl;

    @RequestMapping(method = RequestMethod.POST,value = "/user/{id}")
    public User user(@PathVariable Long id) {
        User book = new User( 55L."Yao Xueyin 2");
        return book;
    }
    
    @RequestMapping(method = RequestMethod.GET,value = "/userbyMerge/{id}")
    public User userbyMerge(@PathVariable Long id) throws InterruptedException, ExecutionException {
        Future<User> userFu = this.userBatchServiceImpl.getUserById(id);
        User user = userFu.get();
        returnuser; }}Copy the code

4. Write request merge logic

/ * * * *@author kelvin.cai
 *
 */
@Component
public class UserBatchServiceImpl {

    @HystrixCollapser(batchMethod = "getUserBatchById",scope=Scope.GLOBAL,
            collapserProperties = {@HystrixProperty(name ="timerDelayInMilliseconds",value = "2000")})
    public Future<User> getUserById(Long id) {
        throw new RuntimeException("This method body should not be executed");
    }

    @HystrixCommand
    public List<User> getUserBatchById(List<Long> ids) {
        System.out.println("Enter batch processing method"+ids);
        List<User> ps = new ArrayList<User>();
        for (Long id : ids) {
            User p = new User();
            p.setId(id);
            p.setUsername("dizang"+ids);
            ps.add(p);
        }
        returnps; }}Copy the code

Here are a few key points (see if it doesn’t work)

  1. HystrixCollapser the batchMethod parameter has the name of the batch processing method, which must be in the same class.
  2. The single handler and the batch handler must be of the same basic type, except that the batch handler needs to be wrapped with a List
  3. A single handler, Future, is recommended. This is the class that the JDK thread asynchronously retrieves for results asynchronously. There is an alternative return type that lets getUserById use synchronous blocking, but it is not recommended.
  4. Is a scope. The scope, there are two value REQUEST, which means that when the time REQUEST within the interface call UserBatchServiceImpl. GetUserById will merge many times. Think about it, if I call a single flashback multiple times within an interface, why not just use a batch query? I didn’t think of any scenario that would require this value. Scope has another value scope.global, which is shown in the sample, meaning that all requested interfaces are merged. We review the purpose of the demand, it is more in line with the requirements, such as multiple Alipay users to query their information is to merge the global request.
  5. @hystrixProperty Specifies the unit time of the merge request. It can be set to 5 seconds for debug, which is easier to test.

Here is a suggestion for the package path

The merge request class UserBatchServiceImpl is not recommended to be placed in the business logic layer. In order to keep the business logic in the service layer code clean, the UserBatchServiceImpl class is recommended to be placed in another package Collapser. Let the package path be used only to handle merging requests.

Because this class is implemented using the SpringCloud framework, what if instead of springCloud doing merge requests, we use the raw queue plus thread pool?

And some engineering design, is to establish the server project only request and service governance, another project to write domain domain things, do not include other framework, so for the third project called job timed task project can directly use domain project dependencies. This area drives design, see my previous article.

The test method

1. Trigger the test

Swagger – UI if you have added swagger, that you open http://localhost:7902/swagger-ui.html, fill in the docking port parameter request twice.

2. Result output

In the following figure, the console log has already printed the input parameters for two requests

3. Jmeter

Postman cannot test for concurrent requests. To test for concurrent requests, either use the above method or download Jmeter to test.

conclusion

By now, I believe that everyone has completed the merge request. In fact, the principle is still based on the original method, which uses the queue to store the parameter, and then uses the thread pool to obtain the input of the queue periodically, and then batch processing, using the thread’s Future, asynchronously return the result. I’m not going to describe it, but if I have time, I’m going to continue to merge requests from the original method. You can also look at other parameters of Hystrix merge requests and search for information to extend Hystrix’s capabilities.

In this paper, the Demo

It’s all in my SpringCloud demo. Look at provider-Hystrix-request-Merge project.

Gitee.com/kelvin-cai/…


Welcome to pay attention to the public number, the article faster step

My public account: Tibet thinking

Nuggets of gold: Hide Kelvin

Jane: Hide Kelvin

My Gitee: Underground Collection Kelvin gitee.com/kelvin-cai