背景介绍
由于公司需要进行公共服务架构调整,决定采用开源的Jeepay项目来构建自身的公共支付服务。
传送门:Jeepay项目github地址
在现有项目的基础上,我们进行了一些业务层面的改造,以更好地适应公司的业务场景。关于这些业务改造的具体内容,在本次专题中不会详细展开。
Jeepay是一套适合互联网企业使用的开源支付系统,支持多渠道服务商和普通商户模式。已对接微信支付,支付宝,云闪付官方接口,支持聚合码支付。
专题想要重点讨论的是:关于Jeepay中MQ的相关功能扩展,以及技术实现细节。
Jeepay天然支持四种MQ,分别为:ActiveMQ、RabbitMQ、AliyunRocketMQ、RocketMQ。
然而,公司的公共服务中选择了Kafka作为消息中间件。我们并不想因为Jeepay的需求而额外引入其他的MQ组件。因此,我们计划通过代码改造的方式,使Jeepay支持Kafka。
以上就是该专题的项目背景,希望可以帮助大家理解后续的讨论内容。
难点分析
阅读接口我们可以得知,Jeepay为集成MQ,提供了如下两个接口进行功能扩展:
1、MQSender接口要求:实现即时发送和延迟发送这两个方法
public interface IMQSender {
/** 推送MQ消息, 实时 **/
void send(AbstractMQ mqModel);
/** 推送MQ消息, 延迟接收,单位:s **/
void send(AbstractMQ mqModel, int delay);
}
2、MQ的发送方式,被抽象成为两种:队列发送和广播发送,都需要Kafka进行实现。
public enum MQSendTypeEnum {
/** QUEUE - 点对点 **/
QUEUE,
/** BROADCAST - 订阅模式 **/
BROADCAST
}
在专题开始之前,我觉得我们有必要搞清楚如下两个问题:
1、为什么Jeepay没有集成kafka
2、为什么kafka没有延时发送功能
思考这个两个问题的好处在于:能够帮助于我们在后续的方案设计阶段,快速的找到适合自己的解决方案。
为什么Jeepay没有集成kafka
提到消息中间件,Kafka在某些领域和场景的应用十分广泛。
既然如此,那为什么Jeepay中没有对其进行集成?
我通过对Kafak进行的系统性了解,得出了以下几个结论。个人理解,欢迎讨论和补充。
- kafka不能天然支持延迟发送 。
- kafka的广播模式的实现,不符合Jeepay的MQ框架标准。
- Kafka在大数据领域下具有低延迟、高吞吐的特色 ,但是消息可靠性上并没有提供完善的解决方案。在支付场景中无法发挥出它应有的能力。
为什么kafka没有延时发送
在上个问题中,我们站在应用层的角度,思考了为什么Jeepay没有集成kafka的几点原因。
现在,站在架构的层面上,我们接着思考:为什么kafka没有延时发送呢?
这个问题基于Kafka的底层原理,简要回顾一下:Kafka底层原理剖析
1、kafka 最低结构是一个分区(Partition),它是队列中具有增量偏移量的顺序事件 —— 即在生成日志的那一刻,除了末尾之外,不能在其他任何地方插入日志,没有延迟消息的概念。
2、除此之外,kafka 的定位是实时流处理平台,延时发送功能在业务应用场景上,也没有什么特别的必要。
所以,想要实现延时发送,底层天然不支持。
因此,我们只能尝试从外围改造,来解决这个问题。
定时发送和延时发送都是绝对时间,虽然 kafka 内部有用到时间轮的概念,但仅依靠时间轮,对于解决延时发送还是过于简单。在面对这个问题时,我们首先需要明白如下几个难点:
1、因为延时任务存在不可预期性,有的可能是十分钟后执行,有的可能是是半年或者一年后执行。这种情况下直接使用单个时间轮会导致圈数过大。
2、一个槽中的所有任务分发也是比较复杂的逻辑。
在寻找解决方案的时候,我们参考了RocketMQ的实现方式:RocketMQ只持特定的延时时间段:1s, 5s, 10s … 2h。不支持任意时间段的延时。因此,我们可以总结出两种解决延时发送问题的思路:
1、如果业务场景并不包括实时的大数据流处理的场景,且延时的时间跨度不大,我们可以把延迟消息存储到某种介质中,这种介质可以是key-value数据库、可以是redis缓存,可以是内存中的hashmap等等… 但这样做,性能方面肯定大打折扣。
2、如果从生产的角度,考虑性能的前提下,最极限的解决方案就是参考RocketMQ的实现方式。
最终,在进行权衡后,我们选择了第一种解决方案:利用Redis的有序集合(Zset)构建缓存队列,将延迟消息存储其中,从而实现延时发送功能。
下一章我们继续讨论:02-Kafka实现延迟消息与广播模式详细设计