Spring boot 使用線程池實現無返回的異步處理和有返回的異步處理

1、場景

http請求,業務異步處理;

1)無需返回的,比如發送短信,push消息,郵件發送等附屬業務,異步處理,減少http請求總耗時,提升客戶體驗。

2)需要返回的,前端等待業務處理結果數據,異步處理返交給http請求的主線程,返回前端;如果是單個業務此處可以優化,請查看此博文前一篇和後一篇;如果是多個業務處理,可以使用多個有返回的異步處理,總重彙總結果並返回。

2、知識點

1)線程池ThreadPoolTaskExecutor

2)註解:@EnableAsync,@Async("asyncServiceExecutor")

3)返回值封裝Future<T>,new AsyncResult<>(T)

3、代碼示例

1)controller

package com.liuxd.controller;

import com.liuxd.entity.Responses;
import com.liuxd.service.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.Future;

@Slf4j
@RestController
public class AsyncCtr {

    @Autowired
    private TaskService taskService;

    @RequestMapping(value = "/get", method = RequestMethod.GET)
    public Responses<String> getResult() throws Exception {

        log.info("收到HTTP請求...");
        long startTime = System.currentTimeMillis();

        //1.異步執行_處理數據_無需等待
        taskService.handleData();

        //2.異步執行_處理數據_等待處理結果
        Future<String> future = taskService.getData();
        String result = future.get();

        log.info("接收HTTP請求線程任務已完成,退出!");
        long endTime = System.currentTimeMillis();
        log.info("http請求總耗時: " + (endTime - startTime) + "ms");

        return new Responses<>(0, result, "SUCCESS");

    }

}

2)service

package com.liuxd.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import java.util.concurrent.Future;

@Slf4j
@Service
public class TaskService {

    @Async("asyncServiceExecutor")
    public void handleData() {

        log.info("調用service無返回異步方法,開始執行...");
        long startTime = System.currentTimeMillis();
        try {
            Thread.sleep(2500L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        log.info("調用service無返回異步方法,執行結束!!");
        long endTime = System.currentTimeMillis();
        log.info("調用service無返回異步方法總耗時: " + (endTime - startTime) + "ms");

    }

    @Async("asyncServiceExecutor")
    public Future<String> getData(){

        log.info("調用service有返回異步方法,開始執行...");
        long startTime = System.currentTimeMillis();

        try {
            Thread.sleep(2500L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        log.info("調用service有返回異步方法,執行結束!!");

        long endTime = System.currentTimeMillis();
        log.info("調用service有返回異步方法總耗時: " + (endTime - startTime) + "ms");

        return new AsyncResult<>("異步處理完成!");

    }


}

3)線程池

package com.liuxd.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @program: unif-insure-service
 * @description: 線程池
 **/
@Configuration
@EnableAsync
public class ExecutorConfig {
    @Bean
    public Executor asyncServiceExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心線程數
        executor.setCorePoolSize(5);
        //配置最大線程數
        executor.setMaxPoolSize(5);
        //配置隊列大小
        executor.setQueueCapacity(1000);
        //配置線程池中的線程的名稱前綴
        executor.setThreadNamePrefix("async-system-");

        // rejection-policy:當pool已經達到max size的時候,如何處理新任務
        // CALLER_RUNS:不在新線程中執行任務,而是有調用者所在的線程來執行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //執行初始化
        executor.initialize();
        return executor;
    }
}

4)Responses

package com.liuxd.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Responses<T> {

    private Integer code;

    private String msg;

    private T data;

}

5)打印結果

6)結果分析

1) 共三個線程,http請求主線程,異步無返回線程,異步有返回線程

2)主線程不需要等待無返回線程,會等待有返回線程結果,並最終返回

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章