参考:PHP高级编程之消息队列

消息队列就是在消息的传输过程中,可以保存消息的容器。

常见用途:

  • 存储转发:异步处理耗时的任务
  • 分布式事务:多个消费者消费同一个消息队列
  • 应对高并发:通过消息队列保存任务,慢慢处理
  • 发布订阅:实现解耦

PHP 可以基于 Redis 的 List 数据类型实现简单的消息队列,可以参考 php-resque。当然也可以使用更强大的 RabbitMQ。

实现方式

PHP 守护进程

PHP 业务代码:

<?php

class MyDaemon
{
public $procNum = 8; // 进程总数 // 启动进程
public function run()
{
for ($i = 0; $i < $this->procNum; $i++) {
$nPID = \pcntl_fork();//创建子进程
if ($nPID == 0) {
//子进程
\Org\Util\MsgQ::init();
$this->work();
exit(0);
}
}
// 等待子进程执行完毕,避免僵尸进程
$n = 0;
while ($n < $this->procNum) {
$nStatus = -1;
$nPID = \pcntl_wait($nStatus);
if ($nPID > 0) {
++$n;
}
}
} //业务代码
public function work()
{
$MsgData = "";
while (true) {
usleep(10000); // 10 ms 执行一次
$ret = MsgQ::BlockSubsribe("MyMsgName", $MsgData);
// 业务代码
}
}

消息队列(基于Redis)库代码:

<?php

namespace Org\Util;

class MsgQ {
public static $errCode = 0;
public static $errMsg = "";
public static $redis;
private static $preFix = "MsgQ.";
private static $timeOut = 10; private static $redisHost = '';
private static $redisPort = '';
private static $redisAuth = ''; function __construct()
{
self::$redis = new \Redis();
$ret = self::$redis->connect($redisHost,$redisPort,self::$timeOut);
$ret = self::$redis->auth($redisAuth);
} function __destruct()
{
if(self::$redis) {
self::$redis->close();
}
} public static function init($timeOut = 0){
if (!self::$redis) {
self::$redis = new \Redis();
if(!empty($timeOut)){
self::$timeOut = $timeOut;
ini_set('default_socket_timeout', 259200);
$ret = self::$redis->connect($redisHost,$redisPort,self::$timeOut);
$ret = self::$redis->auth($redisAuth);
}
else{
self::$timeOut = 0;
ini_set('default_socket_timeout', 259200);
$ret = self::$redis->connect($redisHost,$redisPort,259200);
$ret = self::$redis->auth($redisAuth);
}
}
} public static function Publish($pubKey,$data){
if(!self::PingAndConnect()){
return false;
}
$ret = self::$redis->rPush(self::$preFix.$pubKey,$data);
if ($ret === false){
return false;
}
return true;
} public static function GetListLen($pubKey,&$len){
if(!self::PingAndConnect()){
return false;
}
$len = 0;
$ret = self::$redis->lLen(self::$preFix.$pubKey);
if ($ret === false){
return false;
}
$len = $ret;
return true;
} public static function Subsribe($pubKey,&$data){
if(!self::PingAndConnect()){
return false;
}
$ret = self::$redis->lPop(self::$preFix.$pubKey);
if ($ret === false){
return false;
}
$data = $ret;
return true;
} public static function BlockSubsribe($pubKey,&$data){
if(!self::PingAndConnect()){
return false;
}
try{
$ret = self::$redis->blPop(array(self::$preFix.$pubKey),0);
}
catch(Exception $e){
if(!self::PingAndConnect(true)){
return false;
}
return false;
}
if ($ret === false){
return false;
}
if ($ret === array()){
return false;
}
$data = $ret[1];
return true;
} private static function PingAndConnect($isException = false){
if (!self::$redis) {
self::$redis = new \Redis();
if (self::$timeOut == 0){
ini_set('default_socket_timeout', 259200);
$ret = self::$redis->connect($redisHost,$redisPort,259200);
}
else{
$ret = self::$redis->connect($redisHost,$redisPort,self::$timeOut);
}
if ($ret === false){
return false;
}
$ret = self::$redis->auth($redisAuth);
if ($ret === false){
return false;
}
}
else{
if (self::$timeOut == 0 && !$isException){
return true;
}
$ret = self::$redis->ping();
if ($ret === false){
if (self::$timeOut == 0){
ini_set('default_socket_timeout', 259200);
$ret = self::$redis->connect($redisHost,$redisPort,259200);
}
else{
$ret = self::$redis->connect($redisHost,$redisPort,self::$timeOut);
}
if ($ret === false){
return false;
}
$ret = self::$redis->auth($redisAuth);
if ($ret === false){
return false;
}
}
}
return true;
}
}

重启守护进程的 shell 脚本 restartprocess.sh

#!/bin/sh
if [ ! -n "$1" ]; then
echo "input proc name"
exit
else
procname=$1
fi pids=`(ps -ef | grep "$procname" | grep -v "grep" | grep -v $0) | awk '{print $2}'` for pid in ${pids[*]}
do
kill -9 $pid
done
cd /path/to/your/project/
setsid $procname &

启动守护进程的命令:

restartprocess.sh "php index.php /path/to/your/MyDaemon/func/run"

Linux 定时任务

可以设置一分钟或一秒钟执行一次 PHP 脚本。因为每次处理消息的时间不固定,可能导致消息积压或服务器负载过大。

手工执行脚本

用于处理偶然需求,简单。

用 Redis 实现 PHP 的简单消息队列的更多相关文章

