【PHP】你使用过redis做异步队列么,是怎么用的?有什么缺点?

摘要:
Redis设计主要是用来做缓存的,但是由于它自身的某种特性使得它可以用来做消息队列。具体文章可查看:PHP与redis队列实现电商订单自动确认收货方式二:发布订阅者模式使用pub/sub主题订阅者模式,可以实现1:N的消息队列。redis做异步队列的缺点在消费者下线的情况下,生产的消息会丢失。方式二是通过修改redis的配置项,因此仅对redis连接生效,相对于方式1,不会产生意外的对其他方法的影响。

Redis设计主要是用来做缓存的,但是由于它自身的某种特性使得它可以用来做消息队列。

它有几个阻塞式的API可以使用,正是这些阻塞式的API让其有能力做消息队列;

另外,做消息队列的其他特性例如FIFO(先入先出)也很容易实现,只需要一个list对象从头取数据,从尾部塞数据即可;

Redis能做消息队列还得益于其list对象blpop brpop接口以及Pub/Sub(发布/订阅)的某些接口,它们都是阻塞版的,所以可以用来做消息队列。(List : lpush / rpop)

方式一:生产者与消费者模式

使用list结构作为队列,rpush生产消息,lpop消费消息,当lpop没有消息的时候,要适当sleep一会再重试。

或者,不用sleep,直接用blpop指令,在没有消息的时候,它会阻塞住直到消息到来。

具体文章可查看:PHP与redis队列实现电商订单自动确认收货

在这里插入图片描述

方式二:发布订阅者模式

使用pub/sub主题订阅者模式,可以实现1:N的消息队列。

基于事件的系统中,Pub/Sub是目前广泛使用的通信模型,它采用事件作为基本的通信机制,提供大规模系统所要求的松散耦合的交互模式:订阅者(如客户端)以事件订阅的方式表达出它有兴趣接收的一个事件或一类事件;发布者(如服务器)可将订阅者感兴趣的事件随时通知相关订阅者。

消息发布者,即publish客户端,无需独占链接,你可以在publish消息的同时,使用同一个redis-client链接进行其他操作(例如:INCR等)

消息订阅者,即subscribe客户端,需要独占链接,即进行subscribe期间,redis-client无法穿插其他操作,此时client以阻塞的方式等待“publish端”的消息;这一点很好理解,因此subscribe端需要使用单独的链接,甚至需要在额外的线程中使用。

redis做异步队列的缺点

在消费者下线的情况下,生产的消息会丢失。此场景,建议用MQ。

使用MQ可查看:在分布式系统上,你是如何处理数据的一致性的

下面用代码实际实现,用发布订阅者模式,给大家讲解讲解

消息订阅者subscribe.php

<?php
ini_set('default_socket_timeout', -1);  //php配置设置不超时
$redis = new Redis();
$redis->connect("127.0.0.1",6379);
//$redis->setOption(Redis::OPT_READ_TIMEOUT, -1);  //redis方式设置不超时,推荐

$redis->subscribe(['chan'],'callback');     //callback为回调函数名称
//$redis->subscribe(['chan'],array(new TestCall(),'callback') ); //如果回调函数是类中的方法名,这样写

// 回调函数,这里写处理逻辑
function callback($instance, $channelName, $message)
{
         echo $channelName, "==>", $message, PHP_EOL;

         //$instance,即为上面创建的redis实例对象,在回调函数中,默认的这个参数就是,因此不需专门传参。 这里除了SUBSCRIBE、PSUBSCRIBE、UNSUBSCRIBE、PUNSUBSCRIBE这4条命令之外其它命令都不能使用
         //如果要使用redis中的其他命令,这样实现
         $newredis = new Redis();
        $newredis->connect("127.0.0.1", 6379);
        echo $newredis->get('test') . PHP_EOL;
        $newredis->close();

          //可以根据$channelName, $message,处理不同的业务逻辑
          switch($chan) {
               case 'chan-1':
                  ...
                  break;

               case 'chan-2':
                              ...
                   break;
           }

           switch($message) {
               case 'msg1':
                  ...
                  break;

               case 'msg2':
                              ...
                   break;
           }

}

subscribe.php中设置不超时

方法1:ini_set('default_socket_timeout', -1);

方法2:$redis->setOption(Redis::OPT_READ_TIMEOUT, -1);

如果不设置不超时,60s后会报一个错误

 PHP Fatal error: Uncaught RedisException: read error on connection to 127.0.0.1:6379 in subscribe.php:6

方式一的实现,是通过临时修改ini的配置值,default_socket_timeout默认为60s,default_socket_timeout是socket流的超时参数,即socket流从建立到传输再到关闭整个过程必须要在这个参数设置的时间以内完成,如果不能完成,那么PHP将自动结束这个socket并返回一个警告。

在这里插入图片描述

方式二是通过修改redis的配置项,因此仅对redis连接生效,相对于方式1,不会产生意外的对其他方法的影响。

消息发布者publish.php

<?php

$redis = new Redis();
$redis->connect("127.0.0.1",6379);

$redis->publish('chan','this is a message');

批量订阅

redis的psubscribe支持通过模式匹配的方式实现批量订阅,订阅方式

