精华内容
下载资源
问答
  • springboot异步调用
    千次阅读
    2019-04-23 18:28:29

    SpringBoot异步调用方法

     

    一、spring boot--使用异步请求,提高系统的吞吐量

    https://blog.csdn.net/liuchuanhong1/article/details/78744138

    1.使用Callable来实现

    2. 通过WebAsyncTask,也能实现异步调用

    这种方式和上面的callable方式最大的区别就是,WebAsyncTask支持超时,并且还提供了两个回调函数,分别是onCompletion和onTimeout,顾名思义,这两个回调函数分别在执行完成和超时的时候回调。

    3、Deferred方式实现异步调用

    在我们是生产中,往往会遇到这样的情景,controller中调用的方法很多都是和第三方有关的,例如JMS,定时任务,队列等,拿JMS来说,比如controller里面的服务需要从JMS中拿到返回值,才能给客户端返回,而从JMS拿值这个过程也是异步的,这个时候,我们就可以通过Deferred来实现整个的异步调用
    -- 异步调用可以使用AsyncHandlerInterceptor进行拦截

     

    二、Spring Boot异步调用(@Async)

    https://blog.csdn.net/wayne_ren/article/details/84859507

    异步处理
    [list]
    [*]Java的异步处理Thread/Runnable、Callable/Future
    [*]Servlet 2.5的异步处理 Tomcat的CometProcessor、Jetty的Continuations
    [*]Servlet 3.0的异步处理 asyncSupported、AsyncContext
    [*]Spring MVC的异步处理 @Async、AsyncTaskExecutor
    [*]Spring MVC的SSE ResponseBodyEmitter、SseEmitter、StreamingResponseBody
    [/list]

    Spring Boot本身对异步调用没有多大的变动,基本还是Spring MVC的@Async。

    [b](1)开启Spring的异步支持[/b]

    @Configuration
    @EnableAsync
    public class SpringAsyncConfig { 
    
    }

    [color=red]开启@EnableWebMvc的时候也自动开启了异步处理,但在Spring Boot项目中是不能使用@EnableWebMvc的。它会使Spring Boot的AutoConfigure一部分功能失效。[/color]
    官方文档里有说明:
    [url=http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration]http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration[/url]

     

    三、理解Callable 和 Spring DeferredResult(翻译)

    https://www.cnblogs.com/aheizi/p/5659030.html

    1-介绍

    Servlet 3中的异步支持为在另一个线程中处理HTTP请求提供了可能性。当有一个长时间运行的任务时,这是特别有趣的,因为当另一个线程处理这个请求时,容器线程被释放,并且可以继续为其他请求服务。
    这个主题已经解释了很多次,Spring框架提供的关于这个功能的类似乎有一点混乱——在一个Controller中返回Callable 和 DeferredResult。
    在这篇文章中,我将实施这两个例子,以显示其差异。
    这里所显示的所有示例都包括执行一个控制器,该控制器将执行一个长期运行的任务,然后将结果返回给客户机。长时间运行的任务由taskservice处理:

    @Service
    public class TaskServiceImpl implements TaskService {
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
        
        @Override
        public String execute() {
            try {
                Thread.sleep(5000);
                logger.info("Slow task executed");
                return "Task finished";
            } catch (InterruptedException e) {
                throw new RuntimeException();
            }
        }
    }

    这个web应用是用Spring Boot创建的,我们将执行下面的类来运行我们的例子:

    @SpringBootApplication
    public class MainApp {
        
        public static void main(String[] args) {
            SpringApplication.run(MainApp.class, args);
        }
    }

    2-阻塞的Controller

    在这个例子中,一个请求到达控制器。servlet线程不会被释放,直到长时间运行的方法被执行,我们退出@requestmapping注释的方法。

    @RestController
    public class BlockingController {
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
        private final TaskService taskService;
        
        @Autowired
        public BlockingController(TaskService taskService) {
            this.taskService = taskService;
        }
        
        @RequestMapping(value = "/block", method = RequestMethod.GET, produces = "text/html")
        public String executeSlowTask() {
            logger.info("Request received");
            String result = taskService.execute();
            logger.info("Servlet thread released");
            
            return result;
        }
    }

    如果我们运行这个例子http://localhost:8080/block,在日志里我们会发现servlet request不会被释放,直到长时间的任务执行完(5秒后)。

    2015-07-12 12:41:11.849  [nio-8080-exec-6] x.s.web.controller.BlockingController    : Request received
    2015-07-12 12:41:16.851  [nio-8080-exec-6] x.spring.web.service.TaskServiceImpl     : Slow task executed
    2015-07-12 12:41:16.851  [nio-8080-exec-6] x.s.web.controller.BlockingController    : Servlet thread released

    3-返回Callable

    在这个例子中,不是直接返回的结果,我们将返回一个Callable:

    @RestController
    public class AsyncCallableController {
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
        private final TaskService taskService;
        
        @Autowired
        public AsyncCallableController(TaskService taskService) {
            this.taskService = taskService;
        }
        
        @RequestMapping(value = "/callable", method = RequestMethod.GET, produces = "text/html")
        public Callable<String> executeSlowTask() {
            logger.info("Request received");
            Callable<String> callable = taskService::execute;
            logger.info("Servlet thread released");
            
            return callable;
        }
    }

    返回Callable意味着Spring MVC将调用在不同的线程中执行定义的任务。Spring将使用TaskExecutor来管理线程。在等待完成的长期任务之前,servlet线程将被释放。

    2015-07-12 13:07:07.012  [nio-8080-exec-5] x.s.w.c.AsyncCallableController          : Request received
    2015-07-12 13:07:07.013  [nio-8080-exec-5] x.s.w.c.AsyncCallableController          : Servlet thread released
    2015-07-12 13:07:12.014  [      MvcAsync2] x.spring.web.service.TaskServiceImpl     : Slow task executed

    你可以看到我们在长时间运行的任务执行完毕之前就已经从servlet返回了。这并不意味着客户端收到了一个响应。与客户端的通信仍然是开放的等待结果,但接收到的请求的线程已被释放,并可以服务于另一个客户的请求。

    4-返回DeferredResult

    首先,我们需要创建一个deferredresult对象。此对象将由控制器返回。我们将完成和Callable相同的事,当我们在另一个线程处理长时间运行的任务的时候释放servlet线程。

    @RestController
    public class AsyncDeferredController {
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
        private final TaskService taskService;
        
        @Autowired
        public AsyncDeferredController(TaskService taskService) {
            this.taskService = taskService;
        }
        
        @RequestMapping(value = "/deferred", method = RequestMethod.GET, produces = "text/html")
        public DeferredResult<String> executeSlowTask() {
            logger.info("Request received");
            DeferredResult<String> deferredResult = new DeferredResult<>();
            CompletableFuture.supplyAsync(taskService::execute)
                .whenCompleteAsync((result, throwable) -> deferredResult.setResult(result));
            logger.info("Servlet thread released");
            
            return deferredResult;
        }
    }

    所以,返回DeferredResult和返回Callable有什么区别?不同的是这一次线程是由我们管理。创建一个线程并将结果set到DeferredResult是由我们自己来做的。
    用completablefuture创建一个异步任务。这将创建一个新的线程,在那里我们的长时间运行的任务将被执行。也就是在这个线程中,我们将set结果到DeferredResult并返回。
    是在哪个线程池中我们取回这个新的线程?默认情况下,在completablefuture的supplyasync方法将在forkjoin池运行任务。如果你想使用一个不同的线程池,你可以通过传一个executor到supplyasync方法:

    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

    如果我们运行这个例子,我们将得到如下结果:

    2015-07-12 13:28:08.433  [io-8080-exec-10] x.s.w.c.AsyncDeferredController          : Request received
    2015-07-12 13:28:08.475  [io-8080-exec-10] x.s.w.c.AsyncDeferredController          : Servlet thread released
    2015-07-12 13:28:13.469  [onPool-worker-1] x.spring.web.service.TaskServiceImpl     : Slow task executed 

    5-结论

    站在一定高度来看这问题,Callable和Deferredresult做的是同样的事情——释放容器线程,在另一个线程上异步运行长时间的任务。不同的是谁管理执行任务的线程。

    文中涉及的代码spring-rest

    翻译自Xavier Padró's Blog

     

     

     

     

     

     

    更多相关内容
  • 主要为大家详细介绍了SpringBoot异步调用方法并接收返回值,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 主要介绍了springboot 异步调用的实现方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • SpringBoot异步调用

    2022-06-13 15:31:53
    2.1、无返回值的异步方法2.1、有返回值的异步方法3.1、方法级别重写Executor3.2、应用级别重写Executor3.3、自定义线程池配置“异步调用”对应的是“同步调用”,在实际开发中,有时候为了及时处理请求和进行响应,...
    • 2.1、无返回值的异步方法

    • 2.1、有返回值的异步方法

    • 3.1、方法级别重写Executor

    • 3.2、应用级别重写Executor

    • 3.3、自定义线程池配置

    “异步调用”对应的是“同步调用”,

    在实际开发中,有时候为了及时处理请求和进行响应,我们可能使用异步调用,同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行;异步调用指程序在顺序执行时,不等待异步调用的语句返回结果就执行后面的程序。异步调用的实现有很多,例如多线程、定时任务、消息队列等。

    这里学习使用@Async注解来实现异步调用。

    1、@EnableAsync

    首先,我们需要在启动类上添加  @EnableAsync 注解来声明开启异步方法。

    @SpringBootApplication
    @EnableAsync
    public class SpringbootAsyncApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootAsyncApplication.class, args);
        }
    
    }
    

    2、@Async

    需要注意的,@Async在使用上有一些限制:

    • 它只能应用于public修饰的方法

    • 自调用–从同一个类中调用async方法,将不起作用

    原因很简单:

    • 只有公共方法,才可以被代理。

    • 自调用不起作用,因为它越过了代理直接调用了方法。

    2.1、无返回值的异步方法

    这是一个异步运行的无返回值方法:

        @Async
        public void asyncMethodWithVoidReturnType() {
            System.out.println("异步无返回值方法 "
                    + Thread.currentThread().getName());
        }
    

    实例:

    • AsyncTask:异步式任务类,定义了三个异步式方法。

    /**
     * @Author 三分恶
     * @Date 2020/7/15
     * @Description 异步式任务
     */
    @Component
    public class AsyncTask {
       Logger log= LoggerFactory.getLogger(AsyncTask.class);
    
        private Random random = new Random();
    
        /**
         * 定义三个异步式方法
         * @throws InterruptedException
         */
        @Async
        public void taskOne() throws InterruptedException {
            long start = System.currentTimeMillis();
            //随机休眠若干毫秒
            Thread.sleep(random.nextInt(10000));
            long end = System.currentTimeMillis();
            log.info("任务一执行完成耗时{}秒", (end - start)/1000f);
        }
    
        @Async
        public void taskTwo() throws InterruptedException {
            long start = System.currentTimeMillis();
            Thread.sleep(random.nextInt(10000));
            long end = System.currentTimeMillis();
            log.info("任务二执行完成耗时{}秒", (end - start)/1000f);
        }
    
        @Async
        public void taskThree() throws InterruptedException {
            long start = System.currentTimeMillis();
            Thread.sleep(random.nextInt(10000));
            long end = System.currentTimeMillis();
            log.info("任务三执行完成耗时{}秒", (end - start)/1000f);
        }
    
    }
    
    • 在测试类中调用三个异步式方法:

    /**
     * @Author 三分恶
     * @Date 2020/7/15
     * @Description
     */
    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class AsyncTaskTest {
    
        @Autowired
        private AsyncTask asyncTask;
    
        Logger log= LoggerFactory.getLogger(AsyncTaskTest.class);
    
        @Test
        public void doAsyncTasks(){
            try {
                long start = System.currentTimeMillis();
                //调用三个异步式方法
                asyncTask.taskOne();
                asyncTask.taskTwo();
                asyncTask.taskThree();
                Thread.sleep(5000);
                long end = System.currentTimeMillis();
                log.info("主程序执行完成耗时{}秒", (end - start)/1000f);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }
    
    

    运行结果:可以看到三个方法没有顺序执行,这个复执行单元测试,您可能会遇到各种不同的结果,比如:

    • 没有任何任务相关的输出

      • 乱序的任务相关的输出

      • 有部分任务相关的输出

    在这里插入图片描述

    在这里插入图片描述

    原因是目前doTaskOne、doTaskTwo、doTaskThree三个函数的时候已经是异步执行了。主程序在异步调用之后,主程序并不会理会这三个函数是否执行完成了,由于没有其他需要执行的内容,所以程序就自动结束了,导致了不完整或是没有输出任务相关内容的情况。

    2.1、有返回值的异步方法

    @Async也可以应用有返回值的方法–通过在Future中包装实际的返回值:

       /**
         * 有返回值的异步方法
         * @return
         */
        @Async
        public Future<String> asyncMethodWithReturnType() {
            System.out.println("执行有返回值的异步方法 "
                    + Thread.currentThread().getName());
            try {
                Thread.sleep(5000);
                return new AsyncResult<String>("hello world !!!!");
            } catch (InterruptedException e) {
                //
            }
            return null;
        }
    

    Spring还提供了一个实现Future的AsyncResult类。这个类可用于跟踪异步方法执行的结果。

    实例:

    • 我们将2.1的实例改造成有返回值的异步方法:

        @Async
        public Future<String> taskOne() throws InterruptedException {
            long start = System.currentTimeMillis();
            //随机休眠若干毫秒
            Thread.sleep(random.nextInt(10000));
            long end = System.currentTimeMillis();
            log.info("任务一执行完成耗时{}秒", (end - start)/1000f);
            return new AsyncResult<>("任务一完事了");
        }
    

    taskTwo、taskThree方法做同样的改造。

    • 测试有返回值的异步方法:

       @Test
        public void doFutureTask(){
            try {
                long start=System.currentTimeMillis();
                Future<String> future1=asyncTask.taskOne();
                Future <String> future2 = asyncTask.taskTwo();
                Future <String> future3 = asyncTask.taskThree();
                //三个任务执行完再执行主程序
                do {
                    Thread.sleep(100);
                } while (future1.isDone() && future2.isDone() && future3.isDone());
                log.info("获取异步方法的返回值:{}", future1.get());
                Thread.sleep(5000);
                long end = System.currentTimeMillis();
                log.info("主程序执行完成耗时{}秒", (end - start)/1000f);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    

    运行结果:可以看到三个任务完成后才执行主程序,还输出了异步方法的返回值。

    在这里插入图片描述

    3、 Executor

    默认情况下,Spring使用SimpleAsyncTaskExecutor异步运行这些方法。

    可以在两个级别上重写默认线程池——应用程序级别或方法级别。

    3.1、方法级别重写Executor

    所需的执行程序需要在配置类中声明 Executor:

    /**
     * @Author 三分恶
     * @Date 2020/7/15
     * @Description 方法级别重写线程池
     */
    @Configuration
    @EnableAsync
    public class SpringAsyncConfig {
    
        @Bean(name = "threadPoolTaskExecutor")
        public Executor threadPoolTaskExecutor() {
            return new ThreadPoolTaskExecutor();
        }
    }
    

    然后,在@Async中的属性提供Executor名称:

        @Async("threadPoolTaskExecutor")
        public void asyncMethodWithConfiguredExecutor() {
            System.out.println("Execute method with configured executor - "
                    + Thread.currentThread().getName());
        }
    

    3.2、应用级别重写Executor

    配置类应实现AsyncConfigurer接口,重写getAsyncExecutor()方法。

    在这里,我们将返回整个应用程序的Executor,这样一来,它就成为运行以@Async注释的方法的默认Executor:

    /**
     * @Author 三分恶
     * @Date 2020/7/15
     * @Description 应用级别重写 Excutor
     */
    @Configuration
    @EnableAsync
    public class SpringApplicationAsyncConfig implements AsyncConfigurer {
        @Override
        public Executor getAsyncExecutor() {
            return new ThreadPoolTaskExecutor();
        }
    }
    

    3.3、自定义线程池配置

    在上面,自定义线程池只是简单地返回了一个线程池:

    return new ThreadPoolTaskExecutor();
    

    实际上,还可以对线程池做一些配置:

    /**
     * @Author 三分恶
     * @Date 2020/7/15
     * @Description
     */
    @Configuration
    @EnableAsync
    public class SpringPropertiesAsyncConfig implements AsyncConfigurer {
    
        /**
         * 对线程池进行配置
         * @return
         */
        @Override
        public Executor getAsyncExecutor() {
            ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
            taskExecutor.setCorePoolSize(20);
            taskExecutor.setMaxPoolSize(200);
            taskExecutor.setQueueCapacity(25);
            taskExecutor.setKeepAliveSeconds(200);
            taskExecutor.setThreadNamePrefix("oKong-");
            // 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者
            taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            taskExecutor.initialize();
            return taskExecutor;
        }
    }
    

    ThreadPoolTaskExecutor配置参数的简单说明:

    • corePoolSize:线程池维护线程的最少数量

    • keepAliveSeconds:允许的空闲时间,当超过了核心线程出之外的线程在空闲时间到达之后会被销毁

    • maxPoolSize:线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程

    • queueCapacity:缓存队列

    • rejectedExecutionHandler:线程池对拒绝任务(无线程可用)的处理策略。这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务。还有一个是AbortPolicy策略:处理程序遭到拒绝将抛出运行时RejectedExecutionException。

    4、异常处理

    当方法返回类型为Future时,异常处理很容易– Future.get()方法将抛出异常。

    但是如果是无返回值的异步方法,异常不会传播到调用线程。因此,我们需要添加额外的配置来处理异常。

    我们将通过实现AsyncUncaughtExceptionHandler接口来创建自定义异步异常处理程序。

    当存在任何未捕获的异步异常时,将调用handleUncaughtException()方法:

    /**
     * @Author 三分恶
     * @Date 2020/7/15
     * @Description
     */
    public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
        @Override
        public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
            System.out.println("Exception message - " + throwable.getMessage());
            System.out.println("Method name - " + method.getName());
            for (Object param : objects) {
                System.out.println("Parameter value - " + param);
            }
        }
    }
    

    上面,我们使用配置类实现了AsyncConfigurer接口。

    作为其中的一部分,我们还需要重写getAsyncUncaughtExceptionHandler()方法以返回我们的自定义异步异常处理:

        /**
         * 返回自定义异常处理
         * @return
         */
        @Override
        public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
             return new CustomAsyncExceptionHandler();
        }
    

    5、总结

    这里异步请求的使用及相关配置,如超时,异常等处理。在剥离一些和业务无关的操作时,就可以考虑使用异步调用进行其他无关业务操作,以此提供业务的处理效率。或者一些业务场景下可拆分出多个方法进行同步执行又互不影响时,也可以考虑使用异步调用方式提供执行效率。



     

    展开全文
  • 主要介绍了SpringBoot异步调用方法实现场景代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • SpringBoot异步调用1、异步调用简介2、@Async的使用1.使用@Async注解之前,需要在入口类添加注解@EnableAsync开启异步调用2.在需要使用异步调用的方法加上@Async3.测试 1、异步调用简介 异步调用是相对于同步调用而...

    1、异步调用简介

    异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步都得上一步执行完后才能执行,而一步调用无需等待上一步执行完成即可执行。在项目中想让程序并行执行,除了可以使用多线程来并行地处理任务,也可以使用Spring Boot提供的异步处理方式@Async来处理。在springboot框架中,只需要添加@Async注解就能将普通的同步任务改为异步调用任务。

    2、@Async的使用

    1.使用@Async注解之前,需要在入口类添加注解@EnableAsync开启异步调用

    代码:

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    import org.springframework.context.annotation.ImportResource;
    import org.springframework.retry.annotation.EnableRetry;
    import org.springframework.scheduling.annotation.EnableAsync;
    
    @SpringBootApplication
    @ServletComponentScan
    @ImportResource(locations = {"classpath:spring-mvc.xml"})
    @EnableRetry
    @EnableAsync   //开启异步调用
    public class DemojpaApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemojpaApplication.class, args);
        }
    
    }
    

    @EnableAsync 是要加的,其他除了@SpringBootApplication可以不用

    2.在需要使用异步调用的方法加上@Async

    具体代码:

        @Override
        @Async
        public Future<List<AyUser>> findAsynAll() {
            try{
                logger.info("开始做任务!");
                Long start = System.currentTimeMillis();
                List<AyUser> ayUserList = ayUserRepository.findAll();
                Long end = System.currentTimeMillis();
                logger.info("总共消耗:"+(end-start)+"毫秒");
                return new AsyncResult<List<AyUser>>(ayUserList);
            }catch (Exception e){
                logger.error("method [findAll] error",e);
                return new AsyncResult<List<AyUser>>(null);
            }
        }
    

    3.测试

    测试代码:

        @Test
        public void testAsync2() throws InterruptedException {
            Long startTime = System.currentTimeMillis();
            logger.info("第一次查询用户!");
            Future<List<AyUser>> ayUserList = ayUserService.findAsynAll();
            logger.info("第二次查询用户!");
            Future<List<AyUser>> ayUserList2 = ayUserService.findAsynAll();
            logger.info("第三次查询用户!");
            Future<List<AyUser>> ayUserList3 = ayUserService.findAsynAll();
            while(true){
                if (ayUserList.isDone()&&ayUserList2.isDone()&&ayUserList3.isDone()){
                    break;
                }else {
                    Thread.sleep(10);
                }
            }
            Long endTime = System.currentTimeMillis();
            logger.info("总共消耗:"+(endTime-startTime)+"毫秒");
        }
    

    运行截图:
    在这里插入图片描述
    可以看出,三个方法是并行执行的。

    展开全文
  • } /** * 异步调用 * pdfConvertImgUtils.asyncPdfConvertImgUploadObs(url,"IBSPrint" + id,redisTemplate,1L); * * @param pdfUrl PDF地址 * @param id redis key * @param timeout redis有效期 * @return */ ...

    测试类

    package com.controller.yibu;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.HashMap;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    
    @RequestMapping("/test")
    @Slf4j
    @RestController
    public class TestYiBu {
    
        @Resource
        private PdfConvertImgUtils pdfConvertImgUtils;
    
    
    
        /**
         * 导出pdf方法
         * http://10.79.17.4:8090/test/upload
         */
        @GetMapping("/upload")
        public Object exportPDF(HttpServletRequest req, HttpServletResponse resp) throws Exception {
            Map<String, String> xdMap = new HashMap<>(16);
            xdMap.put("printUrl", "printUrl");
            List<String> resultList = new LinkedList<>();
            String printUrl = xdMap.get("printUrl");
            String orderId = "123456";
            if (StringUtils.isNotBlank(printUrl)) {
                resultList.add(printUrl);
                // 异步上传
                pdfConvertImgUtils.asyncPdfConvertImgUploadObs(printUrl, orderId, 8L);
            }
    
    
            System.out.println("orderId = " + orderId);
            System.out.println("orderId = " + orderId);
            System.out.println("orderId = " + orderId);
            System.out.println("orderId = " + orderId);
    
    
            return "upload##";
        }
    
    }
    
    

    异步工具类

    package com.controller.yibu;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Component;
    
    import java.awt.image.BufferedImage;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    
    
    @Slf4j
    @Component
    public class PdfConvertImgUtils {
    
    
        /**
         * pdf转图片并上传OBS
         *
         * @param pdfUrl
         * @return
         */
        public List<String> downloadPdfConvertImgUtils(String pdfUrl) {
            List<String> list = new ArrayList<>();
    
            try {
                Thread.sleep(2000);
                System.out.println("线程睡眠2秒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            list.add("a");
            list.add("c");
            list.add("d");
            list.add(pdfUrl);
            return list;
        }
    
    
        /**
         * 生成临时文件并上传OBS后删除
         *
         * @param image 图像缓冲区
         * @param i     图像页数
         * @return
         */
        private String upload(BufferedImage image, int i) {
            // 返回结果
            String result = "upload";
            System.out.println("执行上传图片的业务逻辑 = " + result);
    
            return result;
        }
    
        /**
         * 异步调用
         * pdfConvertImgUtils.asyncPdfConvertImgUploadObs(url,"IBSPrint" + id,redisTemplate,1L);
         *
         * @param pdfUrl  PDF地址
         * @param id      redis key
         * @param timeout redis有效期
         * @return
         */
        public void asyncPdfConvertImgUploadObs(String pdfUrl, String id, Long timeout) {
            log.info("提交订单异步PDF转图片上传ID: " + id);
            // 开始异步标识,表示异步已开始
            String ibsPrintAsync = "IBSPrintAsync" + id;
            List<String> list = new ArrayList<>();
            list.add(id);
    //        redisTemplate.opsForValue().set(ibsPrintAsync, list, timeout, TimeUnit.MINUTES);
    
            CompletableFuture<List<String>> future = CompletableFuture.supplyAsync(() -> this.downloadPdfConvertImgUtils(pdfUrl), CheckFileUtils.THREAD_POOL);
    
            future.whenComplete((result, exception) -> {
                if (result != null && result.size() > 0 && exception == null) {
    //                redisTemplate.opsForValue().set("IBSPrint" + id, result, timeout, TimeUnit.HOURS);
                    // 删除异步标识,表示异步已结束
    //                if (Boolean.TRUE.equals(redisTemplate.hasKey(ibsPrintAsync))) {
    //                    redisTemplate.delete(ibsPrintAsync);
    //                }
                    List<String> result2 = result;
                    System.out.println("##result2 = " + result2);//[a, c, d, printUrl]
                    try {
                        List<String> list1 = future.get();
                        System.out.println("###list1= " + list1);//[a, c, d, printUrl]
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    }
    
    
                } else {
    //                redisTemplate.delete(ibsPrintAsync);
                    log.warn("提交订单异步PDF转图片上传OBS异常: " + exception.getMessage());
                }
            });
    
            //TODO 如果是在次调用会变成非异步
    //        try {
    //            List<String> list1 = future.get();
    //            System.out.println("#########list1 = " + list1);
    //        } catch (InterruptedException e) {
    //            e.printStackTrace();
    //        } catch (ExecutionException e) {
    //            e.printStackTrace();
    //        }
    
        }
    
        /**
         * 判断异步上传是否完成
         * @param id 订单id
         * @param redisTemplate     redis
         * @param i 次数(防止死循环)
         */
    //    public void sleep(String id, RedisTemplate<String, List<String>> redisTemplate, int i) {
    //        try {
    //            i++;
    //            // 提交订单异步上传标识
    //            String ibsPrintAsync = "IBSPrintAsync" + id;
    //            if (Boolean.TRUE.equals(redisTemplate.hasKey(ibsPrintAsync))) {
    //                Thread.sleep(2000);
    //                log.info("异步上传中,打印睡眠..."+i);
    //                sleep(id,redisTemplate,i);
    //            }
    //        } catch (Exception e) {
    //            e.printStackTrace();
    //            // 异常超过3次return
    //            if (i > OrderConstant.INT_NUM_3) {
    //                return;
    //            }
    //            sleep(id,redisTemplate,i);
    //        }
    //    }
    
    }
    
    

    线程池

    package com.controller.yibu;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    
    @Slf4j
    public class CheckFileUtils {
    
        /**
         * IO阻塞型,线程数尽量大一点
         * 线程数 = CPU核数 * 1 /(1 - 阻塞系数)
         * 当CPU密集型时,阻塞系数为0,线程数=CPU核数
         * 当IO密集型时,阻塞系统趋向于1,如果为0.9,则线程数=CPU核数*10;如果为0.99,则线程数=CPU核数*100
         * <p>
         * 针对BSP这种场景,大部时间是在网络IO上,极少一部分时间是在组装对象(CPU)上,另外,我们的订单量最大可能达到2000~3000,
         * 所以,这里设置核心线程数量为1000,队列长度为1000,最大线程数为2000,应该比较合理。
         * <p>
         * 如果后续并发量继续增大,可以考虑增大最大线程数,来压测对比。
         */
        public static final ThreadPoolExecutor THREAD_POOL = new ThreadPoolExecutor(500, 1000,
                5, TimeUnit.SECONDS,
                new LinkedBlockingQueue(500),
                new ThreadPoolExecutor.CallerRunsPolicy());
    
    
    }
    
    
    展开全文
  • } } 2:当前需要执行异步调用的类上方 3:配置类上方使用 在SpringBoot中 该配置类不仅可以开启异步调用开启异步调用,我们还可以使用线程池对开启的异步线程进行统一管理。 package com.inspur.tax.config; import...
  • Springboot异步调用 全解析

    千次阅读 2018-10-23 17:34:03
    本文主要介绍Springboot异步调用,包括: 1、Springboot同步调用演示; 2、Springboot两种异步调用方式; 3、Springboot使用注解的异步调用方式详解; 4、Springboot使用注解异步调用时的异常处理; 5、利用Future...
  • 1、spring管理的异步线程数量有限,如果是web项目的话,线程数量由... * springboot里面创建异步线程配置类 * @author kouyy */ @Configuration @EnableAsync public class ThreadAsyncConfigurer implements Asyn...
  • Springboot 异步调用

    2019-10-29 18:40:11
    当我们业务中需要执行一个比较耗时的任务并且不影响主题程序的运行,那就需要异步了 首先,配置springboot框架整体的线程池: package pers.zc.activiti.config; import org.springframework.context.annotation....
  • springboot异步调用@Async

    2019-03-01 03:39:50
    文章目录一、异步调用和同步调用的比较二、注解三、实例1、在启动类中开启异步—@EnabledAsync2、注册异步组件,在组件中标注异步方法—@Component、@Async3、测试用例:注入异步组件——@Autowired4、运行测试用例 ...
  • SpringBoot 异步调用

    2020-11-24 17:52:21
    SpringBoot 异步调用1 开发场景说明2 使用 1 开发场景说明 项目有个功能是在处理请求后推送公众号消息给用户。 请求的响应无需等到推送公众号消息之后,此时公众号消息推送应选择异步调用。 2 使用 1)启动类添加...
  • 主要为大家详细介绍了SpringBoot开启异步调用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • Spring为任务调度与异步方法执行提供了注解支持。通过在方法上设置@Async注解,可使得方法被异步调用。下面这篇文章主要给大家介绍了关于spring boot异步调用方式@Async的相关资料,需要的朋友可以参考下。
  • 线程池配置 @Configuration @Slf4j @EnableAsync public class AsyncThreadPoolConfig implements AsyncConfigurer { @Bean(name = "asyncExecutor") @Override public Executor getAsyncExecutor() { ...
  • SpringBoot异步调用Async

    2021-04-25 11:30:31
    这个和定时器差不多,启动加上@EnableAsync ,只需要在我们需要异步的方法上面加上@Async注解 设置异步方法: @Component public class TestAsync { @Async//异步方法,另外开子线程执行的方法 public void Test1...
  • 主要介绍了SpringBoot中异步请求和异步调用问题,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
  • 本篇文章给大家带来的内容是关于springboot异步调用的介绍(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。同步程序按照定义顺序依次执行,每一行程序都必须等待上一行程序执行完成之后...
  • (六)SpringBoot 异步调用

    2018-11-28 19:09:04
    异步调用 开启异步调用支持   @EnableAsync 启动类使用注解开启异步调用  2. 添加异步方法 @Async  public void sendSms(){  System.out.println("@Async");    } 3.配置线程池 @...
  • springboot异步调用demo

    2021-03-12 21:21:39
    异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到上一步执行完成之后才能执行,而异步调用则无需等待上一步程序执行完成即可执行。最好往数据库里多添加数据,效果明显。 1、...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 30,546
精华内容 12,218
关键字:

springboot异步调用

spring 订阅