PHP memcache 环形队列
<?php
/**
* PHP memcache 环形队列类
* 因业务需要只保留的队列中的Pop和Push,修改过期时间为0即永久
*/
class
MQueue
{
public
static
$client
;
private
$expire
;
//过期时间,秒,1~2592000,即30天内
private
$sleepTime
;
//等待解锁时间,微秒
private
$queueName
;
//队列名称,唯一值
private
$retryNum
;
//尝试次数
private
$MAXNUM
;
//最大队列容量
private
$canRewrite
;
//是否可以覆写开关,满出来的内容从头部开始覆盖重写原来的数据
private
$HEAD
;
//下一步要进入的指针位置
private
$TAIL
;
//下一步要进入的指针位置
private
$LEN
;
//队列现有长度
const
LOCK_KEY =
'_Fox_MQ_LOCK_'
;
//锁存储标示
const
LENGTH_KEY =
'_Fox_MQ_LENGTH_'
;
//队列现长度存储标示
const
VALU_KEY =
'_Fox_MQ_VAL_'
;
//队列键值存储标示
const
HEAD_KEY =
'_Fox_MQ_HEAD_'
;
//队列HEAD指针位置标示
const
TAIL_KEY =
'_Fox_MQ_TAIL_'
;
//队列TAIL指针位置标示
/*
* 构造函数
* 对于同一个$queueName,实例化时必须保障构造函数的参数值一致,否则pop和push会导队列顺序混乱
*/
public
function
__construct(
$queueName
=
''
,
$maxqueue
= 1,
$canRewrite
= false,
$expire
= 0,
$config
=
''
)
{
if
(
empty
(
$config
)) {
self::
$client
= memcache_pconnect(
'127.0.0.1'
, 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] :
'127.0.0.1'
;
$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
)) ? 0 : (int)
$expire
+ 1;
$this
->expire =
$expire
;
$this
->queueName =
$queueName
;
$this
->retryNum = 20000;
$this
->MAXNUM =
$maxqueue
!= null ?
$maxqueue
: 1;
$this
->canRewrite =
$canRewrite
;
$this
->getHeadAndTail();
if
(!isset(
$this
->HEAD) ||
empty
(
$this
->HEAD))
$this
->HEAD = 0;
if
(!isset(
$this
->TAIL) ||
empty
(
$this
->TAIL))
$this
->TAIL = 0;
if
(!isset(
$this
->LEN) ||
empty
(
$this
->LEN))
$this
->LEN = 0;
}
//获取队列首尾指针信息和长度
private
function
getHeadAndTail()
{
$this
->HEAD = (int) memcache_get(self::
$client
,
$this
->queueName . self::HEAD_KEY);
$this
->TAIL = (int) memcache_get(self::
$client
,
$this
->queueName . self::TAIL_KEY);
$this
->LEN = (int) memcache_get(self::
$client
,
$this
->queueName . self::LENGTH_KEY);
}
// 利用memcache_add原子性加锁
private
function
lock()
{
if
(
$this
->access === false) {
$i
= 0;
while
(!memcache_add(self::
$client
,
$this
->queueName . self::LOCK_KEY, 1, false,
$this
->expire)) {
usleep(
$this
->sleepTime);
@
$i
++;
if
(
$i
>
$this
->retryNum) {
//尝试等待N次
return
false;
break
;
}
}
return
$this
->access = true;
}
return
false;
}
//更新头部指针指向,指向下一个位置
private
function
incrHead()
{
//$this->getHeadAndTail(); //获取最新指针信息 ,由于本方法体均在锁内调用,其锁内已调用了此方法,本行注释
$this
->HEAD++;
//头部指针下移
if
(
$this
->HEAD >=
$this
->MAXNUM) {
$this
->HEAD = 0;
//边界值修正
}
;
$this
->LEN--;
//Head的移动由Pop触发,所以相当于数量减少
if
(
$this
->LEN < 0) {
$this
->LEN = 0;
//边界值修正
}
;
memcache_set(self::
$client
,
$this
->queueName . self::HEAD_KEY,
$this
->HEAD, false,
$this
->expire);
//更新
memcache_set(self::
$client
,
$this
->queueName . self::LENGTH_KEY,
$this
->LEN, false,
$this
->expire);
//更新
}
//更新尾部指针指向,指向下一个位置
private
function
incrTail()
{
//$this->getHeadAndTail(); //获取最新指针信息,由于本方法体均在锁内调用,其锁内已调用了此方法,本行注释
$this
->TAIL++;
//尾部指针下移
if
(
$this
->TAIL >=
$this
->MAXNUM) {
$this
->TAIL = 0;
//边界值修正
}
;
$this
->LEN++;
//Head的移动由Push触发,所以相当于数量增加
if
(
$this
->LEN >=
$this
->MAXNUM) {
$this
->LEN =
$this
->MAXNUM;
//边界值长度修正
}
;
memcache_set(self::
$client
,
$this
->queueName . self::TAIL_KEY,
$this
->TAIL, false,
$this
->expire);
//更新
memcache_set(self::
$client
,
$this
->queueName . self::LENGTH_KEY,
$this
->LEN, false,
$this
->expire);
//更新
}
// 解锁
private
function
unLock()
{
memcache_delete(self::
$client
,
$this
->queueName . self::LOCK_KEY);
$this
->access = false;
}
//判断是否满队列
public
function
isFull()
{
//外部直接调用的时候由于没有锁所以此处的值是个大概值,并不很准确,但是内部调用由于在前面有lock,所以可信
if
(
$this
->canRewrite)
return
false;
return
$this
->LEN ==
$this
->MAXNUM ? true : false;
}
//判断是否为空
public
function
isEmpty()
{
//外部直接调用的时候由于没有锁所以此处的值是个大概值,并不很准确,但是内部调用由于在前面有lock,所以可信
return
$this
->LEN == 0 ? true : false;
}
public
function
getLen()
{
//外部直接调用的时候由于没有锁所以此处的值是个大概值,并不很准确,但是内部调用由于在前面有lock,所以可信
return
$this
->LEN;
}
/*
* push值
* @param mixed 值
* @return bool
*/
public
function
push(
$data
=
''
)
{
$result
= false;
if
(
empty
(
$data
))
return
$result
;
if
(!
$this
->lock()) {
return
$result
;
}
$this
->getHeadAndTail();
//获取最新指针信息
if
(
$this
->isFull()) {
//只有在非覆写下才有Full概念
$this
->unLock();
return
false;
}
if
(memcache_set(self::
$client
,
$this
->queueName . self::VALU_KEY .
$this
->TAIL,
$data
, MEMCACHE_COMPRESSED,
$this
->expire)) {
//当推送后,发现尾部和头部重合(此时指针还未移动),且右边仍有未由Head读取的数据,那么移动Head指针,避免尾部指针跨越Head
if
(
$this
->TAIL ==
$this
->HEAD &&
$this
->LEN >= 1) {
$this
->incrHead();
}
$this
->incrTail();
//移动尾部指针
$result
= true;
}
$this
->unLock();
return
$result
;
}
/*
* Pop一个值
* @param [length] int 队列长度
* @return array
*/
public
function
pop(
$length
= 0)
{
if
(!
is_numeric
(
$length
))
return
false;
if
(!
$this
->lock())
return
false;
$this
->getHeadAndTail();
if
(
empty
(
$length
))
$length
=
$this
->LEN;
//默认读取所有
if
(
$this
->isEmpty()) {
$this
->unLock();
return
false;
}
//获取长度超出队列长度后进行修正
if
(
$length
>
$this
->LEN)
$length
=
$this
->LEN;
$data
=
$this
->popKeyArray(
$length
);
$this
->unLock();
return
$data
;
}
/*
* pop某段长度的值
* @param [length] int 队列长度
* @return array
*/
private
function
popKeyArray(
$length
)
{
$result
=
array
();
if
(
empty
(
$length
))
return
$result
;
for
(
$k
= 0;
$k
<
$length
;
$k
++) {
$result
[] = @memcache_get(self::
$client
,
$this
->queueName . self::VALU_KEY .
$this
->HEAD);
@memcache_delete(self::
$client
,
$this
->queueName . self::VALU_KEY .
$this
->HEAD, 0);
//当提取值后,发现头部和尾部重合(此时指针还未移动),且右边没有数据,即队列中最后一个数据被完全掏空,此时指针停留在本地不移动,队列长度变为0
if
(
$this
->TAIL ==
$this
->HEAD &&
$this
->LEN <= 1) {
$this
->LEN = 0;
memcache_set(self::
$client
,
$this
->queueName . self::LENGTH_KEY,
$this
->LEN, false,
$this
->expire);
//更新
break
;
}
else
{
$this
->incrHead();
//首尾未重合,或者重合但是仍有未读取出的数据,均移动HEAD指针到下一处待读取位置
}
}
return
$result
;
}
/*
* 重置队列
* * @return NULL
*/
private
function
reset(
$all
= false)
{
if
(
$all
) {
memcache_delete(self::
$client
,
$this
->queueName . self::HEAD_KEY, 0);
memcache_delete(self::
$client
,
$this
->queueName . self::TAIL_KEY, 0);
memcache_delete(self::
$client
,
$this
->queueName . self::LENGTH_KEY, 0);
}
else
{
$this
->HEAD =
$this
->TAIL =
$this
->LEN = 0;
memcache_set(self::
$client
,
$this
->queueName . self::HEAD_KEY, 0, false,
$this
->expire);
memcache_set(self::
$client
,
$this
->queueName . self::TAIL_KEY, 0, false,
$this
->expire);
memcache_set(self::
$client
,
$this
->queueName . self::LENGTH_KEY, 0, false,
$this
->expire);
}
}
/*
* 清除所有memcache缓存数据
* @return NULL
*/
public
function
memFlush()
{
memcache_flush(self::
$client
);
}
public
function
clear(
$all
= false)
{
if
(!
$this
->lock())
return
false;
$this
->getHeadAndTail();
$Head
=
$this
->HEAD;
$Length
=
$this
->LEN;
$curr
= 0;
for
(
$i
= 0;
$i
<
$Length
;
$i
++) {
$curr
=
$this
->
$Head
+
$i
;
if
(
$curr
>=
$this
->MAXNUM) {
$this
->HEAD =
$curr
= 0;
}
@memcache_delete(self::
$client
,
$this
->queueName . self::VALU_KEY .
$curr
, 0);
}
$this
->unLock();
$this
->reset(
$all
);
return
true;
}
}
PHP memcache 环形队列的更多相关文章
- 【转】C#环形队列
概述 看了一个数据结构的教程,是用C++写的,可自己C#还是一个菜鸟,更别说C++了,但还是大胆尝试用C#将其中的环形队列的实现写出来,先上代码: 1 public class MyQueue< ...
- Atitit.提升软件稳定性---基于数据库实现的持久化 循环队列 环形队列
Atitit.提升软件稳定性---基于数据库实现的持久化 循环队列 环形队列 1. 前言::选型(马) 1 2. 实现java.util.queue接口 1 3. 当前指针的2个实现方式 1 1.1 ...
- 队列(Queue)--环形队列、优先队列和双向队列
1. 队列概述 队列和堆栈都是有序列表,属于抽象型数据类型(ADT),所有加入和删除的动作都发生在不同的两端,并符合First In, First Out(先进先出)的特性. 特性: ·FIFO ·拥 ...
- 环形队列C++实现
大家好,我是小鸭酱,博客地址为:http://www.cnblogs.com/xiaoyajiang 以下鄙人用C++实现了环形队列 /******************************** ...
- C#实现环形队列
概述 看了一个数据结构的教程,是用C++写的,可自己C#还是一个菜鸟,更别说C++了,但还是大胆尝试用C#将其中的环形队列的实现写出来,先上代码: public class MyQueue<T& ...
- 数据结构-环形队列 C和C++的实现
队列: 含义:是一种先入先出(FIFO)的数据结构. 当我们把数据一个一个放入队列中.当我们需要用到这些数据时,每次都从队列的头部取出第一个数据进行处理.就像排队进场一样,先排队的人先进场. 结构如下 ...
- [LeetCode] Design Circular Queue 设计环形队列
Design your implementation of the circular queue. The circular queue is a linear data structure in w ...
- ucos-iii串口用信号量及环形队列中断发送,用内建消息队列中断接收
串口发送部分代码: //通过信号量的方法发送数据 void usart1SendData(CPU_INT08U ch) { OS_ERR err; CPU_INT08U isTheFirstCh; O ...
- 1-关于单片机通信数据传输(中断发送,大小端,IEEE754浮点型格式,共用体,空闲中断,环形队列)
补充: 程序优化 为避免普通发送和中断发送造成冲突(造成死机,复位重启),printf修改为中断发送 写这篇文章的目的呢,如题目所言,我承认自己是一个程序猿.....应该说很多很多学单片机的对于... ...
随机推荐
- List Map Set的线程安全
常见的ArrayList LinkedList HashMap TreeMap LinkedHashMap HashSet TreeSet LinkedHashSet 都是线程不安全的.如果要使用 ...
- standard_init_linux.go:178: exec user process caused "no such file or directory"
golang docker build 制作完进项后运行报错 出现该问题的原因是编译的环境和运行的环境不同,可能有动态库的依赖 1.默认go使用静态链接,在docker的golang环境中默认是使用动 ...
- 使用git svn clone迁移svn仓库(保留提交记录)
使用git svn clone迁移svn仓库 clone命令可以指定很多参数,主要用到这些,你也可以使用git svn help查看完整的参数列表. git svn clone https://172 ...
- 基准测试工具:Wrk初识
最近和同事聊起常用的一些压测工具,谈到了Apache ab.阿里云的PTS.Jmeter.Locust以及wrk各自的一些优缺点和适用的场景类型. 这篇博客,简单介绍下HTTP基准测试工具wrk的基本 ...
- 2019 百合佳缘java面试笔试题 (含面试题解析)
本人5年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.百合佳缘等公司offer,岗位是Java后端开发,因为发展原因最终选择去了百合佳缘,入职一年时间了,也成为了面 ...
- python数据分析三剑客之: Numpy
数据分析三剑客之: Numpy 一丶Numpy的使用 numpy 是Python语言的一个扩展程序库,支持大维度的数组和矩阵运算.也支持针对数组运算提供大量的数学函数库 创建ndarray # 1 ...
- vue组件4 利用slot将内容传递给组件
除了将数据作为prop传入到组件中,vue也允许传入HTML 父组件中的子组件:<custom-button>点我<custom-button/> custom-button子 ...
- 13、vue如何解决跨域问题
开发环境:配置config文件夹中index.js文件: proxyTable: { '/api': { target: 'http://10.10.1.242:8245',//后端地址 // sec ...
- Crontab常用命令总结
一.启动服务 /sbin/service crond start 二.关闭服务 /sbin/service crond stop 三.重启服务 /sbin/service crond restart ...
- MES应用案例|新宏泰电器乘上智能制造的东风
企业背景: 无锡新宏泰电器科技股份有限公司(下文简称:新宏泰电器)创立于1984年,公司主要生产断路器.微型电机.BMC/SMC材料.BMC/SMC模压制品及各类塑料模具的设计制造.已于2016年在沪 ...