应用场景
Redis Stream 是 Redis 5.0 版本新增加的数据结构。
Redis Stream 主要用于消息队列(MQ,Message Queue),Redis 本身是有一个 Redis 发布订阅 (pub/sub) 来实现消息队列的功能,但它有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃。
简单来说发布订阅 (pub/sub) 可以分发消息,但无法记录历史消息。
而 Redis Stream 提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。
详请可见:Redis Stream
此处只讲业务中xadd(作为消息生产者)搭配xreadgroup(消费组消费数据消息)的应用。
框架版本以Yii2.0为例,需保证redis版本为5.0以上。
生产者:
通常生产者由频繁请求的客户端异步发送消息,此处自己模拟第三方客户端作为生产者向自己发送消息。
public const LIST_RECORD_KEY = 'redisKey';
/**
* XADD 测试自生产发送消息
*/
public function producer()
{
//模仿异步接口调用,生产队列
$key = self::LIST_RECORD_KEY;
$redis = Yii::$app->redis;
$i = 0;
//发送100条消息
while ($i < 100) {
$data = 10000 + $i;
$redis->xadd(self::LIST_RECORD_KEY, '*', 'name', $data);
//redis command:XADD redisKey * name 10000
$i ++;
sleep(1);
}
}
消费者
消费组可设置多个,此处设置一个消费组为例,如果队列中有耗时任务,可将该任务交给另一个消费组处理。
/**
* XREADGROUP 消费组消费队列消息
*/
public function getCampaignRule()
{
$redis = Yii::$app->redis;
//消费组名称
$groupName = 'gid:test01';
//消费者
$consumerName = 'test02';
//获取当前队列长度
// $length = $redis->xlen(self::LIST_RECORD_KEY);
//接收消费前防止重名先销毁消费组,销毁后无法继续上一次的任务执行
// $destroy = $redis->xgroup('DESTROY', self::LIST_RECORD_KEY, $groupName);
//创建消费组后不销毁一直存在,断线重连后重启服务会报错,但会接着之前的消息处理,抛出异常不用管
try {
//创建消费组,头部开始消费
$group = $redis->xgroup('create', self::LIST_RECORD_KEY, $groupName, '0');
//创建消费组,尾部开始消费
//$group = $redis->xgroup('create', self::LIST_RECORD_KEY, $groupName, '$');
} catch (\Throwable $e) {
echo 'create group failed:' . $e->getMessage();
saveLog(['data' => ['create group failed'], 'error' => $e->getMessage()], "automatic_xread", true);
}
//获取stream信息
// $xinfo = $redis->xinfo('stream', self::LIST_RECORD_KEY);
while (1) {
echo 'pending...' . PHP_EOL;
//最后的'>'参数不可省略,继续执行后面的任务
$read = $redis->xreadgroup('GROUP', $groupName, $consumerName, 'COUNT', 10, 'STREAMS', self::LIST_RECORD_KEY, '>');
print_r($read);
$info = $read[0][1] ?? [];
if (empty($info)) {
continue;
}
$msgCount = count($info);
for ($a = 0; $a < $msgCount; $a ++) {
//每条消息的id
$msgId = $info[$a][0] ?? 0;
//处理业务逻辑
#coding...
//将处理过的消息挂起
$xack = $redis->xack(self::LIST_RECORD_KEY, $groupName, $msgId);
echo PHP_EOL;
}
sleep(1);
}
}
脚本运行生产者:
php yii automatically/producer
脚本运行消费组:
yii automatically/get-campaign-rule