溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring異步接口返回結(jié)果的方式有哪些

發(fā)布時間:2022-08-29 11:24:47 來源:億速云 閱讀:182 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹了Spring異步接口返回結(jié)果的方式有哪些的相關(guān)知識,內(nèi)容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Spring異步接口返回結(jié)果的方式有哪些文章都會有所收獲,下面我們一起來看看吧。

    1. 需求

    開發(fā)中我們經(jīng)常遇到異步接口需要執(zhí)行一些耗時的操作,并且接口要有返回結(jié)果。

    使用場景:用戶綁定郵箱、手機號,將郵箱、手機號保存入庫后發(fā)送郵件或短信通知
    接口要求:數(shù)據(jù)入庫后給前臺返回成功通知,后臺異步執(zhí)行發(fā)郵件、短信通知操作

    一般的話在企業(yè)中會借用消息隊列來實現(xiàn)發(fā)送,業(yè)務(wù)量大的話有一個統(tǒng)一消費、管理的地方。但有時項目中沒有引用mq相關(guān)組件,這時為了實現(xiàn)一個功能去引用、維護一個消息組件有點大材小用,下面介紹幾種不引用消息隊列情況下的解決方式

    定義線程池:

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.task.TaskExecutor;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    import java.util.concurrent.ThreadPoolExecutor;
    /**
     * @description: 公共配置
     * @author: yh
     * @date: 2022/8/26
     */
    @EnableAsync
    @Configuration
    public class CommonConfig {
        @Bean
        public TaskExecutor taskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    
            // 設(shè)置核心線程數(shù)
            executor.setCorePoolSize(50);
            // 設(shè)置最大線程數(shù)
            executor.setMaxPoolSize(200);
            // 設(shè)置隊列容量
            executor.setQueueCapacity(200);
            // 設(shè)置線程活躍時間(秒)
            executor.setKeepAliveSeconds(800);
            // 設(shè)置默認線程名稱
            executor.setThreadNamePrefix("task-");
            // 設(shè)置拒絕策略
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            // 等待所有任務(wù)結(jié)束后再關(guān)閉線程池
            executor.setWaitForTasksToCompleteOnShutdown(true);
            return executor;
        }
    }

    2. 解決方案

    2.1 @Async

    定義異步任務(wù),如發(fā)送郵件、短信等

    @Service
    public class ExampleServiceImpl implements ExampleService {
        @Async("taskExecutor")
        @Override
        public void sendMail(String email) {
            try {
                Thread.sleep(3000);
                System.out.println("異步任務(wù)執(zhí)行完成, " + email + " 當(dāng)前線程:" + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    Controller

    @RequestMapping(value = "/api")
    @RestController
    public class ExampleController {
        @Resource
        private ExampleService exampleService;
    
        @RequestMapping(value = "/bind",method = RequestMethod.GET)
        public String bind(@RequestParam("email") String email) {
            long startTime = System.currentTimeMillis();
            try {
                // 綁定郵箱....業(yè)務(wù)
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //模擬異步任務(wù)(發(fā)郵件通知、短信等)
            exampleService.sendMail(email);
    
            long endTime = System.currentTimeMillis();
            System.out.println("方法執(zhí)行完成返回,耗時:" + (endTime - startTime));
            return "ok";
        }
    }

    運行結(jié)果:

    Spring異步接口返回結(jié)果的方式有哪些

    2.2 TaskExecutor

    @RequestMapping(value = "/api")
    @RestController
    public class ExampleController {
        @Resource
        private ExampleService exampleService;
        @Resource
        private TaskExecutor taskExecutor;
    
        @RequestMapping(value = "/bind", method = RequestMethod.GET)
        public String bind(@RequestParam("email") String email) {
            long startTime = System.currentTimeMillis();
            try {
                // 綁定郵箱....業(yè)務(wù)
                Thread.sleep(2000);
    
                // 將發(fā)送郵件交給線程池去執(zhí)行
                taskExecutor.execute(() -> {
                    exampleService.sendMail(email);
                });
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
    
            long endTime = System.currentTimeMillis();
            System.out.println("方法執(zhí)行完成返回,耗時:" + (endTime - startTime));
            return "ok";
        }
    }

    運行結(jié)果:

    Spring異步接口返回結(jié)果的方式有哪些

    2.3 Future

    首先去掉Service方法中的@Async("taskExecutor"),此時執(zhí)行就會變成同步,總計需要5s才能完成接口返回。這次我們使用jdk1.8中的CompletableFuture來實現(xiàn)異步任務(wù)

    @RequestMapping(value = "/api")
    @RestController
    public class ExampleController {
        @Resource
        private ExampleService exampleService;
        @Resource
        private TaskExecutor taskExecutor;
    
        @RequestMapping(value = "/bind", method = RequestMethod.GET)
        public String bind(@RequestParam("email") String email) {
            long startTime = System.currentTimeMillis();
            try {
                // 綁定郵箱....業(yè)務(wù)
                Thread.sleep(2000);
    
                // 將發(fā)送郵件交給異步任務(wù)Future,需要記錄返回值用supplyAsync
                CompletableFuture.runAsync(() -> {
                    exampleService.sendMail(email);
                }, taskExecutor);
    
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
    
            long endTime = System.currentTimeMillis();
            System.out.println("方法執(zhí)行完成返回,耗時:" + (endTime - startTime));
            return "ok";
        }
    }

    運行結(jié)果:

    Spring異步接口返回結(jié)果的方式有哪些

    2.4 @EventListener

    Spring為我們提供的一個事件監(jiān)聽、訂閱的實現(xiàn),內(nèi)部實現(xiàn)原理是觀察者設(shè)計模式;為的就是業(yè)務(wù)系統(tǒng)邏輯的解耦,提高可擴展性以及可維護性。事件發(fā)布者并不需要考慮誰去監(jiān)聽,監(jiān)聽具體的實現(xiàn)內(nèi)容是什么,發(fā)布者的工作只是為了發(fā)布事件而已。

    2.4.1 定義event事件模型

    public class NoticeEvent extends ApplicationEvent {
        private String email;
        private String phone;
        public NoticeEvent(Object source, String email, String phone) {
            super(source);
            this.email = email;
            this.phone = phone;
        }
        public String getEmail() {
            return email;
        }
        public void setEmail(String email) {
            this.email = email;
        }
        public String getPhone() {
            return phone;
        }
        public void setPhone(String phone) {
            this.phone = phone;
        }
    }

    2.4.2 事件監(jiān)聽

    @Component
    public class ComplaintEventListener {
    
        /**
         * 只監(jiān)聽NoticeEvent事件
         * @author: yh
         * @date: 2022/8/27
         */
        @Async
        @EventListener(value = NoticeEvent.class)
    //    @Order(1) 指定事件執(zhí)行順序
        public void sendEmail(NoticeEvent noticeEvent) {
            //發(fā)郵件
            try {
                Thread.sleep(3000);
                System.out.println("發(fā)送郵件任務(wù)執(zhí)行完成, " + noticeEvent.getEmail() + " 當(dāng)前線程:" + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    
        @Async
        @EventListener(value = NoticeEvent.class)
    //    @Order(2) 指定事件執(zhí)行順序
        public void sendMsg(NoticeEvent noticeEvent) {
            //發(fā)短信
            try {
                Thread.sleep(3000);
                System.out.println("發(fā)送短信步任務(wù)執(zhí)行完成, " + noticeEvent.getPhone() + " 當(dāng)前線程:" + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    2.4.5 事件發(fā)布

    @RequestMapping(value = "/api")
    @RestController
    public class ExampleController {
        /**
         * 用于事件推送
         * @author:  yh
         * @date:  2022/8/27
         */
        @Autowired
        private ApplicationEventPublisher applicationEventPublisher;
    
        @RequestMapping(value = "/bind", method = RequestMethod.GET)
        public String bind(@RequestParam("email") String email) {
            long startTime = System.currentTimeMillis();
            try {
                // 綁定郵箱....業(yè)務(wù)
                Thread.sleep(2000);
    
                // 發(fā)布事件,這里偷個懶手機號寫死
                applicationEventPublisher.publishEvent(new NoticeEvent(this, email, "13211112222"));
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            long endTime = System.currentTimeMillis();
            System.out.println("方法執(zhí)行完成返回,耗時:" + (endTime - startTime));
            return "ok";
        }
    }

    運行結(jié)果:

    Spring異步接口返回結(jié)果的方式有哪些

    關(guān)于“Spring異步接口返回結(jié)果的方式有哪些”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“Spring異步接口返回結(jié)果的方式有哪些”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI