在某些应用场景,我们不想要整个业务流程中一部分耗时较长,且并不影响主流程的任务,同主流程一步一步的执行,这样很耽误对用户的响应。于是可以优化到同步执行或者异步去执行这些可以隔离的任务。比如邮件、短信的发送,交易通知带数据同步的问题。
常考虑到的是我们直接加入中间件,但是这样会增加系统的复杂性以及交易链路。所以遇到一般的支线任务,可考虑使用springBoot event.
通过demo了解这个过程,学习记录,欢迎指出不对或者建议.^^
首先得定义事件源,有以下几种的,根据场景任意选取
1-
public class MsgEvent {
/** 该类型事件携带的信息 */
public String orderId;
}
2-
public class OrderProductEvent extends ApplicationEvent {
/** 该类型事件携带的信息 */
private String orderId;
/**
* 在自定义事件的构造方法中除了第一个source参数,其他参数都可以去自定义,
* 可以根据项目实际情况进行监听传参,这里就只定义个简单的String字符串的透传
* @param source
* @param orderId
*/
public OrderProductEvent(Object source, String orderId) {
super(source);
this.orderId = orderId;
}
}
3-
public class MyEventDemo extends ApplicationEvent {
private static final Logger logger = LoggerFactory.getLogger(MyEventDemo.class);
private MessageEntity messageEntity;
/**
* 在自定义事件的构造方法中除了第一个source参数,其他参数都可以去自定义,
* 可以根据项目实际情况进行监听传参,这里就只定义个简单的String字符串的透传
*
* @param source
* @param messageEntity
*/
public MyEventDemo(Object source, MessageEntity messageEntity) {
super(source);
this.messageEntity = messageEntity;
}
public MessageEntity getMessageEntity() {
return messageEntity;
}
public void setMessageEntity(MessageEntity messageEntity) {
this.messageEntity = messageEntity;
}
}
然后,得发布事件,一般在业务层处理,通过ApplicationContext,ApplicationEventPublisher的publishEvent(agrs)都可以.
public class OrderService {
/** 注入ApplicationContext用来发布事件 */
private final ApplicationContext applicationContext;
/**
* 下单
*
* @param orderId 订单ID
*/
public String buyOrder(String orderId) {
long start = System.currentTimeMillis();
// 1.查询订单详情
// 2.检验订单价格 (同步处理)
applicationContext.publishEvent(new OrderProductEvent(this, orderId));
// 3.短信通知(异步处理)
applicationContext.publishEvent(new MsgEvent(orderId));
long end = System.currentTimeMillis();
log.info("任务全部完成,总耗时:({})毫秒", end - start);
return "购买成功";
}
}
最后便是事件的监听处理,可以通过@EventListener不配置参数监听方法内的对象事件。也可以通过@EventListener(MsgEvent.class)对象类这种方式监听,还可以通过监听对象中属性值来触发事件处理@EventListener(condition = “#event.orderId==‘123123’”)。对于异步事件来说,最好使用自定义线程池来处理,避免溢出。
@Slf4j
@Component
public class MsgListener {
@Async
@SneakyThrows
@EventListener(MsgEvent.class)
public void sendMsg(MsgEvent event) {
String orderId = event.getOrderId();
long start = System.currentTimeMillis();
log.info("event开发发送短信");
log.info("event开发发送邮件");
Thread.sleep(4000);
long end = System.currentTimeMillis();
log.info("{}:event发送短信、邮件耗时:({})毫秒", orderId, (end - start));
}
/**
* 按照具体条件值进行消费处理
* @param event
*/
@Async("taskExecutor")
@SneakyThrows
@EventListener(condition = "#event.orderId=='123123'")
public void sendMsg123(MsgEvent event) {
String orderId = event.getOrderId();
long start = System.currentTimeMillis();
log.info("开发发送短信");
log.info("开发发送邮件");
Thread.sleep(4000);
long end = System.currentTimeMillis();
log.info("{}:发送短信、邮件耗时:({})毫秒", orderId, (end - start));
}
@Async
@SneakyThrows
@EventListener(condition = "#event.orderId=='456456'")
public void sendMsg456(MsgEvent event) {
String orderId = event.getOrderId();
long start = System.currentTimeMillis();
log.info("开发发送短信");
log.info("开发发送邮件");
Thread.sleep(4000);
long end = System.currentTimeMillis();
log.info("{}:发送短信、邮件耗时:({})毫秒", orderId, (end - start));
}
}
还可以通过实现ApplicationListenner接口重写接口的方式,监听事件
同步消费事件
@Slf4j
@Component
public class OrderProductListener implements ApplicationListener<OrderProductEvent> {
/** 使用 onApplicationEvent 方法对消息进行接收处理 */
@SneakyThrows
@Override
public void onApplicationEvent(OrderProductEvent event) {
String orderId = event.getOrderId();
long start = System.currentTimeMillis();
Thread.sleep(2000);
long end = System.currentTimeMillis();
log.info("{}:校验订单商品价格耗时:({})毫秒", orderId, (end - start));
}
}
测试测试
/**
* 完整模板springboot的事件测试,@NotControllerResponseAdvice不对此返回做封装
* @param orderId
* @return
*/
@GetMapping(value = "/fullVersionSpringBootEventTest")
@NotControllerResponseAdvice
public String fullVersionSpringBootEventTest(String orderId){
return orderService.buyOrder(orderId);
}
根据实际场景选取是否使用,自带的功能很多东西没有完善,