Redis发布与订阅

210 阅读3分钟

概述

一般来说,消息队列的应用有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式。利用redis这两种场景的消息队列都能够实现。

定义:

生产者消费者模式(用redis的list结构实现):生产者生产消息放到队列里,多个消费者同时监听队列,谁先抢到消息谁就会从队列中取走消息;即对于每个消息只能被最多一个消费者拥有。

发布者订阅者模式(pub/sub):发布者生产消息放到队列里,多个监听队列的订阅者都会收到同一份消息;即正常情况下每个订阅者收到的消息应该都是一样的。

发布/订阅机制

发布订阅(pub/sub)是一种消息通信模式,主要的目的是解耦消息发布者和消息订阅者之间的耦合。发布订阅通常用来构建实时消息系统,比如即时聊天、实时广播、实时提醒等应用。当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher)。而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE 命令接收信息的时候,我们称这个客户端为订阅者(subscriber)。为了解耦发布者(publisher)和订阅者(subscriber)之间的关系,Redis 使用了 channel (频道)作为两者的中介 —— 发布者将信息直接发布给 channel ,而 channel 负责将信息发送给适当的订阅者,发布者和订阅者之间没有相互关系,也不知道对方的存在。订阅者可以订阅一个或若干个channel,当发布者向某个channel发布了消息,所有订阅了这个channel的订阅者都会收到该消息。

redis-pub> publish channel.1 "Hello World"
(integer) 0

使用publish命令来发布消息,publish命令的返回值表示接收到这条消息的订阅者数量。因为此时没有客户端订阅channel.1,所以返回0。发出去的消息不会被持久化,也就是说当有客户端订阅该频道后只能收到后续发布到该频道的消息,之前发送的就收不到了。

redis-sub> subscribe channel.1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel.1"
3) (integer) 1
redis-pub> publish channel.1 "Hello World"
(integer) 1
redis-sub> subscribe channel.1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel.1"
3) (integer) 1

1) "message"
2) "channel.1"
3) "Hello World"

进入订阅状态后客户端可能收到三种类型的回复。每种类型的回复都包含3个值,第一个值是消息的类型,根据消息类型的不同,第二、三个值的含义也不同。消息类型可能的取值有:

  • subscribe。表示订阅成功的反馈信息。第二个值是订阅成功的频道名称,第三个值是当前客户端订阅的频道数量。
  • message。这个类型的回复是我们最关心的,它表示接收到的消息。第二个值表示产生消息的频道名称,第三个值是消息的内容。
  • unsubscribe。表示成功取消订阅某个频道。第二个值是对应的频道名称,第三个值是当前客户端订阅的频道数量,当此值为0时客户端会退出订阅状态,之后就可以执行其他非"发布/订阅"模式的命令了。

除了可以使用subscribe订阅相关channel外,还可以使用psubscribe命令订阅指定的规则。规则支持glob风格通配符格式。

redis-sub> psubscribe channel*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "channel*"
3) (integer) 1

1) "pmessage"
2) "channel*"
3) "channel.1"
4) "Hello World"

1) "pmessage"
2) "channel*"
3) "channel.2"
4) "welcome"

消息发布者示例代码pub.php

$redis = new Redis();
$redis->connect("127.0.0.1", 6379);
$channel = $argv[1];
$msg = $argv[2];
$redis->publish($channel, $msg);

消息订阅者示例代码sub.php

$redis = new Redis();
$redis->connect("127.0.0.1", 6379);
$channel = $argv[1];
$redis->subscribe(array($channel), "callback");

function callback($instance, $channelName, $message) {
    echo $channelName."=>".$message."\n";
}