今天被我姐叫出去吃饭,聊天的时候又聊到为什么宅了。还能为什么,当然是因为本人是个重度懒癌患者咯 /傲娇脸
今天又懒得写代码了,给大家分享一个我们现在在用的小小任务系统。
为什么需要任务系统呢?
其实无非是两方面,一方面就是这个功能的处理时间较长必须使用异步来处理,比如订单导出,订单导入发货。另一方面就是为了实现业务逻辑的解耦。
什么叫解耦以及为什么需要解耦?
可以想象一下用户支付成功之后都需要进行哪些业务逻辑需要处理。
1.检查是否是待支付状态,不是则判定为支付超时进行退款操作。
2.记录支付信息并修改订单状态
3.计算销量及库存
4.释放库存锁
5.发送短信和微信通知用户购买成功
可以看到,支付成功之后少说也有5步,如果再有积分系统还得加入计算积分的逻辑。而且随着业务的不断扩展,这里的逻辑会越来越复杂。最可怕的是,因为都在这一个步骤里,每次增减这里的业务代码都会对整个系统造成影响,只要有一步出现问题都会导致整个接口不可用。一般这种场景我们就说这里的业务逻辑耦合度太高了。
如何解耦?
当然就是利用消息队列异步的去处理这些逻辑。还拿支付这里举例。解耦之后这个接口只需要处理支付成功必须的业务逻辑,然后发送一个消息告诉任务系统就可以了,比如支付超时的退款操作、计算销量及库存、发送通知都可以由任务系统去完成。
一个简单任务系统
如图,这个系统由以下几部分组成。
1.Jobpool,即任务池,是由key-value组成的任务数据
2.Ready队列,由Redis的队列做为实时处理队列
3.Delay队列,由Redis的有序集合做为延迟处理队列
4.Server,负责接收外部系统发送来的消息,采用Hprose实现的RPC服务
5.Worker,负责消费实时队列中的任务,采用Swoole的多进程模块实现
6.Timer,负责消费延迟队列中的任务,采用Swoole的定时器模块实现
举例说明一个任务的生命周期
1.Server接收到事件通知后将会去查询该事件注册的任务有哪些,然后将这些任务根据注册信息扔到对应的队列中,如果有延迟时间则丢到Delay队列中,如果没有则丢到Ready队列中。
2.如果是延迟任务,那么将等待Timer的扫描,当到了执行时间时Timer会将该任务丢到Ready队列中。
3.Worker会阻塞Pop实时队列中的任务,当拿到一个任务时就会启动一个子进程去处理该任务。
4.处理完成后将处理结果写入数据库。
结尾
今天我们只从系统架构上来了解一下这个系统,具体的底层实现基础有兴趣的可以阅读我之前的文章http://www.jianshu.com/p/54ffd360454f
写这篇文章的时候这个系统的第一个版本还没做完呢,当时是刚确定了方案的可实施性。现在这个小系统经过几次迭代已经可以稳定的运行了,平均每个月处理的任务数量大概有三四百万个。
最近我们又筹备升级了,准备将Redis替换成RabbitMQ,下次再跟大家分享有关RabbitMQ的技术。GoodNight~
**** 我是闫大伯,一只进化中的野生程序猿 ****