免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 10341 | 回复: 4
打印 上一主题 下一主题

PHP实现的memcache环形队列类实例 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-07-30 10:33 |只看该作者 |倒序浏览
本文实例讲述了PHP实现的memcache环形队列类。分享给大家供大家参考

代码
  1. <?php
  2. /**
  3. * PHP memcache 环形队列类
  4. * author LKK/lianq.net
  5. * edit http://www.lai18.com
  6. */
  7. class MQueue
  8. {
  9. public static $client;
  10. private $expire; //过期时间,秒,1~2592000,即30天内
  11. private $sleepTime; //等待解锁时间,微秒
  12. private $queueName; //队列名称,唯一值
  13. private $retryNum; //尝试次数
  14. private $MAXNUM; //最大队列容量
  15. private $canRewrite; //是否可以覆写开关,满出来的内容从头部开始覆盖重写原来的数据
  16. private $HEAD; //下一步要进入的指针位置
  17. private $TAIL; //下一步要进入的指针位置
  18. private $LEN; //队列现有长度
  19. const LOCK_KEY = '_Fox_MQ_LOCK_'; //锁存储标示
  20. const LENGTH_KEY = '_Fox_MQ_LENGTH_'; //队列现长度存储标示
  21. const VALU_KEY = '_Fox_MQ_VAL_'; //队列键值存储标示
  22. const HEAD_KEY = '_Fox_MQ_HEAD_'; //队列HEAD指针位置标示
  23. const TAIL_KEY = '_Fox_MQ_TAIL_'; //队列TAIL指针位置标示
  24. /*
  25.   * 构造函数
  26.   * 对于同一个$queueName,实例化时必须保障构造函数的参数值一致,否则pop和push会导队列顺序混乱
  27.   */
  28. public function __construct($queueName = '', $maxqueue = 1, $canRewrite = false, $expire = 0, $config = '')
  29. {
  30.   if (empty($config)) {
  31.    self::$client = memcache_pconnect('127.0.0.1', 11211);
  32.   } elseif (is_array($config)) { //array('host'=>'127.0.0.1','port'=>'11211')
  33.    self::$client = memcache_pconnect($config['host'], $config['port']);
  34.   } elseif (is_string($config)) { //"127.0.0.1:11211"
  35.    $tmp   = explode(':', $config);
  36.    $conf['host'] = isset($tmp[0]) ? $tmp[0] : '127.0.0.1';
  37.    $conf['port'] = isset($tmp[1]) ? $tmp[1] : '11211';
  38.    self::$client = memcache_pconnect($conf['host'], $conf['port']);
  39.   }
  40.   if (!self::$client)
  41.    return false;
  42.   ignore_user_abort(true); //当客户断开连接,允许继续执行
  43.   set_time_limit(0); //取消脚本执行延时上限
  44.   $this->access  = false;
  45.   $this->sleepTime = 1000;
  46.   $expire   = (empty($expire)) ? 0 : (int) $expire + 1;
  47.   $this->expire  = $expire;
  48.   $this->queueName = $queueName;
  49.   $this->retryNum = 20000;
  50.   $this->MAXNUM  = $maxqueue != null ? $maxqueue : 1;
  51.   $this->canRewrite = $canRewrite;
  52.   $this->getHeadAndTail();
  53.   if (!isset($this->HEAD) || empty($this->HEAD))
  54.    $this->HEAD = 0;
  55.   if (!isset($this->TAIL) || empty($this->TAIL))
  56.    $this->TAIL = 0;
  57.   if (!isset($this->LEN) || empty($this->LEN))
  58.    $this->LEN = 0;
  59. }
  60. //获取队列首尾指针信息和长度
  61. private function getHeadAndTail()
  62. {
  63.   $this->HEAD = (int) memcache_get(self::$client, $this->queueName . self::HEAD_KEY);
  64.   $this->TAIL = (int) memcache_get(self::$client, $this->queueName . self::TAIL_KEY);
  65.   $this->LEN = (int) memcache_get(self::$client, $this->queueName . self::LENGTH_KEY);
  66. }
  67. // 利用memcache_add原子性加锁
  68. private function lock()
  69. {
  70.   if ($this->access === false) {
  71.    $i = 0;
  72.    while (!memcache_add(self::$client, $this->queueName . self::LOCK_KEY, 1, false, $this->expire)) {
  73.     usleep($this->sleepTime);
  74.     @$i++;
  75.     if ($i > $this->retryNum) { //尝试等待N次
  76.      return false;
  77.      break;
  78.     }
  79.    }
  80.    return $this->access = true;
  81.   }
  82.   return false;
  83. }
  84. //更新头部指针指向,指向下一个位置
  85. private function incrHead()
  86. {
  87.   //$this->getHeadAndTail(); //获取最新指针信息 ,由于本方法体均在锁内调用,其锁内已调用了此方法,本行注释
  88.   $this->HEAD++; //头部指针下移
  89.   if ($this->HEAD >= $this->MAXNUM) {
  90.    $this->HEAD = 0; //边界值修正
  91.   }
  92.   ;
  93.   $this->LEN--; //Head的移动由Pop触发,所以相当于数量减少
  94.   if ($this->LEN < 0) {
  95.    $this->LEN = 0; //边界值修正
  96.   }
  97.   ;
  98.   memcache_set(self::$client, $this->queueName . self::HEAD_KEY, $this->HEAD, false, $this->expire); //更新
  99.   memcache_set(self::$client, $this->queueName . self::LENGTH_KEY, $this->LEN, false, $this->expire); //更新
  100. }
  101. //更新尾部指针指向,指向下一个位置
  102. private function incrTail()
  103. {
  104.   //$this->getHeadAndTail(); //获取最新指针信息,由于本方法体均在锁内调用,其锁内已调用了此方法,本行注释
  105.   $this->TAIL++; //尾部指针下移
  106.   if ($this->TAIL >= $this->MAXNUM) {
  107.    $this->TAIL = 0; //边界值修正
  108.   }
  109.   ;
  110.   $this->LEN++; //Head的移动由Push触发,所以相当于数量增加
  111.   if ($this->LEN >= $this->MAXNUM) {
  112.    $this->LEN = $this->MAXNUM; //边界值长度修正
  113.   }
  114.   ;
  115.   memcache_set(self::$client, $this->queueName . self::TAIL_KEY, $this->TAIL, false, $this->expire); //更新
  116.   memcache_set(self::$client, $this->queueName . self::LENGTH_KEY, $this->LEN, false, $this->expire); //更新
  117. }
  118. // 解锁
  119. private function unLock()
  120. {
  121.   memcache_delete(self::$client, $this->queueName . self::LOCK_KEY);
  122.   $this->access = false;
  123. }
  124. //判断是否满队列
  125. public function isFull()
  126. {
  127.   //外部直接调用的时候由于没有锁所以此处的值是个大概值,并不很准确,但是内部调用由于在前面有lock,所以可信
  128.   if ($this->canRewrite)
  129.    return false;
  130.   return $this->LEN == $this->MAXNUM ? true : false;
  131. }
  132. //判断是否为空
  133. public function isEmpty()
  134. {
  135.   //外部直接调用的时候由于没有锁所以此处的值是个大概值,并不很准确,但是内部调用由于在前面有lock,所以可信
  136.   return $this->LEN == 0 ? true : false;
  137. }
  138. public function getLen()
  139. {
  140.   //外部直接调用的时候由于没有锁所以此处的值是个大概值,并不很准确,但是内部调用由于在前面有lock,所以可信
  141.   return $this->LEN;
  142. }
  143. /*
  144.   * push值
  145.   * @param mixed 值
  146.   * @return bool
  147.   */
  148. public function push($data = '')
  149. {
  150.   $result = false;
  151.   if (empty($data))
  152.    return $result;
  153.   if (!$this->lock()) {
  154.    return $result;
  155.   }
  156.   $this->getHeadAndTail(); //获取最新指针信息
  157.   if ($this->isFull()) { //只有在非覆写下才有Full概念
  158.    $this->unLock();
  159.    return false;
  160.   }
  161.   if (memcache_set(self::$client, $this->queueName . self::VALU_KEY . $this->TAIL, $data, MEMCACHE_COMPRESSED, $this->expire)) {
  162.    //当推送后,发现尾部和头部重合(此时指针还未移动),且右边仍有未由Head读取的数据,那么移动Head指针,避免尾部指针跨越Head
  163.    if ($this->TAIL == $this->HEAD && $this->LEN >= 1) {
  164.     $this->incrHead();
  165.    }
  166.    $this->incrTail(); //移动尾部指针
  167.    $result = true;
  168.   }
  169.   $this->unLock();
  170.   return $result;
  171. }
  172. /*
  173.   * Pop一个值
  174.   * @param [length] int 队列长度
  175.   * @return array
  176.   */
  177. public function pop($length = 0)
  178. {
  179.   if (!is_numeric($length))
  180.    return false;
  181.   if (!$this->lock())
  182.    return false;
  183.   $this->getHeadAndTail();
  184.   if (empty($length))
  185.    $length = $this->LEN; //默认读取所有
  186.   if ($this->isEmpty()) {
  187.    $this->unLock();
  188.    return false;
  189.   }
  190.   //获取长度超出队列长度后进行修正
  191.   if ($length > $this->LEN)
  192.    $length = $this->LEN;
  193.   $data = $this->popKeyArray($length);
  194.   $this->unLock();
  195.   return $data;
  196. }
  197. /*
  198.   * pop某段长度的值
  199.   * @param [length] int 队列长度
  200.   * @return array
  201.   */
  202. private function popKeyArray($length)
  203. {
  204.   $result = array();
  205.   if (empty($length))
  206.    return $result;
  207.   for ($k = 0; $k < $length; $k++) {
  208.    $result[] = @memcache_get(self::$client, $this->queueName . self::VALU_KEY . $this->HEAD);
  209.    @memcache_delete(self::$client, $this->queueName . self::VALU_KEY . $this->HEAD, 0);
  210.    //当提取值后,发现头部和尾部重合(此时指针还未移动),且右边没有数据,即队列中最后一个数据被完全掏空,此时指针停留在本地不移动,队列长度变为0
  211.    if ($this->TAIL == $this->HEAD && $this->LEN <= 1) {
  212.     $this->LEN = 0;
  213.     memcache_set(self::$client, $this->queueName . self::LENGTH_KEY, $this->LEN, false, $this->expire); //更新
  214.     break;
  215.    } else {
  216.     $this->incrHead(); //首尾未重合,或者重合但是仍有未读取出的数据,均移动HEAD指针到下一处待读取位置
  217.    }
  218.   }
  219.   return $result;
  220. }
  221. /*
  222.   * 重置队列
  223.   * * @return NULL
  224.   */
  225. private function reset($all = false)
  226. {
  227.   if ($all) {
  228.    memcache_delete(self::$client, $this->queueName . self::HEAD_KEY, 0);
  229.    memcache_delete(self::$client, $this->queueName . self::TAIL_KEY, 0);
  230.    memcache_delete(self::$client, $this->queueName . self::LENGTH_KEY, 0);
  231.   } else {
  232.    $this->HEAD = $this->TAIL = $this->LEN = 0;
  233.    memcache_set(self::$client, $this->queueName . self::HEAD_KEY, 0, false, $this->expire);
  234.    memcache_set(self::$client, $this->queueName . self::TAIL_KEY, 0, false, $this->expire);
  235.    memcache_set(self::$client, $this->queueName . self::LENGTH_KEY, 0, false, $this->expire);
  236.   }
  237. }
  238. /*
  239.   * 清除所有memcache缓存数据
  240.   * @return NULL
  241.   */
  242. public function memFlush()
  243. {
  244.   memcache_flush(self::$client);
  245. }
  246. public function clear($all = false)
  247. {
  248.   if (!$this->lock())
  249.    return false;
  250.   $this->getHeadAndTail();
  251.   $Head = $this->HEAD;
  252.   $Length = $this->LEN;
  253.   $curr = 0;
  254.   for ($i = 0; $i < $Length; $i++) {
  255.    $curr = $this->$Head + $i;
  256.    if ($curr >= $this->MAXNUM) {
  257.     $this->HEAD = $curr = 0;
  258.    }
  259.    @memcache_delete(self::$client, $this->queueName . self::VALU_KEY . $curr, 0);
  260.   }
  261.   $this->unLock();
  262.   $this->reset($all);
  263.   return true;
  264. }
  265. }