  1. redis(五)---- 简单消息队列

    消息队列一个消息的链表,是一个异步处理的数据处理引擎.不仅能够提高系统的负荷,还能够改善因网络阻塞导致的数据缺失.一般用于邮件发送.手机短信发送,数据表单提交.图片生成.视频转换.日志储存等. red ...

  2. 用redis实现支持优先级的消息队列

    http://www.cnblogs.com/tianqiq/p/4309791.html http://www.cnblogs.com/it-cen/p/4312098.html http://ww ...

  3. simple简单消息队列

    一:介绍 1.优缺点 简单,但是耦合性较高. 这种模式是生产者与消费者一一对应,就是一个产生者,有一个消费者来消费. 如果,多个消费者想消费一个队列中的消息就不适合了.这种情况在后面会接着介绍. 2. ...

  4. Redis实现简单消息队列

    http://www.jianshu.com/p/9c04890615ba 任务异步化 打开浏览器,输入地址,按下回车,打开了页面.于是一个HTTP请求(request)就由客户端发送到服务器,服务器 ...

  5. Redis学习之实现优先级消息队列

    很久没有写博客了,最近简单的学习了一下Redis,其中学习了一下用Redis实现优先级消息队列.关于更多更为详细的可以在www.redis.cn找到相关资料. 对于熟悉Redis的童鞋提到队列很自然的 ...

  6. redis简单消息队列

    <?php $redis = new Redis(); $redis->connect('127.0.0.1',6379); $redis->flushall(); $redis-& ...

  7. redis(六)---- 简单延迟队列

    延迟队列的应用场景也很常见,例如:session的超时过期.自动取消未付款订单等等.redis中有一种数据结构叫做zset,即有序集合.元素类型为String类型,且元素具有唯一性不能重复,每个元素可 ...

  8. 使用redis原生list结构作为消息队列取代celery框架。

    1.web后台对大批量的繁重的io任务需要解耦使用分布式异步技术,否则会使接口阻塞,并发延迟,一般就选celery好了.此篇的取代主要是针对取代celery的worker模式.没有涉及到周期和定时模式 ...

  9. RabbitMq(2) 简单消息队列

    <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client </ar ...

随机推荐

  1. 理解 OutOfMemoryError 异常

    OutOfMemoryError 异常应该可以算得上是一个非常棘手的问题.JAVA 的程序员不用像苦逼的 C 语言程序员手动地管理内存,JVM 帮助他们分配内存,释放内存.但是当遇到内存相关的问题,就 ...

  2. 豆壳CMS本地安装教程

    DouPHP安装教程 一.下载DouPHP程序. 解压后得到三个文件夹 将upload文件夹里面的内容复制到wamp的www的文件夹. 二.打开浏览器,输入127.0.0.1. 勾选后选择下一步 注意 ...

  3. IDEA显示Run Dashboard窗口

    接下来在workspace.xml RunDashBoard节点中添加如下内容:<component name="RunDashboard"> 在代码中加入 <o ...

  4. c++ Socket客户端和服务端示例版本三(多线程版本)

    客户端 #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/soc ...

  5. egretios包接微信/facebook登录

    ios包有了,现在需要登录,就先使用微信登录吧,后继加一个facebook登录. 根据我的理解,先预测一下应该是怎么回事吧. 首先应该是要去微信的公共平台注册一下,说我的应用是什么应用,要填写企业信息 ...

  6. 互联网技术笔试总通不过?leetcode刷对了么

    https://36kr.com/p/5084645 Leetcode,绕都绕不过去的程序员刷题神器 编者按:本文来自逆行求职(ID:nixingjihua). 对所有求职技术岗位的童鞋来说,有这么一 ...

  7. 【转】linux 查看哪些进程用了swap

    转自:http://blog.csdn.net/xiangliangyu/article/details/8213127 如果系统的物理内存用光了,则会用到swap.系统就会跑得很慢,但仍能运行;如果 ...

  8. php内置函数分析之current()、next()、prev()、reset()、end()

    current()初始指向插入到数组中的第一个单元 next() 将数组的内部指针向前移动一位 prev() 将数组的内部指针倒回一位 reset() 将数组的内部指针指向第一个单元 end() 将数 ...

  9. xtrabackup备份mysql以及创建自动定时任务

    xtrabackup的安装 安装依赖关系 wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo y ...

  10. 【学习笔记】Minkowski和

    这还是个被我咕了N久的玩意 Minkowski和是一个奇怪的玩意 他长这样 $S={a+b \| a \in A , b \in B}$ AB可以是点集也可是向量集(显然) 他可以处理一些奇怪的东西 ...