Skip to content

Spring MVC异步请求Controller #185

Description

@v5tech

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions