一、任务队列
1.介绍
任务队列顾名思义就是传递任务的队列,与任务队列进行交互的角色有两类,一类是生产者(producer),一类是消费者(consumer).生产者会将需要处理的任务放到任务队列中,而消费者则不断的从任务队列中读取任务并执行。现实生活中有很多这样的例子:例如著名的面包店故事:厨师将做好的面包放到橱窗中,顾客通过购买从橱窗中拿到面包来吃。
2.任务队列的优势
- 松耦合:生产者和消费者无需知道彼此的实现细节,只需要约定好任务的描述格式,这使得生产者和消费者可以有不同的团队采用不用的编程语言来实现。
- 易于拓展:消费者可以有多个,而且可以分布在不同的服务器中,借此可以轻易的降低单台服务器的负载。
3.使用redis实现任务队列
首先,类比面包店的故事可以很显然的得知,任务队列最明显的三要素是:“厨师、橱窗、顾客”
这里我们使用列表类型来实现橱窗。因为列表类型可以从两端存取元素的特性(LPUSH、RPOP),首先看一下伪代码:
loop
#循环
$task = RPOP queue
if $task
#如果任务队列中有任务则执行它
execute($task)
else
#如果没有则等待一秒以免过于频繁的请求数据
wait 1 second
至此一个简单的队列已经完成了!
不过此时消费者不管队列中有无任务都会每秒执行一次RPOP命令查看是否有新任务,接下来用BRPOP实现一旦有新任务加入任务队列就通知消费者。
BRPOP与RPOP相似,区别是:当列表中没有元素时BRPOP会一直阻塞住连接知道有新任务加入。修改上边伪代码:
loop
#如果任务队列中没有新任务,BRPOP会一直阻塞,不会执行execute()
$task = BRPOP queue 0
#返回值是一个数组,数组的第二个元素使我们需要执行的任务
execute($task[1])
BRPOP接受两个参数,第一个是键名,第二个是过期时间(s),超过此时间仍没有获得新元素返回Nil,0表示不限制时间。
4.优先级队列
对于不同的队列来说是程序希望执行的完成速度也不同,有的希望在短时间内执行完成,而有的则可以接受较长的时间。为了在队列并发的情况下优先执行一些队列,我们需要实现一个优先级。
BRPOP可以接受多个键,并且如果多个键中都有元素则按照从左到右的顺序取键中的元素,这样可以保证不管后边的键中元素有多少个,消费者都会优先执行第一个键中的元素,以此类推。知道前边的键中的元素被执行完。
二、发布/订阅
(一)、发布/订阅模式可以实现进程进程间的消息传递:
发布/订阅模式中包含两个角色:发布者和订阅者,订阅者可以订阅一个或多个频道,而发布者可以向指定的频道发送消息,所有订阅此频道的订阅者都会接收到该消息;和一个“载体”:频道。
- 发布者:发布消息。命令是 PUBLISH channel message;channel代表频道名,message代表信息,返回值是订阅该频道的订阅者数量。发出去的消息不会持久化,即当有客户端订阅某个频道后只能收到后续该频道发出的消息。之前的就收不到了。
- 订阅者:订阅消息。命令是SUBSCRIPT channel;channel代表频道名。执行订阅命令后的客户端会进入订阅状态,处于此状态的客户端不能执行除:SUBSCRIBE、UNSUBSCRIBE、PSUBSCRIBE、PUNSUBSCRIBE四个之外的命令,否则会报错。进入订阅状态客户端可能会受到3种类型的回复。
(1)subscribe。表示订阅成功的返回值,第二个值是订阅成功的频道名称,第三个值是当前客户端订阅的频道数量。
(2)message。表示接收到的消息,第二个值表示产生消息的频道名称,第三个值表示消息内容。
(3)unsubscribe。表示成功取消订阅某个频道,第二个值是取消订阅的频道名称,第三个值是当前客户端订阅的频道数量。当数量为0时,会退出订阅模式,之后就可以执行其他非“发布/订阅”的命令了。
(二)、按照规则订阅
PSUBSCRIBE命令可以订阅指定的规则。
规则channel1.*可以匹配channel1.1和channel1.10但是匹配不到channel1.
返回结果如下所示:
第一个值表示接受信息,第二个值表示订阅时使用的通配符,第三个值表示实际接收到消息的频道,第四个值表示消息内容。
使用PSUBSCRIBE可以重复订阅一个频道,例如 PSUBSCRIBE channel1.? channel1.?*,此时通过channel1.1频道发出消息后,该客户端收到的消息是两条而不是一条,同样的,publish命令的返回值是2而不是1.