$redis->psubscribe(['my'],'psubscribe'); //回调函数写函数名
或者
$redis->psubscribe(['my
'],array(new TestCall(),'psubscribe')); //回调函数为类中的方法,类名写你自己定义的类

subscribe.php

<?php
//ini_set('default_socket_timeout', -1);  //不超时
$redis = new Redis();
$redis->connect("127.0.0.1",6379);
$redis->setOption(Redis::OPT_READ_TIMEOUT, -1);

//匹配方式1:发布可用$redis->publish('mymest','this is a message');
//$redis->psubscribe(['my*'],'psubscribe');    

//匹配方式2:发布可用$redis->publish('mydest','this is a message');
//$redis->psubscribe(['my?est'],'psubscribe');

//匹配方式3:发布可用$redis->publish('myaest','this is a message');或$redis->publish('myeest','this is a message');
$redis->psubscribe(['my[ae]est'],'psubscribe');

function psubscribe($redis, $pattern, $chan, $msg) {
      echo "Pattern: $pattern
";
      echo "Channel: $chan
";
      echo "Payload: $msg
";
}

模式匹配规则

支持以下几种,以hello举例:

h?llo subscribes to hello, hallo and hxllo
h*llo subscribes to hllo and heeeello
h[ae]llo subscribes to hello and hallo, but not hillo
特殊字符用转义

pubsub方法介绍

public function pubsub( $keyword, $argument )

pubsub获取pub/sub系统的信息,$keyword可用为"channels", "numsub", 或者"numpat",三种,传入不同的keyword返回的数据不同

* $redis->pubsub('channels'); // All channels 获取所有的频道,返回数组
* $redis->pubsub('channels', '*pattern*'); // Just channels matching your pattern,返回符合匹配模式的频道
* $redis->pubsub('numsub', array('chan1', 'chan2')); // Get subscriber counts for 'chan1' and 'chan2'    //返回每个订阅频道的数量,返回数组
* $redis->pubsub('numpat'); // Get the number of pattern subscribers 获取模式匹配方式的订阅的数量,即$redis->psubscribe(['my[ae]est'],'psubscribe');返回数量为1,$redis->subscribe(['chan'],'callback');    这种方式获取不到,因此返回数量为0

点关注,不迷路

好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。之前说过,PHP方面的技术点很多,也是因为太多了,实在是写不过来,写过来了大家也不会看的太多,所以我这里把它整理成了PDF和文档,如果有需要的可以

点击进入暗号: PHP+「平台」

在这里插入图片描述

在这里插入图片描述


更多学习内容可以访问【对标大厂】精品PHP架构师教程目录大全,只要你能看完保证薪资上升一个台阶(持续更新)

以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的可以加入我的 PHP技术交流群

免责声明:文章转载自《【PHP】你使用过redis做异步队列么,是怎么用的?有什么缺点?》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇12个高矮不同的人,排成两排(catalan数)【ORACLE】调整序列的当前种子值下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

【php5权威编程-1】

 public/private/protected 访问修饰符,针对对象中的方法和属性。 <?php class MyClass { private $id = 18; public function getId() { return $this->id; } } $obj = new My...

redis环境搭建(Linux)、Jredis

简介 1. NoSql是以key-value形式存储,和传统的关系型数据库不一样,不一定遵循传统数据库的一些基本要求,比如说遵循SQL标准,ACID属性,表结构等等,这类数据库主要有一下特点:非关系型的,分布式的,开源的,水平可扩展的。2. NoSql的特点:a) 处理超大量的数据。b) 运行在便宜的pc服务器集群上c) 击碎了性能瓶颈。3. NoSql适...

用PHP编写Android应用程序 狼人:

Google的开源Android移动操作系统正在席卷全球智能手机市场,和苹果不一样,它对那些想将应用程序提交到iPhone App Store的开发人员有着严格的指导方针和要求,Google的Android平台非常开放,甚至还可以用PHP编写Android应用程序,Irontech创建了一个运行在Android上的PHP移植程序,结合Android的脚本层...

SpringBoot 整合EasyExcel 获取动态Excel列名

导读 最近负责消息网关,里面有个短信模板导入功能,因为不同模板编号对应不同参数,导入后的数据定时发送,涉及到Excel中列名不固定问题,于是想根据列名+值,组合成一个大JSON,具体代码如下。 引入依赖 <dependency> <groupId>com.alibaba</groupId...

消息中间件系列三:使用RabbitMq原生Java客户端进行消息通信(消费者(接收方)自动确认模式、消费者(接收方)自行确认模式、生产者(发送方)确认模式)

准备工作: 1)安装RabbitMQ,参考文章:消息中间件系列二:RabbitMQ入门(基本概念、RabbitMQ的安装和运行) 2.)分别新建名为OriginalRabbitMQProducer和OriginalRabbitMQConsumer的maven工程 在pom.xml文件里面引入如下依赖: <dependency>...

ActiveMQ消息的消费原理

消费端消费消息:   在 初识ActiveMQ 中我提到过,两种方法可以接收消息,一种是使用同步阻塞的ActiveMQMessageConsumer#receive方法。另一种是使用消息监听器MessageListener。这里需要注意的是,在同一个session下,这两者不能同时工作,也就是说不能针对不同消息采用不同的接收方式。否则会抛出异常。至于为什么...