v5tech / notes

notes
https://ameizi.gitee.io/notes
MIT License
1.52k stars 378 forks source link

Spring MVC异步请求Controller #185

Open v5tech opened 5 years ago

v5tech commented 5 years ago

AsyncTaskService 模拟后台耗时请求业务

import org.joda.time.LocalDateTime;
import org.springframework.stereotype.Service;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class AsyncTaskService {

    public String uuid() {
        try {
            TimeUnit.MILLISECONDS.sleep(5000);
            System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->工作线程(" + Thread.currentThread().getName() + ")");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return UUID.randomUUID().toString();
    }

}

AsyncController 异步请求几种写法

import org.joda.time.LocalDateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureTask;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.WebAsyncTask;

import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 异步Controller
 */
@RestController
@EnableAsync
public class AsyncController {

    @Autowired
    private AsyncTaskService asyncTaskService;

    /**
     * 阻塞请求
     * @return
     */
    @GetMapping("blockingRequest")
    public String blockingRequest() {

        System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->主线程开始");

        String uuid = asyncTaskService.uuid();

        System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->主线程结束");

        return uuid;

    }

    /**
     * 使用Callable
     * @return
     */
    @GetMapping("callable")
    public Callable<String> callable() {

        System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->主线程开始");

        Callable<String> callable = () -> {
            String uuid = asyncTaskService.uuid();
            System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->子任务线程("+Thread.currentThread().getName()+")");
            return uuid;
        };

        System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->主线程结束");

        return callable;

    }

    /**
     * 使用CompletableFuture
     *
     * 使用该方式时,切记自己创建执行器,不要使用内置的 ForkJoinPool线程池,会有性能问题
     *
     * @return
     */
    @GetMapping("completableFuture")
    public CompletableFuture<String> completableFuture() {

        ExecutorService executor = Executors.newCachedThreadPool();

        System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->主线程开始");

        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(asyncTaskService::uuid,executor);

        System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->主线程结束");

        return completableFuture;

    }

    /**
     * 使用ListenableFuture
     *
     * 使用该方式时,切记自己创建执行器,不要使用内置的 ForkJoinPool线程池,会有性能问题
     *
     * @return
     */
    @GetMapping("listenableFuture")
    public ListenableFuture<String> listenableFuture() {

        System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->主线程开始");

        ListenableFutureTask<String> listenableFuture = new ListenableFutureTask<>(()-> asyncTaskService.uuid());

        Executors.newCachedThreadPool().execute(listenableFuture);

        System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->主线程结束");

        return listenableFuture;

    }

    /**
     * 使用WebAsyncTask
     * @return
     */
    @GetMapping("asynctask")
    public WebAsyncTask asyncTask() {

        SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();

        System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->主线程开始");

        WebAsyncTask<String> task = new WebAsyncTask(10000L, executor, ()-> asyncTaskService.uuid());

        task.onCompletion(()->{
            System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->调用完成");
        });

        task.onTimeout(()->{
            System.out.println("onTimeout");
            return "onTimeout";
        });

        System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->主线程结束");

        return task;
    }

    /**
     * 使用DeferredResult
     * @return
     */
    @GetMapping("deferredResult")
    public DeferredResult<String> deferredResult() {

        System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->主线程("+Thread.currentThread().getName()+")开始");

        DeferredResult<String> deferredResult = new DeferredResult<>();

        CompletableFuture.supplyAsync(()->{

            String uuid = asyncTaskService.uuid();

            // int abc = 2/0;

            return uuid;

        }, Executors.newFixedThreadPool(5)).whenCompleteAsync((result,throwable)->{
            if (throwable!=null) {
                deferredResult.setErrorResult(throwable.getMessage());
            }else {
                deferredResult.setResult(result);
            }
        });

        // 异步请求超时时调用
        deferredResult.onTimeout(()->{
            System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->onTimeout");
        });

        // 异步请求完成后调用
        deferredResult.onCompletion(()->{
            System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->onCompletion");
        });

        System.out.println(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss:S") + "--->主线程("+Thread.currentThread().getName()+")结束");

        return deferredResult;
    }

}

参考文档

https://www.javacodegeeks.com/2015/07/understanding-callable-and-spring-deferredresult.html

https://nickebbitt.github.io/blog/2017/03/22/async-web-service-using-completable-future