广告位联系
返回顶部
分享到

Spring异步接口返回结果的四种方式

java 来源:互联网 作者:佚名 发布时间:2022-08-28 20:31:42 人浏览
摘要

1. 需求 开发中我们经常遇到异步接口需要执行一些耗时的操作,并且接口要有返回结果。 使用场景:用户绑定邮箱、手机号,将邮箱、手机号保存入库后发送邮件或短信通知 接口要求

1. 需求

开发中我们经常遇到异步接口需要执行一些耗时的操作,并且接口要有返回结果。

使用场景:用户绑定邮箱、手机号,将邮箱、手机号保存入库后发送邮件或短信通知
接口要求:数据入库后给前台返回成功通知,后台异步执行发邮件、短信通知操作

一般的话在企业中会借用消息队列来实现发送,业务量大的话有一个统一消费、管理的地方。但有时项目中没有引用mq相关组件,这时为了实现一个功能去引用、维护一个消息组件有点大材小用,下面介绍几种不引用消息队列情况下的解决方式

定义线程池:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

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();

 

        // 设置核心线程数

        executor.setCorePoolSize(50);

        // 设置最大线程数

        executor.setMaxPoolSize(200);

        // 设置队列容量

        executor.setQueueCapacity(200);

        // 设置线程活跃时间(秒)

        executor.setKeepAliveSeconds(800);

        // 设置默认线程名称

        executor.setThreadNamePrefix("task-");

        // 设置拒绝策略

        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        // 等待所有任务结束后再关闭线程池

        executor.setWaitForTasksToCompleteOnShutdown(true);

        return executor;

    }

}

2. 解决方案

2.1 @Async

定义异步任务,如发送邮件、短信等

1

2

3

4

5

6

7

8

9

10

11

12

13

@Service

public class ExampleServiceImpl implements ExampleService {

    @Async("taskExecutor")

    @Override

    public void sendMail(String email) {

        try {

            Thread.sleep(3000);

            System.out.println("异步任务执行完成, " + email + " 当前线程:" + Thread.currentThread().getName());

        } catch (InterruptedException e) {

            throw new RuntimeException(e);

        }

    }

}

Controller

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

@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 {

            // 绑定邮箱....业务

            Thread.sleep(2000);

        } catch (InterruptedException e) {

            throw new RuntimeException(e);

        }

        //模拟异步任务(发邮件通知、短信等)

        exampleService.sendMail(email);

 

        long endTime = System.currentTimeMillis();

        System.out.println("方法执行完成返回,耗时:" + (endTime - startTime));

        return "ok";

    }

}

运行结果:

2.2 TaskExecutor

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

@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 {

            // 绑定邮箱....业务

            Thread.sleep(2000);

 

            // 将发送邮件交给线程池去执行

            taskExecutor.execute(() -> {

                exampleService.sendMail(email);

            });

        } catch (InterruptedException e) {

            throw new RuntimeException(e);

        }

 

        long endTime = System.currentTimeMillis();

        System.out.println("方法执行完成返回,耗时:" + (endTime - startTime));

        return "ok";

    }

}

运行结果:

2.3 Future

首先去掉Service方法中的@Async("taskExecutor"),此时执行就会变成同步,总计需要5s才能完成接口返回。这次我们使用jdk1.8中的CompletableFuture来实现异步任务

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

@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 {

            // 绑定邮箱....业务

            Thread.sleep(2000);

 

            // 将发送邮件交给异步任务Future,需要记录返回值用supplyAsync

            CompletableFuture.runAsync(() -> {

                exampleService.sendMail(email);

            }, taskExecutor);

 

        } catch (InterruptedException e) {

            throw new RuntimeException(e);

        }

 

        long endTime = System.currentTimeMillis();

        System.out.println("方法执行完成返回,耗时:" + (endTime - startTime));

        return "ok";

    }

}

运行结果:

2.4 @EventListener

Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式;为的就是业务系统逻辑的解耦,提高可扩展性以及可维护性。事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,发布者的工作只是为了发布事件而已。

2.4.1 定义event事件模型

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

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 事件监听

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

@Component

public class ComplaintEventListener {

 

    /**

     * 只监听NoticeEvent事件

     * @author: yh

     * @date: 2022/8/27

     */

    @Async

