1. 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);
    */
  2.  
  3. 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 * 理论并发数
  4.  
  5. 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
  6.  
  7. /*
    * 构造函数
    * @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;
  8.  
  9. ignore_user_abort(TRUE);//当客户断开连接,允许继续执行
    set_time_limit(0);//取消脚本执行延时上限
  10.  
  11. $this->access = false;
    $this->sleepTime = 1000;
    $expire = (empty($expire)) ? 3600 : (int)$expire+1;
    $this->expire = $expire;
    $this->queueName = $queueName;
    $this->retryNum = 20000;
  12.  
  13. $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 = ;
    }
  14.  
  15. /*
    * 获取队列首尾值
    * @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);
    }
  16.  
  17. /*
    * 获取当前轮值的队列面
    * @return string 队列面名称
    */
    public function getCurrentSide(){
    $currentSide = memcache_get(self::$client, $this->queueName . self::SIDE_KEY);
    if($currentSide == 'A'){
    $this->currentSide = 'A';
    $this->lastSide = 'B';
  18.  
  19. $this->currentHead = $this->sideAHead;
    $this->currentTail = $this->sideATail;
    $this->lastHead = $this->sideBHead;
    $this->lastTail = $this->sideBTail;
    }else{
    $this->currentSide = 'B';
    $this->lastSide = 'A';
  20.  
  21. $this->currentHead = $this->sideBHead;
    $this->currentTail = $this->sideBTail;
    $this->lastHead = $this->sideAHead;
    $this->lastTail = $this->sideATail;
    }
  22.  
  23. return $this->currentSide;
    }
  24.  
  25. /*
    * 队列加锁
    * @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;
    }
  26.  
  27. /*
    * 队列解锁
    * @return NULL
    */
    private function unLock(){
    memcache_delete(self::$client, $this->queueName .self::LOCK_KEY);
    $this->access = false;
    }
  28.  
  29. /*
    * 添加数据
    * @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();
  30.  
  31. if($this->isFull()){
    $this->unLock();
    return false;
    }
  32.  
  33. 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);
    }
  34.  
  35. $this->unLock();
    return $result;
    }
  36.  
  37. /*
    * 取出数据
    * @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;
  38.  
  39. if($this->isEmpty()){
    $this->unLock();
    return false;
    }
  40.  
  41. $keyArray = $this->getKeyArray($length);
    $lastKey = $keyArray['lastKey'];
    $currentKey = $keyArray['currentKey'];
    $keys = $keyArray['keys'];
    $this->changeHead($this->lastSide,$lastKey);
    $this->changeHead($this->currentSide,$currentKey);
  42.  
  43. $data = @memcache_get(self::$client, $keys);
    if(empty($data)) $data = array();
    foreach($keys as $v){//取出之后删除
    @memcache_delete(self::$client, $v, );
    }
    $this->unLock();
  44.  
  45. return $data;
    }
  46.  
  47. /*
    * 读取数据
    * @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;
    }
  48.  
  49. /*
    * 获取队列某段长度的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;
  50.  
  51. //先取上一面的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'];
    }
  52.  
  53. //再取当前面的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'];
    }
  54.  
  55. return $result;
    }
  56.  
  57. /*
    * 更新当前轮值面队列尾的值
    * @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);
    }
  58.  
  59. /*
    * 更新队列首的值
    * @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);
    }
    }
  60.  
  61. /*
    * 重置队列面,即将该队列面的队首、队尾值置为
    * @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);
    }
  62.  
  63. /*
    * 改变当前轮值队列面
    * @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;
    }
  64.  
  65. /*
    * 检查当前队列是否已满
    * @return boolean
    */
    public function isFull(){
    $result = false;
    if($this->sideATail == self::MAXNUM && $this->sideBTail == self::MAXNUM){
    $result = true;
    }
    return $result;
    }
  66.  
  67. /*
    * 检查当前队列是否为空
    * @return boolean
    */
    public function isEmpty(){
    $result = true;
    if($this->sideATail > 0 || $this->sideBTail > ){
    $result = false;
    }
    return $result;
    }
  68.  
  69. /*
    * 获取当前队列的长度
    * 该长度为理论长度,某些元素由于过期失效而丢失,真实长度小于或等于该长度
    * @return int
    */
    public function getQueueLength(){
    $this->getHeadNTail($this->queueName);
  70.  
  71. $sideALength = $this->sideATail - $this->sideAHead;
    $sideBLength = $this->sideBTail - $this->sideBHead;
    $result = $sideALength + $sideBLength;
  72.  
  73. return $result;
    }
  74.  
  75. /*
    * 清空当前队列数据,仅保留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++;
  76.  
  77. //删除A面
    for($i=$AHead;$i<$ATail;$i++){
    @memcache_delete(self::$client, $this->queueName.'A'. self::VALU_KEY .$i, );
    }
  78.  
  79. //删除B面
    for($j=$BHead;$j<$BTail;$j++){
    @memcache_delete(self::$client, $this->queueName.'A'. self::VALU_KEY .$j, );
    }
  80.  
  81. $this->unLock();
    $this->resetSide('A');
    $this->resetSide('B');
    return true;
    }
  82.  
  83. /*
    * 清除所有memcache缓存数据
    * @return NULL
    */
    public function memFlush(){
    memcache_flush(self::$client);
    }
  84.  
  85. }

