memcache+tp3.2实现消息队列
memcache模型
<?php
namespace Stat\Model;
/*
* memcache队列类
* 支持多进程并发写入、读取
* 边写边读,AB面轮值替换
* @author lkk/blog.lianq.net
* @version 0.2
* @create on 9:25 2012-9-28
*
* @edited on 14:03 2013-4-28
* 修改说明:
* 1.修改了changeHead方法,当get(1)只取一条数据时$head_key的值没改变的问题
* 2.修改了clear方法,当队列较小时按最大队列长度删除的问题
*
* 使用方法:
* $obj = new memcacheQueue('duilie');
* $obj->add('1asdf');
* $obj->getQueueLength();
* $obj->read(11);
* $obj->get(8);
*/ class MequeModel{
public static $client; //memcache客户端连接
public $access; //队列是否可更新
private $currentSide; //当前轮值的队列面:A/B
private $lastSide; //上一轮值的队列面:A/B
private $sideAHead; //A面队首值
private $sideATail; //A面队尾值
private $sideBHead; //B面队首值
private $sideBTail; //B面队尾值
private $currentHead; //当前队首值
private $currentTail; //当前队尾值
private $lastHead; //上轮队首值
private $lastTail; //上轮队尾值
private $expire; //过期时间,秒,1~2592000,即30天内
private $sleepTime; //等待解锁时间,微秒
private $queueName; //队列名称,唯一值
private $retryNum; //重试次数,= 10 * 理论并发数 const MAXNUM = 50000; //(单面)最大队列数,建议上限10K
const HEAD_KEY = '_lkkQueueHead_'; //队列首kye
const TAIL_KEY = '_lkkQueueTail_'; //队列尾key
const VALU_KEY = '_lkkQueueValu_'; //队列值key
const LOCK_KEY = '_lkkQueueLock_'; //队列锁key
const SIDE_KEY = '_lkkQueueSide_'; //轮值面key /*
* 构造函数
* @param [queueName] string 队列名称
* @param [expire] string 过期时间
* @param [config] array memcache服务器参数
* @return NULL
*/
public function __construct($queueName ='',$expire='',$config =''){
if(empty($config)){
self::$client = memcache_pconnect('tcp://m-2ze60467a1719914.memcache.rds.aliyuncs.com',11211);
}elseif(is_array($config)){//array('host'=>'127.0.0.1','port'=>'11211')
self::$client = memcache_pconnect($config['host'],$config['port']);
}elseif(is_string($config)){//"127.0.0.1:11211"
$tmp = explode(':',$config);
$conf['host'] = isset($tmp[0]) ? $tmp[0] : 'tcp://m-2ze60467a1719914.memcache.rds.aliyuncs.com';
$conf['port'] = isset($tmp[1]) ? $tmp[1] : '11211';
self::$client = memcache_pconnect($conf['host'],$conf['port']);
}
if(!self::$client) return false; ignore_user_abort(TRUE);//当客户断开连接,允许继续执行
set_time_limit(0);//取消脚本执行延时上限 $this->access = false;
$this->sleepTime = 1000;
$expire = (empty($expire)) ? 3600 : (int)$expire+1;
$this->expire = $expire;
$this->queueName = $queueName;
$this->retryNum = 20000; $side = memcache_add(self::$client, $queueName . self::SIDE_KEY, 'A',false, $expire);
$this->getHeadNTail($queueName);
if(!isset($this->sideAHead) || empty($this->sideAHead)) $this->sideAHead = 0;
if(!isset($this->sideATail) || empty($this->sideATail)) $this->sideATail = 0;
if(!isset($this->sideBHead) || empty($this->sideBHead)) $this->sideBHead = ;
if(!isset($this->sideBTail) || empty($this->sideBTail)) $this->sideBTail = ;
} /*
* 获取队列首尾值
* @param [queueName] string 队列名称
* @return NULL
*/
private function getHeadNTail($queueName){
$this->sideAHead = (int)memcache_get(self::$client, $queueName.'A'. self::HEAD_KEY);
$this->sideATail = (int)memcache_get(self::$client, $queueName.'A'. self::TAIL_KEY);
$this->sideBHead = (int)memcache_get(self::$client, $queueName.'B'. self::HEAD_KEY);
$this->sideBTail = (int)memcache_get(self::$client, $queueName.'B'. self::TAIL_KEY);
} /*
* 获取当前轮值的队列面
* @return string 队列面名称
*/
public function getCurrentSide(){
$currentSide = memcache_get(self::$client, $this->queueName . self::SIDE_KEY);
if($currentSide == 'A'){
$this->currentSide = 'A';
$this->lastSide = 'B'; $this->currentHead = $this->sideAHead;
$this->currentTail = $this->sideATail;
$this->lastHead = $this->sideBHead;
$this->lastTail = $this->sideBTail;
}else{
$this->currentSide = 'B';
$this->lastSide = 'A'; $this->currentHead = $this->sideBHead;
$this->currentTail = $this->sideBTail;
$this->lastHead = $this->sideAHead;
$this->lastTail = $this->sideATail;
} return $this->currentSide;
} /*
* 队列加锁
* @return boolean
*/
private function getLock(){
if($this->access === false){
while(!memcache_add(self::$client, $this->queueName .self::LOCK_KEY, , false, $this->expire) ){
usleep($this->sleepTime);
@$i++;
if($i > $this->retryNum){//尝试等待N次
return false;
break;
}
}
return $this->access = true;
}
return false;
} /*
* 队列解锁
* @return NULL
*/
private function unLock(){
memcache_delete(self::$client, $this->queueName .self::LOCK_KEY);
$this->access = false;
} /*
* 添加数据
* @param [data] 要存储的值
* @return boolean
*/
public function add($data=''){
$result = false;
if(empty($data)) return $result;
if(!$this->getLock()){
return $result;
}
$this->getHeadNTail($this->queueName);
$this->getCurrentSide(); if($this->isFull()){
$this->unLock();
return false;
} if($this->currentTail < self::MAXNUM){
$value_key = $this->queueName .$this->currentSide . self::VALU_KEY . $this->currentTail;
if(memcache_set(self::$client, $value_key, $data, false, $this->expire)){
$this->changeTail();
$result = true;
}
}else{//当前队列已满,更换轮值面
$this->unLock();
$this->changeCurrentSide();
return $this->add($data);
} $this->unLock();
return $result;
} /*
* 取出数据
* @param [length] int 数据的长度
* @return array
*/
public function get($length=){
if(!is_numeric($length)) return false;
if(empty($length)) $length = self::MAXNUM * ;//默认读取所有
if(!$this->getLock()) return false; if($this->isEmpty()){
$this->unLock();
return false;
} $keyArray = $this->getKeyArray($length);
$lastKey = $keyArray['lastKey'];
$currentKey = $keyArray['currentKey'];
$keys = $keyArray['keys'];
$this->changeHead($this->lastSide,$lastKey);
$this->changeHead($this->currentSide,$currentKey); $data = @memcache_get(self::$client, $keys);
if(empty($data)) $data = array();
foreach($keys as $v){//取出之后删除
@memcache_delete(self::$client, $v, );
}
$this->unLock(); return $data;
} /*
* 读取数据
* @param [length] int 数据的长度
* @return array
*/
public function read($length=){
if(!is_numeric($length)) return false;
if(empty($length)) $length = self::MAXNUM * ;//默认读取所有
$keyArray = $this->getKeyArray($length);
$data = @memcache_get(self::$client, $keyArray['keys']);
if(empty($data)) $data = array();
return $data;
} /*
* 获取队列某段长度的key数组
* @param [length] int 队列长度
* @return array
*/
private function getKeyArray($length){
$result = array('keys'=>array(),'lastKey'=>null,'currentKey'=>null);
$this->getHeadNTail($this->queueName);
$this->getCurrentSide();
if(empty($length)) return $result; //先取上一面的key
$i = $result['lastKey'] = ;
for($i=;$i<$length;$i++){
$result['lastKey'] = $this->lastHead + $i;
if($result['lastKey'] >= $this->lastTail) break;
$result['keys'][] = $this->queueName .$this->lastSide . self::VALU_KEY . $result['lastKey'];
} //再取当前面的key
$j = $length - $i;
$k = $result['currentKey'] = ;
for($k=;$k<$j;$k++){
$result['currentKey'] = $this->currentHead + $k;
if($result['currentKey'] >= $this->currentTail) break;
$result['keys'][] = $this->queueName .$this->currentSide . self::VALU_KEY . $result['currentKey'];
} return $result;
} /*
* 更新当前轮值面队列尾的值
* @return NULL
*/
private function changeTail(){
$tail_key = $this->queueName .$this->currentSide . self::TAIL_KEY;
memcache_add(self::$client, $tail_key, ,false, $this->expire);//如果没有,则插入;有则false;
$v = memcache_get(self::$client, $tail_key) +;
memcache_set(self::$client, $tail_key,$v,false,$this->expire);
} /*
* 更新队列首的值
* @param [side] string 要更新的面
* @param [headValue] int 队列首的值
* @return NULL
*/
private function changeHead($side,$headValue){
$head_key = $this->queueName .$side . self::HEAD_KEY;
$tail_key = $this->queueName .$side . self::TAIL_KEY;
$sideTail = memcache_get(self::$client, $tail_key);
if($headValue < $sideTail){
memcache_set(self::$client, $head_key,$headValue+,false,$this->expire);
}elseif($headValue >= $sideTail){
$this->resetSide($side);
}
} /*
* 重置队列面,即将该队列面的队首、队尾值置为
* @param [side] string 要重置的面
* @return NULL
*/
private function resetSide($side){
$head_key = $this->queueName .$side . self::HEAD_KEY;
$tail_key = $this->queueName .$side . self::TAIL_KEY;
memcache_set(self::$client, $head_key,,false,$this->expire);
memcache_set(self::$client, $tail_key,,false,$this->expire);
} /*
* 改变当前轮值队列面
* @return string
*/
private function changeCurrentSide(){
$currentSide = memcache_get(self::$client, $this->queueName . self::SIDE_KEY);
if($currentSide == 'A'){
memcache_set(self::$client, $this->queueName . self::SIDE_KEY,'B',false,$this->expire);
$this->currentSide = 'B';
}else{
memcache_set(self::$client, $this->queueName . self::SIDE_KEY,'A',false,$this->expire);
$this->currentSide = 'A';
}
return $this->currentSide;
} /*
* 检查当前队列是否已满
* @return boolean
*/
public function isFull(){
$result = false;
if($this->sideATail == self::MAXNUM && $this->sideBTail == self::MAXNUM){
$result = true;
}
return $result;
} /*
* 检查当前队列是否为空
* @return boolean
*/
public function isEmpty(){
$result = true;
if($this->sideATail > 0 || $this->sideBTail > ){
$result = false;
}
return $result;
} /*
* 获取当前队列的长度
* 该长度为理论长度,某些元素由于过期失效而丢失,真实长度小于或等于该长度
* @return int
*/
public function getQueueLength(){
$this->getHeadNTail($this->queueName); $sideALength = $this->sideATail - $this->sideAHead;
$sideBLength = $this->sideBTail - $this->sideBHead;
$result = $sideALength + $sideBLength; return $result;
} /*
* 清空当前队列数据,仅保留HEAD_KEY、TAIL_KEY、SIDE_KEY三个key
* @return boolean
*/
public function clear(){
if(!$this->getLock()) return false;
$this->getHeadNTail($this->queueName);
$AHead = $this->sideAHead;$AHead--;
$ATail = $this->sideATail;$ATail++;
$BHead = $this->sideBHead;$BHead--;
$BTail = $this->sideBTail;$BTail++; //删除A面
for($i=$AHead;$i<$ATail;$i++){
@memcache_delete(self::$client, $this->queueName.'A'. self::VALU_KEY .$i, );
} //删除B面
for($j=$BHead;$j<$BTail;$j++){
@memcache_delete(self::$client, $this->queueName.'A'. self::VALU_KEY .$j, );
} $this->unLock();
$this->resetSide('A');
$this->resetSide('B');
return true;
} /*
* 清除所有memcache缓存数据
* @return NULL
*/
public function memFlush(){
memcache_flush(self::$client);
} }
memcache+tp3.2实现消息队列的更多相关文章
- PHP memcache实现消息队列实例
现在,memcache于server缓存广泛应用.下面我来介绍一下memcache消息队列中等待的样本实现,有需要了解的朋友可以参考. memche消息队列原则key上做文章.后消息或者日志. 然后通 ...
- PHP下用Memcache 实现消息队列
Memcache 一般用于缓存服务.但是很多时候,比如一个消息广播系统,需要一个消息队列.直接从数据库取消息,负载往往不行.如果将整个消息队列用一个key缓存到memcache里面, 对于一个很大的消 ...
- 【转】持久化消息队列之MEMCACHEQ
G MEMCACHEQ AS MESSAGE QUEUE PHP,消息队列,MEMCACHEQ 使用消息队列(MESSAGE QUEUE)可以把某些耗时的工作推后,然后在后台慢慢地去执行,这样就不会让 ...
- 持久化消息队列memcacheq的安装配置
MemcacheQ 是一个基于 MemcacheDB 的消息队列服务器. 一.memcacheq介绍 特性: 1.简单易用 2.处理速度快 3.多条队列 4.并发性能好 5.与memcache的协议兼 ...
- php中对共享内存,消息队列的操作
http://www.cnblogs.com/fengwei/archive/2012/09/12/2682646.html php作为脚本程序,通常生命周期都很短,如在web应用中,一次请求就是ph ...
- phpMemcache消息队列类
<?php /** * Memcache 消息队列类 */ class QMC { const PREFIX = 'ASDFASDFFWQKE'; /** * 初始化 mc * @staticv ...
- php消息队列
Memcache 一般用于缓存服务.但是很多时候,比如一个消息广播系统,需要一个消息队列.直接从数据库取消息,负载往往不行.如果将整个消息队列用一个key缓存到memcache里面.对于一个很大的消息 ...
- PHP结合memcacheq消息队列解决并发问题
在处理业务逻辑时有可能遇到高并发问题,例如商城秒杀.微博评论等.如果不做任何措施可能在高瞬间造成服务器瘫痪,如何解决这个问题呢?队列是个不错的选择.队列(Queue)又称先进先出(First In F ...
- PHP消息队列实现及应用
目前对消息队列并不了解其原理,本篇文章主要是通过慕课网学习归纳的一些笔记,为后续学习打下基础. 众所周知在对网站设计的时候,会遇到给用户“群发短信”,“订单系统有大量的日志”,“秒杀设计”等,服务器没 ...
随机推荐
- Virtual Box虚拟机Ubuntu18.X系统安装及Mysql基本开发配置
Linux简介 什么是 Linux? Linux:世界上不仅只有一个 Windows 操作系统,还有 Linux.mac.Unix 等操作系统.桌面操作系统下 Windows 是霸主,而 Linux ...
- RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.3版本全新发布
1.RDIFramework.NET框架介绍 RDIFramework.NET,基于.NET的快速信息化系统开发.整合框架,为企业或个人快速开发系统提供了强大的支持,开发人员不需要开发系统的基础功能和 ...
- 用tornado实现图片标记
背景介绍 在文章Keras入门(四)之利用CNN模型轻松破解网站验证码中,其中的验证码图片标记是采用tornado实现的网页实现的.本文将会讲述如何利用tornado来实现图片标记. 我们的示 ...
- Docker 容器镜像删除
1.停止所有的container,这样才能够删除其中的images: docker stop $(docker ps -a -q) 如果想要删除所有container的话再加一个指令: docker ...
- headfirst设计模式(6)—单例模式
前言 这一章的课题看起来就很和蔼可亲了,比起前面绕的我不要不要的工厂模式,那感觉真是太好了,但是正是因为简单,那么问题就来了,我怎么才能把这个东西叙述清楚?怎么样才能老少咸宜呢? 如何能够在把这个东西 ...
- spring boot拦截器中获取request post请求中的参数
最近有一个需要从拦截器中获取post请求的参数的需求,这里记录一下处理过程中出现的问题. 首先想到的就是request.getParameter(String )方法,但是这个方法只能在get请求中取 ...
- Python全栈开发之---迭代器、可迭代对象、生成器
1.什么叫迭代 现在,我们已经获得了一个新线索,有一个叫做“可迭代的”概念. 首先,我们从报错来分析,好像之所以1234不可以for循环,是因为它不可迭代.那么如果“可迭代”,就应该可以被for循环了 ...
- 个人博客制作如何选择前端模板 thinkcmf后台加载新模板 CSS js文件
我们的博客后台已经搭建好了,接下来我就要选择一个合适的模板做自己的博客,首先要定位你的博客是做什么用的,是属于什么行业,根据自己博客的定位选择适合的模板. 如果你是设计师,又会前端设计开发,那就可以自 ...
- JMeter写入文件
之前我们推文讨论过如何使用jmeter读取文件, 比如csv, txt文件读取, 只要配置csv数据文件, 即可非常容易的从文件中读取想要的数据, 但是如果数据已经从API或者DB中获取, 想存放到 ...
- Git 密钥对处理
生成密钥对: ssh-keygen -t rsa cd .ssh ls id_rsa 私钥 id_rsa.pub 公钥