    @EventListener(value = NoticeEvent.class)

//    @Order(1) 指定事件执行顺序

    public void sendEmail(NoticeEvent noticeEvent) {

        //发邮件

        try {

            Thread.sleep(3000);

            System.out.println("发送邮件任务执行完成, " + noticeEvent.getEmail() + " 当前线程:" + Thread.currentThread().getName());

        } catch (InterruptedException e) {

            throw new RuntimeException(e);

        }

    }

 

    @Async

    @EventListener(value = NoticeEvent.class)

//    @Order(2) 指定事件执行顺序

    public void sendMsg(NoticeEvent noticeEvent) {

        //发短信

        try {

            Thread.sleep(3000);

            System.out.println("发送短信步任务执行完成, " + noticeEvent.getPhone() + " 当前线程:" + Thread.currentThread().getName());

        } catch (InterruptedException e) {

            throw new RuntimeException(e);

        }

    }

}

2.4.5 事件发布

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

@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 {

            // 绑定邮箱....业务

            Thread.sleep(2000);

 

            // 发布事件,这里偷个懒手机号写死

            applicationEventPublisher.publishEvent(new NoticeEvent(this, email, "13211112222"));

        } catch (InterruptedException e) {

            throw new RuntimeException(e);

        }

        long endTime = System.currentTimeMillis();

        System.out.println("方法执行完成返回,耗时:" + (endTime - startTime));

        return "ok";

    }

}

运行结果:

3. 总结

通过@Async、子线程、Future异步任务、Spring自带ApplicationEvent事件监听都可以完成以上描述的需求。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://blog.csdn.net/weixin_43847283/article/details/126557747
相关文章
  • SpringBoot自定义错误处理逻辑介绍

    SpringBoot自定义错误处理逻辑介绍
    1. 自定义错误页面 将自定义错误页面放在 templates 的 error 文件夹下,SpringBoot 精确匹配错误信息,使用 4xx.html 或者 5xx.html 页面可以打印错误
  • Java实现手写一个线程池的代码

    Java实现手写一个线程池的代码
    线程池技术想必大家都不陌生把,相信在平时的工作中没有少用,而且这也是面试频率非常高的一个知识点,那么大家知道它的实现原理和
  • Java实现断点续传功能的代码

    Java实现断点续传功能的代码
    题目实现:网络资源的断点续传功能。 二、解题思路 获取要下载的资源网址 显示网络资源的大小 上次读取到的字节位置以及未读取的字节
  • 你可知HashMap为什么是线程不安全的
    HashMap 的线程不安全 HashMap 的线程不安全主要体现在下面两个方面 在 jdk 1.7 中,当并发执行扩容操作时会造成环形链和数据丢失的情况 在
  • ArrayList的动态扩容机制的介绍

    ArrayList的动态扩容机制的介绍
    对于 ArrayList 的动态扩容机制想必大家都听说过,之前的文章中也谈到过,不过由于时间久远,早已忘却。 所以利用这篇文章做做笔记,加
  • JVM基础之字节码的增强技术介绍

    JVM基础之字节码的增强技术介绍
    字节码增强技术 在上文中,着重介绍了字节码的结构,这为我们了解字节码增强技术的实现打下了基础。字节码增强技术就是一类对现有字
  • Java中的字节码增强技术

    Java中的字节码增强技术
    1.字节码增强技术 字节码增强技术就是一类对现有字节码进行修改或者动态生成全新字节码文件的技术。 参考地址 2.常见技术 技术分类 类
  • Redis BloomFilter布隆过滤器原理与实现

    Redis BloomFilter布隆过滤器原理与实现
    Bloom Filter 概念 布隆过滤器(英语:Bloom Filter)是1970年由一个叫布隆的小伙子提出的。它实际上是一个很长的二进制向量和一系列随机映射
  • Java C++算法题解leetcode801使序列递增的最小交换次

    Java C++算法题解leetcode801使序列递增的最小交换次
    题目要求 思路:状态机DP 实现一:状态机 Java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { public int minSwap(int[] nums1, int[] nums2) { int n
  • Mybatis结果集映射与生命周期介绍

    Mybatis结果集映射与生命周期介绍
    一、ResultMap结果集映射 1、设计思想 对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了 2、resultMap的应用场
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计