引语:在许多的web应用中,我们都是通过同步操作的方式去处理我们的业务,但是往往也有这样的业务诉求,即一个操作可能比较耗时,或者有许多的不确定性(如支付操作需要等待第三方结果通知)。在这种业务场景下,再使用同步的方式去操作,可能就不太合理了。那我们想到的是,使用同步先返回临时结果,再通过异步通知最终结果的方式,进行处理,这是必须的。
那么对于这种场景,怎么处理呢?
对于有自己处理异步操作能力的语言(如java),那么对于这种情况,只需要抛一个线程到代码里,指定一个回调处理即可。
对于没有自行处理异步操作的语言(如php),相对来说就麻烦一点,一般需要借助于后台一些一直运行的进程,它会一直去检测是否有需要处理的事情,如果有,根据需求,依次去操作。像这种操作,我们可以称之为,消息处理。那么,当遇到耗时操作的时候,我们只需要立即将消息存放到消息队列中,以便后台进程能读取到该消息!
市面上有很多现成的消息队列的软件如:rabbitmq,kafka等,如果真有需求,可以选择其中一个进行使用,本文只为解释一些基本原理及个人实践。
因为我用的是php语言,所以,也只能就php的这种特性来表达一下自己的处理方式。对于有自己处理能力的语言,他们可以自行处理各种并发问题,但是对于需要cron脚本辅助的语言,则需要更多的操作!
1. 怎样将消息存入队列?
既然操作很耗时,那我们可以将不耗时的操作,先处理了,然后将后续工作交给服务器,那么我们怎样存储这些要操作的事项呢?方法应该是很多的,比如存储至文件,存储到数据库,存储到缓存。当然了,最好的方式当然是存储到缓存,借助于redis或者memcache这样的缓存工具,可以轻松的实现push和pop操作。但是消息可能就只是一些字符串,怎样表达清楚需要做的事呢?这个,方式当然也很多了,自己定义好消息的结构,如:{msgType: aa, params: [{p1:2, p2:3}], addTime:2016-05-15 10:30:03, from:[{ip:1.1.1.1, who:lee}]}, 然后将消息序列化或者json化,存储起来即可!
2. 怎样处理消息?
消息存入队列后,怎样去处理呢?一般有两种:1. 写一个死循环,一直去检测消息内容,检测到后,根据消息类型,就立即做相应处理。2. 使用cron脚本的方式,定期去检测消息,有就处理,没有就退出!其实还有第三种,使用cron定期执行脚本,但是一次执行就需要处理完所有的消息,再进行返回,提高效率!(每处理完一条消息,就必须将该消息删除或者存放到其他地方)
3. 怎样避免并发执行?
消息应该是只能一次执行的,如果被多次执行,则已超出预期结果,所以,一定要注意多进程同时执行一条消息情况,主要有两种方式,1. 程序中自己创建文件锁,执行前先检测是否存在该锁,没有则创建,执行完后删除,但是这种情况一定要注意程序意外终止的情况(如果意外终止,锁将一直存在,后续将永远无法执行)。2. 让cron生成锁,控制该文件只被一个进程同时执行;
4. 注意事项?
如果消息只是存储于缓存中,执行完成之后,就将其删除,那么,对于排查及恢复来说,都是致命的,所以,一般需要在删除之前,先将其存储到一个备份的位置或者以日志的方式将消息保存下来!
简单来说,消息队列就是一个消息推入,一个消息执行的过程。其他的,只是为了保障执行过程中的各种意外,各种恢复,各种去重操作罢了!(好像也找不到其他话说了我,哈哈哈...)