memcache+tp3.2实现消息队列的更多相关文章

  1. PHP memcache实现消息队列实例

    现在,memcache于server缓存广泛应用.下面我来介绍一下memcache消息队列中等待的样本实现,有需要了解的朋友可以参考. memche消息队列原则key上做文章.后消息或者日志. 然后通 ...

  2. PHP下用Memcache 实现消息队列

    Memcache 一般用于缓存服务.但是很多时候,比如一个消息广播系统,需要一个消息队列.直接从数据库取消息,负载往往不行.如果将整个消息队列用一个key缓存到memcache里面, 对于一个很大的消 ...

  3. 【转】持久化消息队列之MEMCACHEQ

    G MEMCACHEQ AS MESSAGE QUEUE PHP,消息队列,MEMCACHEQ 使用消息队列(MESSAGE QUEUE)可以把某些耗时的工作推后,然后在后台慢慢地去执行,这样就不会让 ...

  4. 持久化消息队列memcacheq的安装配置

    MemcacheQ 是一个基于 MemcacheDB 的消息队列服务器. 一.memcacheq介绍 特性: 1.简单易用 2.处理速度快 3.多条队列 4.并发性能好 5.与memcache的协议兼 ...

  5. php中对共享内存,消息队列的操作

    http://www.cnblogs.com/fengwei/archive/2012/09/12/2682646.html php作为脚本程序,通常生命周期都很短,如在web应用中,一次请求就是ph ...

  6. phpMemcache消息队列类

    <?php /** * Memcache 消息队列类 */ class QMC { const PREFIX = 'ASDFASDFFWQKE'; /** * 初始化 mc * @staticv ...

  7. php消息队列

    Memcache 一般用于缓存服务.但是很多时候,比如一个消息广播系统,需要一个消息队列.直接从数据库取消息,负载往往不行.如果将整个消息队列用一个key缓存到memcache里面.对于一个很大的消息 ...

  8. PHP结合memcacheq消息队列解决并发问题

    在处理业务逻辑时有可能遇到高并发问题,例如商城秒杀.微博评论等.如果不做任何措施可能在高瞬间造成服务器瘫痪,如何解决这个问题呢?队列是个不错的选择.队列(Queue)又称先进先出(First In F ...

  9. PHP消息队列实现及应用

    目前对消息队列并不了解其原理,本篇文章主要是通过慕课网学习归纳的一些笔记,为后续学习打下基础. 众所周知在对网站设计的时候,会遇到给用户“群发短信”,“订单系统有大量的日志”,“秒杀设计”等,服务器没 ...

随机推荐

  1. 遍历 Map 的四种方法

    public static void main(String[] args) { Map<String, String> map = new HashMap<String, Stri ...

  2. python类属性用法总结

    属性的定义:python中的属性其实是普通方法的衍生. 操作类属性有三种方法: 1.使用@property装饰器操作类属性. 2.使用类或实例直接操作类属性(例如:obj.name,obj.age=1 ...

  3. linux 指令备忘

    linux 指令备忘 1.ls [选项] [目录名 | 列出相关目录下的所有目录和文件 -a 列出包括.a开头的隐藏文件的所有文件 -A 通-a,但不列出"."和"..& ...

  4. ConcurrentHashMap1.8源码分析

    文章简介 想必大家对HashMap数据结构并不陌生,JDK1.7采用的是数组+链表的方式,JDK1.8采用的是数组+链表+红黑树的方式.虽然JDK1.8对于HashMap有了很大的改进,提高了存取效率 ...

  5. 解析高德地图api获取省市区,生成最新三级联动sql表

    前言: 最近项目中用到了全国省市区三级信息,但是网上找到的信息都是比较旧的信息.与现在最新的地区信息匹配不上.后来想到高德地图上可能有这些信息.所以解析了一下api接口,生成了相关省市区的sql信息. ...

  6. 基础概念PHP-FPM、FastCGI和CGI

    在搭建 LAMP/LNMP 服务器时,会经常遇到 PHP-FPM.FastCGI和CGI 这几个概念.如果对它们一知半解,很难搭建出高性能的服务器.接下来我们就以图形方式,解释这些概念之间的关系. 基 ...

  7. 设计模式系列13:模板方法模式(Template Method Pattern)

    定义 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤.    --<设计模式GoF> UML类图 使用场景 有 ...

  8. 03 入门 - 安装MVC 5和创建应用程序

    目录索引:<ASP.NET MVC 5 高级编程>学习笔记 本篇内容: 1. ASP.NET MVC 5的软件需求 2. 安装ASP.NET MVC 5 1)安装MVC 5开发组件 2)服 ...

  9. Skyline Terra Explorer6.6弹出窗口实现复制功能

    前段时间继续下来的基于Skyline的B/S项目,是基于Terra Explorer6.6实现的.项目中涉及到基于三维模型查询设备编码等操作,从用户友好角度来讲,查询到的设备编码应该要支持复制,方便用 ...

  10. MyBatis学习---逆向工程 Mybatis Generator代码生成

    [目录]