复制代码

论坛徽章:
154
2022北京冬奥会纪念版徽章
日期:2015-08-07 17:10:5720周年集字徽章-年
日期:2022-10-26 16:44:2015-16赛季CBA联赛之深圳
日期:2022-11-02 14:02:4515-16赛季CBA联赛之八一
日期:2022-11-28 12:07:4820周年集字徽章-20	
日期:2023-07-19 08:49:4515-16赛季CBA联赛之八一
日期:2023-11-04 19:23:5115-16赛季CBA联赛之广夏
日期:2023-12-13 18:09:34
2 [报告]
发表于 2015-08-04 12:53 |只看该作者
果然是银才啊,其实企业需求用不到这些,做一个称职的代码搬运工就好了

评分

参与人数 1信誉积分 +10 收起 理由
substr函数 + 10 很给力!

查看全部评分

论坛徽章:
26
2015亚冠之胡齐斯坦钢铁
日期:2015-06-25 21:40:202015亚冠之柏斯波利斯
日期:2015-08-31 17:03:192015亚冠之柏斯波利斯
日期:2015-11-07 13:10:00程序设计版块每日发帖之星
日期:2015-11-10 06:20:00每日论坛发贴之星
日期:2015-11-10 06:20:00程序设计版块每日发帖之星
日期:2015-11-26 06:20:00程序设计版块每日发帖之星
日期:2015-12-02 06:20:00黄金圣斗士
日期:2015-12-07 17:57:4615-16赛季CBA联赛之天津
日期:2015-12-23 18:34:14程序设计版块每日发帖之星
日期:2016-01-02 06:20:00程序设计版块每日发帖之星
日期:2016-01-06 06:20:00每日论坛发贴之星
日期:2016-01-06 06:20:00
3 [报告]
发表于 2015-08-10 21:03 |只看该作者
顺序混乱啊

论坛徽章:
0
4 [报告]
发表于 2016-01-19 14:44 |只看该作者

论坛徽章:
0
5 [报告]
发表于 2016-02-24 07:52 |只看该作者
有的时候会用到,量大了 应该就会
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP