背景
事件驱动模型编程是程序设计中经常会用到的方法技巧,本质上是为了解耦事件的发布者和订阅者,实现组件之间的松耦合,提高应用程序的扩展性;另外,在一些业务场景中,顺序、阻塞式的执行任务会遇到一些比较耗时的中间步骤,但是往往我们不希望整个流程都停下来等待这些中间过程完成,这个时候我们就会考虑异步执行这些中间步骤; 综上所述,我们需要的是一个可异步执行的事件模型框架;
这里就很容易想到spring Event; spring Event的使用支持Event的发布和订阅,同时也支持@Async注解来达到异步执行的目的。但是spring event有以下缺点:
- 事件的队列是存储在内存中的,一旦机器重启或者宕机,事件本身会丢失
- listener消费事件,消费失败不会重试,缺乏重试机制
设计
因此设计的标准就是:
- 支持异步事件模型(发布、订阅)
- 事件存储在db或者别的地方,重启不会导致事件本身的丢失
- 事件的消费支持重试
实现
消息实体
Topic枚举
public enum Topic {
/**
* 测试topic
**
TEST_TOPIC1;
}
Group枚举
public enum Group {
DEFAULT, //默认分组
FAIL; //失败分组
}
TrxMsg实体:
@Data
public class TrxMsg<T> {
private Long id;
/**
* 消息的topic
*/
private Topic topic;
/**
* 消息体
*/
private T content;
/**
* 累计执行次数
*/
private int execTimes;
/**
* 执行时间(延迟执行)
*/
private Date gmtExec;
/**
* 创建时间
*/
private Date gmtCreate;
/**
* 更新时间
*/
private Date gmtUpdate;
/**
* 消费分组
*/
private Group group;
/**
* 消息的状态
*/
private MsgStatus msgStatus;
}
消息的Listener
定义监听器接口
/**
* 消息监听器
*/
public interface TrxMsgListener<Msg> {
/**
* 监听哪一类消息
* @return
*/
public Topic getTopic();
/**
* 获取消息并执行
* @param msg
* @return
*/
public ExecResult execute(Msg msg);
}
消息mysql实体
建表语句
CREATE TABLE `trx_msg` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_update` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`topic` varchar(30) NOT NULL COMMENT '主题',
`gmt_exec` date