当前位置:首页 > 问答 > 正文

用Redis搞异步任务队列,感觉挺方便的,队列处理也能快点儿了

最近在琢磨怎么让网站的一些操作别那么卡,比如用户注册完发个欢迎邮件,或者上传完图片生成个缩略图什么的,这些事儿吧,用户点了按钮之后其实就不用干等着了,但服务器要是在那儿吭哧吭哧地处理,用户就会觉得慢,后来听人说用Redis搞个异步任务队列挺方便的,队列处理也能快点儿了,我试了试,感觉确实不错,就跟有个专门跑腿儿的一样,把活儿接过去,主程序就能马上回头告诉用户“搞定啦”,实际那些费时间的活儿在后面慢慢干。

用Redis搞异步任务队列,感觉挺方便的,队列处理也能快点儿了

这道理其实就跟我们去银行或者餐厅排队拿号差不多,Redis呢,就相当于那个发号机和等着叫号的显示屏,主程序,就是那个接待你的柜台服务员,你(用户)过来要办个业务,发邮件”(这业务办起来慢),服务员不会让你一直站在柜台前干等,他会啪啪啪在电脑上把你的需求记下来,打出一张写着“发欢迎邮件给张三”的小票(这就是一个任务),然后顺手把这张小票扔进一个叫“待办任务”的盒子里(这个盒子就是Redis的列表List),扔进去之后,服务员马上就可以对你笑着说:“先生/女士,您的业务已经受理了,请稍后留意通知。”你就可以走了(网页可以显示成功,用户无需等待),至于发邮件这个实际动作,由后台专门负责发邮件的伙计(我们叫它工作进程Worker)去操心。

用Redis搞异步任务队列,感觉挺方便的,队列处理也能快点儿了

这个“待办任务”盒子(Redis的列表)有个很好的特性,它是个先进先出的队列,就像排队一样,先扔进去的任务小票,会被工作进程先取出来处理,Redis为啥适合干这个呢?首先它特别快,因为数据都放在内存里,往里扔个任务(LPUSH命令)或者从里面取个任务(RPOP命令)几乎是眨眼间的事儿,比用数据库或者文件系统来排队利索多了,它用起来简单,没那么多弯弯绕绕,就几个基本的命令就能把队列搭起来。

用Redis搞异步任务队列,感觉挺方便的,队列处理也能快点儿了

那后台那个专门干活的伙计(Worker)是咋工作的呢?它其实是个独立的程序,一天到晚就盯着那个“待办任务”盒子,它不停地问Redis:“盒子里有新的小票吗?”(用BRPOP命令),这个BRPOP命令很聪明,如果盒子是空的,它不会傻乎乎地一直问,而是会在那儿耐心地等着,直到有新的任务小票被扔进来,或者等到一个设定的超时时间,一旦有任务来了,它立马就把小票取出来,看到上面写着“发欢迎邮件给张三”,它就开始干活:调用发邮件的代码,连接邮件服务器,把邮件发出去,发完了,它就把这张小票销毁,然后继续去盒子那儿等着接下一个任务。

这样一来,好处就特别明显了,最直接的就是用户感觉快了,点击按钮后响应迅速,体验好,然后服务器的主程序(比如处理Web请求的那个)也轻松了,它只负责快速接收请求、生成任务、响应前端,把重活累活都甩给了后台的Worker们,这样就能腾出手来接待更多的用户,整个网站的吞吐量就上去了,万一哪天发邮件的服务临时抽风,慢得出奇,也不会影响到用户注册这个主要功能,因为Worker在那慢慢重试就好了,队列里的任务会堆积一些,但网站核心功能照常运行,我们还可以随时启动多个Worker一起来处理队列,相当于多开几个办事窗口,处理速度就更快了。

用Redis搞异步队列也不是说一点要注意的地方都没有,因为Redis是内存数据库,虽然快,但万一服务器重启了,内存里还没处理完的任务队列可能就丢了,所以对于那种丢了也无所谓,或者丢了还能补救的任务(比如刷新一下缓存),用这个很合适,但对于非常重要的任务,比如扣款下单,就得想更稳妥的办法,比如用消息队列(如RabbitMQ、Kafka),它们会保证任务至少被处理一次,但设置起来也复杂一些,任务处理成功了还是失败了,Worker得有个地方记录一下日志,方便我们查问题,有时候一个任务处理失败了,可能还需要把它重新放回队列再试几次。

对于大量常见的、对绝对可靠性要求不是那么极致的后台任务,用Redis来搞异步任务队列,就像给程序请了个不要钱的、效率极高的跑腿小哥,确实非常方便,能让队列处理的速度和整体系统的响应性提升一大截,是种简单实用的好办法。