Spring中基于事件监听驱动 和 线程池的异步任务

事件监听驱动 与 异步

事件监听驱动优点:解耦,将 事件和业务进行解耦,通过@Asyc注解可以实现异步

事件监听驱动优点:解耦,将 事件和业务进行解耦,通过@Asyc注解可以实现异步

我们监听事件之前要有事件源source,事件(Event),发布事件(publishEvent),然后才能到监听事件。

事件驱动机制是观察者模式(称发布订阅)具体实现,事件对象(Event)相当于被观察对象(Subject), 事件监听(EventListener) 相当于观察者(Observer)

事件源

实现ApplicationContextAware接口
重写setApplicationContext方法
获取ApplicationContext对象

public class FilePhysicalDeleteEventListener implements ApplicationContextAware {


    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

ApplicationContextAware接口

ApplicationContext作用具体参考 https://blog.csdn.net/Pluto372/article/details/130139628

在Spring/SpringMVC中,我们拿到IOC容器无非有三种方式,那就是使用ApplicationContext接口下的三个实现类:ClassPathXmlApplicationContextFileSystemXmlApplicationContextAnnotationConfigApplicationContext
但是SpringBoot的强大让我们无需再配置xml文件,也因此我们无法通过上述方式拿到ApplicationContext对象,所以当在项目需要用到spring中的bean对象时,一般做法就是实现ApplicationContextAware接口,通过这个接口就可以获取到ApplicationContext对象,进入从ApplicationContext中获取所需要bean对象。

总结:通过ApplicationContextAware接口获取ApplicationContext对象,ApplicationContext可以获取IOC容器中的bean

发布事件

通过ApplicationContext对象发布

 private void physicalDeleteFileByStorageEngine(List<RPanFile> realFileRecords) {
     //映射为路径集合
     List<String> realFilePathList = realFileRecords.stream().map(RPanFile::getRealPath).collect(Collectors.toList());
     DeleteFileContext context = new DeleteFileContext();
     context.setRealFilePathList(realFilePathList);
     try {
         storageEngine.delete(context);
     } catch (IOException e) {
         //记录错误日志
         applicationContext.publishEvent(new ErrorLogEvent(this, "实体文件:" + JSON.toJSONString(realFilePathList) + ", 物理删除失败,请执行手动删除", RPanConstants.ZERO_LONG));
     }
 }

事件实体

事件实体需要继承ApplicationEvent对象

@Getter
@Setter
@EqualsAndHashCode
@ToString
public class ErrorLogEvent extends ApplicationEvent {

    /**
     * 错误日志的内容
     */
    private String errorMsg;

    /**
     * 当前登录的用户ID
     */
    private Long userId;

    public ErrorLogEvent(Object source, String errorMsg, Long userId) {
        super(source);
        this.errorMsg = errorMsg;
        this.userId = userId;
    }
}

监听事件

注解@EventListener(ErrorLogEvent.class)方式监听事件

@Component
public class ErrorLogEventListener {

    @Autowired
    private IErrorLogService iErrorLogService;
    /**
     * 监听系统错误日志事件,并保存到数据库中
     *
     * @param event
     */
    @EventListener(ErrorLogEvent.class)     //监听这个事件
    @Async(value = "eventListenerTaskExecutor")
    public void saveErrorLog(ErrorLogEvent event){
        RPanErrorLog record = new RPanErrorLog();
        //保存到数据库,调用mp 方法
        iErrorLogService.save(record);
    }
}

实现异步

实现异步,并指定线程池任务执行器,value 为指定线程池执行器的 bean的名称

@Async(value = “eventListenerTaskExecutor”)

当 ErrorLogEvent 事件发生时,相关的处理方法将会以异步的方式执行,
并且将使用指定的任务执行器 “eventListenerTaskExecutor”。不阻塞主线程,提高响应。

public class ErrorLogEventListener {

    @Autowired
    private IErrorLogService iErrorLogService;
    /**
     * 监听系统错误日志事件,并保存到数据库中
     *
     * @param event
     */
    @EventListener(ErrorLogEvent.class)     //监听这个事件
    @Async(value = "eventListenerTaskExecutor")
    public void saveErrorLog(ErrorLogEvent event){
        RPanErrorLog record = new RPanErrorLog();
        //保存到数据库,调用mp 方法
        iErrorLogService.save(record);
    }
}

注入綫程池

通过Configuration注解和Bean注解以配置类的方式注入线程池
,通过name属性指定bean的名称

@SpringBootConfiguration
public class TreadPoolConfig {

    // 注入bean
    // 任务线程池执行器
    @Bean(name = "eventListenerTaskExecutor")
    public ThreadPoolTaskExecutor eventListenerTaskExecutor(){
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//        new FutureTask<>();
        taskExecutor.setCorePoolSize(10);
        taskExecutor.setMaxPoolSize(10);
        taskExecutor.setKeepAliveSeconds(200);
        taskExecutor.setQueueCapacity(2048);
        taskExecutor.setThreadNamePrefix("event-listener-thread");
        //拒绝策略
        // CallerRunsPolicy 既不抛弃任务也不抛出异常,直接使用主线程来执行此任务
        // abort 直接拒绝并抛异常
        // discard 丢弃不抛出异常
        // discardEldest 抛弃队列中等待最久的
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return taskExecutor;
    }
}

事件驱动机制,与MQ消息队列比较

MQ驱动的作用:解耦、异步、削峰
优点:解耦,异步,削峰,消息不丢失,解决高并发消息
缺点:难维护

事件驱动机制:解耦、异步,做不到削峰。
优点:维护简单
缺点:大并发扛不住,适合单机环境,消息可能丢失,无法削峰

总结MQ的优点

  1. 异步解耦
  2. 应对高并发的消息
  3. 适用于分布式环境
  4. 消息不丢失
  5. 对消息进行 削峰

总结MQ的缺点:

  1. 分布式场景下引发的复杂问题,如分布式事务等。
  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值