SpringBoot:Event实现发布/订阅模式

开发 架构
不管是否基于spring boot 的发布订阅模型,最终都是开启了线程执行任务,和使用第三方的MQ消息组件,问题在于重启服务器或者未知原因崩溃的时候,消息的恢复机制要自行处理。

如图所示支付业务中,用户支付成功之后,后续还有很多的业务流程,但是对于用户来讲是透明的,所以为了提高接口的响应速率,提高用户体验,后续操作都会选择异步执行

异步执行方式

异步执行主体

@Service
public class OrderService {
   public void orderSuccess(){

       // 订单完成异步任务开启 可以再统一封装
       Order order = new Order();
       order.setOrderNo(String.valueOf(System.currentTimeMillis()));
       Map<String, OrderSuccessService> orderSuccessServiceMap = SpringContextUtil.getBeansOfType(OrderSuccessService.class);
       orderSuccessServiceMap.values().forEach(service -> {
           service.orderSuccess(order);
      });
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

异步执行接口

public interface OrderSuccessService {
   /**
    * 订单支付成功
    * @param order
    */
   public CompletableFuture<Boolean> orderSuccess(Order order);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
@Slf4j
@Service
public class MerchantNoticeServiceImpl implements OrderSuccessService {
   @Override
   @Async("taskExecutor")
   public CompletableFuture<Boolean> orderSuccess(Order order) {
       log.info("{}商户通知:{}",Thread.currentThread(),order);
       // 返回异步调用的结果
       return CompletableFuture.completedFuture(true);
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
@Slf4j
@Service
public class MerchantNoticeServiceImpl implements OrderSuccessService {
   @Override
   @Async("taskExecutor")
   public CompletableFuture<Boolean> orderSuccess(Order order) {
       log.info("{}商户通知:{}",Thread.currentThread(),order);
       // 返回异步调用的结果
       return CompletableFuture.completedFuture(true);
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
@Slf4j
@Service
public class MerchantNoticeServiceImpl implements OrderSuccessService {
   @Override
   @Async("taskExecutor")
   public CompletableFuture<Boolean> orderSuccess(Order order) {
       log.info("{}商户通知:{}",Thread.currentThread(),order);
       // 返回异步调用的结果
       return CompletableFuture.completedFuture(true);
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

自定义线程池,线程池隔离,开启异步任务执行

@Configuration // 配置类
@EnableAsync // @Async注解能够生效
public class TaskConfiguration {
   @Bean("taskExecutor")
   public Executor taskExecutor(){
       ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
       // 线程池创建时候初始化的线程数
       executor.setCorePoolSize(5);
       // 线程池最大的线程数,只有在缓冲队列满了之后,才会申请超过核心线程数的线程
       executor.setMaxPoolSize(10);
       // 用来缓冲执行任务的队列
       executor.setQueueCapacity(200);
       // 当超过了核心线程之外的线程,在空闲时间到达之后会被销毁
       executor.setKeepAliveSeconds(60);
       // 可以用于定位处理任务所在的线程池
       executor.setThreadNamePrefix("taskExecutor-orderSuccess-");
       // 这里采用CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在execute方法的调用线程中运行被拒绝的任务;
       // 如果执行程序已关闭,则会丢弃该任务
       executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
       // 设置 线程池关闭 的时候 等待 所有任务都完成后,再继续 销毁 其他的 Bean,
       // 这样这些 异步任务 的 销毁 就会先于 数据库连接池对象 的销毁。
       executor.setWaitForTasksToCompleteOnShutdown(true);
       // 该方法用来设置线程池中 任务的等待时间,如果超过这个时间还没有销毁就 强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
       executor.setAwaitTerminationSeconds(60);
       return executor;
  }
}
  • 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.

Spring Event实现发布/订阅模式

自定义事件:通过继承ApplicationEve​

nt,并重写构造函数,实现事件扩展。

public class OrderApplicationEvent extends ApplicationEvent {
   public OrderApplicationEvent(OrderData orderData){
       super(orderData);
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

定义事件的消息体

@Data
public class OrderData {
   /**
    * 订单号
    */
   private String orderNo;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

事件监听

@Slf4j
@Service
public class MerchantNoticeListener {
   @Async("asyncEventTaskExecutor")
   @EventListener
   public CompletableFuture<Boolean> orderSuccess(OrderApplicationEvent event) {
       log.info("{}商户通知:{}",Thread.currentThread(),event);
       // 返回异步调用的结果
       return CompletableFuture.completedFuture(true);
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
@Slf4j
@Service
public class UserNoticeListener implements ApplicationListener<OrderApplicationEvent> {
   @Override
   @Async("asyncEventTaskExecutor")
   public void onApplicationEvent(OrderApplicationEvent event) {
       log.info("{}用户通知:{}",Thread.currentThread(),event);
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
@Slf4j
@Service
public class UserNoticeListener implements ApplicationListener<OrderApplicationEvent> {
   @Override
   @Async("asyncEventTaskExecutor")
   public void onApplicationEvent(OrderApplicationEvent event) {
       log.info("{}用户通知:{}",Thread.currentThread(),event);
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
@Slf4j
@Service
public class UserNoticeListener implements ApplicationListener<OrderApplicationEvent> {
   @Override
   @Async("asyncEventTaskExecutor")
   public void onApplicationEvent(OrderApplicationEvent event) {
       log.info("{}用户通知:{}",Thread.currentThread(),event);
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

自定义线程池

@Configuration
@Slf4j
@EnableAsync // @Async注解能够生效
public class AsyncConfiguration implements AsyncConfigurer {
   @Bean("asyncEventTaskExecutor")
   public ThreadPoolTaskExecutor executor(){
       //Spring封装的一个线程池
       ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
       executor.setCorePoolSize(5);
       executor.setMaxPoolSize(50);
       executor.setQueueCapacity(30);
       executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
       executor.setThreadNamePrefix("asyncEventTaskExecutor--orderSuccess-");
       executor.initialize();
       return executor;
  }

   @Override
   public Executor getAsyncExecutor(){
       return executor();
  }

   /**
    * 异常处理
    * @return
   @Override
   public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler(){
       return (ex, method, params) -> log.error(String.format("[async] task{} error:", method), ex);
  }
}
  • 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.

事件发布

@Service
@Slf4j
public class OrderEventService {
   private final ApplicationEventPublisher applicationEventPublisher;

   public OrderEventService(ApplicationEventPublisher applicationEventPublisher){
       this.applicationEventPublisher = applicationEventPublisher;
  }
   public void success(){
       OrderData orderData = new OrderData();
       orderData.setOrderNo(String.valueOf(System.currentTimeMillis()));
       // 消息
       OrderApplicationEvent orderApplicationEvent = new OrderApplicationEvent(orderData);
       // 发布事件
       applicationEventPublisher.publishEvent(orderApplicationEvent);
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

写在最后:不管是否基于spring boot 的发布订阅模型,最终都是开启了线程执行任务,和使用第三方的MQ消息组件,问题在于重启服务器或者未知原因崩溃的时候,消息的恢复机制要自行处理。

建议使用在一些边缘业务,比如记录日志,这些要求没有那么高的业务。

责任编辑:武晓燕 来源: 今日头条
相关推荐

2022-06-27 13:56:10

设计模式缓存分布式系统

2024-07-02 11:42:53

SpringRedis自定义

2024-07-29 08:34:18

C++订阅者模式线程

2023-11-10 09:22:06

2025-01-09 11:15:47

2009-11-05 10:07:37

WCF设计模式

2023-03-03 12:39:25

MQTT工厂模式

2025-02-25 09:29:34

2024-03-28 08:07:42

RabbitMQ订阅模式

2024-05-14 08:03:51

C#EventArgs​属性

2021-08-02 17:21:08

设计模式订阅

2023-12-04 08:24:23

2024-01-10 08:16:08

Redis集成JMS

2022-07-13 08:36:57

MQ架构设计模式

2020-09-15 10:25:13

Redis命令Java

2022-08-15 09:02:22

Redis模式订阅消息

2010-03-02 16:28:11

WCF发布订阅

2024-12-13 16:37:56

SpringBootJava

2021-11-08 11:32:01

观察

2022-09-19 16:08:31

Dapr发布订阅
点赞
收藏

51CTO技术栈公众号