我们写程序难免遇到生产消费模式。如果自己写的话,一般有2种方式。
第一种是同步的方式。每次调用的时候通过调用发布的接口,等待事件完成。
第二种是异步的方式。生产者把事件传递给queue,然后消费者那边进行消费。
这里设计在变更的时候,总是麻烦,需要提前做接口的设计,实现一个同步的类,实现一个异步的类,根据不同的情况进行类的构建。当遇到多种事件时候,还需要做不同的适配和分发。
springboot自带的方案
springboot本身有实现,帮助我们把上面两种情况屏蔽了,并且给了一个开发友好的操作。
生产者的代码是一样的。
@Autowired
ApplicationContext applicationContext;
applicationContext.publishEvent(new Message("test"));
生产者的代码只要注入applicationContext即可,直接调用publishEvent的方法。这里传递的类自己设计,他接收一个object对象。
消费者的代码就有区别了。
同步消费
@EventListener(Message.class)
public void testMessage(Message message) {}
这里需要增加EventListener的注解,并且在里面写消费对象的类型。
下面写的方法名可以随意。springboot通过注解的方式解耦方法,用户不用关心数据的来源,直接写方法就好,也不用纠结如何构造这样的生产消费模式。在注解中写自己需要消费的类。springboot做数据的分发。
异步消费
@Async
@EventListener(Message.class)
public void testMessage(Message message) {}
相比同步消费,异步消费的区别就是增加@Async的注解,生产者不会等消费代码走完就会继续走下去。
优点
上面介绍了springboot的event的方法,这么写的好处就是解耦和利于发开,并且spring已经管理了分发和同步异步的方式。变更也是改个注解就成功了。
缺点
上面的类大家看出来,写法是一种事件发生就必须处理的方式。如果想做批处理,还是需要自己做的。
事件常见的需求就是事件的合并,如果相同的事件多个生产者同时出现,在一段时间内发生的相同事件需要合并成为1个,这里需要合并的规则。springboot的写法是有事件就处理,并没有提供做合并的策略。
无法控制事件的丢弃。事件有采样的需求,例如在一定次数内,只生产一个就好,spring由于屏蔽了细节,所以无法知道现在事件有多少,这次事件是不是应该生产。
小结
springboot的事件的使用方法
- 生产者
@Autowired
ApplicationContext applicationContext;
applicationContext.publishEvent(new Message("test"));
- 同步消费者
@EventListener(Message.class)
public void testMessage(Message message) {}
- 异步消费者
@Async
@EventListener(Message.class)
public void testMessage(Message message) {}
适用场景:
- 每个事件都需要处理
- 每个事件都需要生产
需要改造的场景:
- 消息合并
- 消息丢弃
改造方法:
统一在消费者处理,生产者每次都生成事件,合并和采样的逻辑在消费者处做。