无论是软件还是网站,业务逻辑往往是复杂的,有些模块往往需要经过复杂的运算查询等耗时较长的操作,为了避免与之关联的模块等待太久,应该使用独立的线程来完成这类操作。不过一些编程语言或者框架不易实现多线程,这时很容易想到通过其他进程来实现。进程间实现了异步,而进程间的通信可以实现消息通知的方式。
通知的过程可以借助任务队列来实现。任务队列顾名思义就是“传递任务的队列”。与任务队列进行交互的实体有两类,一类是生产者(producer),一类是消费者(consumer)。生产者会将需要处理的任务放入任务队列中,而消费者则不断地从任务队列中读入任务信息并执行。使用任务队列有如下好处:
- 松耦合。生产者和消费者无需知道彼此的实现细节,只需要约定好任务的描述格式。这使得生产者和消费者可以由不同的编程语言实现。
- 易于扩展消费者可以有多个,而且可以分布在不同的服务器中。借此可以轻易地降低单台服务器的负载。
1. 使用Redis实现任务队列
说道任务队列自然就能想到Redis的列表类型,使用LPUSH和RPOP命令实现队列的概念。如果要实现任务队列,只需要让生产者将任务用LPUSH命令加入到某个键中,另一边让消费者不断地使用RPOP命令从该键中取出任务即可。其伪代码为:
loop
$task = RPOP queue
if $task
execut($task)
else
wait 2 second
到此便实现了一个简单的Redis任务队列。不过有一点不好的地方:当任务队列为空时,消费者还要每2秒钟调用RPOP来查看是否有新的任务。如果可以实现一旦有新的任务加入到队列就通知消费者就好了。其实,借助
BRPOP命令可以实现这样的需求。
BRPOP可RPOP命令相似,唯一的区别是当列表中没有元素时BRPOP命令会一直阻塞住连接,直到有新的元素加入。那么,新的伪代码为:
loop
$task = BRPOP queue 0
execut($task[1])
BRPOP接收两个参数,第一个是键名,第二个是超时时间,单位为秒。当超过了此时间仍然没有获得新元素的话,则返回nil。若时间设置为0,则表示无限等待。当获得一个元素后BRPOP命令返回两个值,分别是键名和元素值。
为了测试BRPOP命令,我么打开两个终端,并分别运行redis-cli实例,在实例A中执行: