PHP memcache ring queue class. Beginner, I have never learned the data structure. because of the business needs, I just tried it hard! The original form is the PHP memcache queue code shared by lusi on oschina. To enable the queue to enter and exit at any time, and avoid the danger of the int length crossing the border (if a single chain uses the Head auto-increment function, it is possible that the process does not cross the border), simply rewrite the queue to a ring queue. There may be bugs. sorry!
-
- /**
- * PHP memcache ring queue
- * Original author LKK/lianq.net
- * Modify FoxHunter
- * Because the business only needs to retain Pop and Push in the queue, the modification of the Expiration Time to 0 is permanent
- */
- Class MQueue
- {
- Public static $ client;
-
- Private $ expire; // expiration time, in seconds, 1 ~ 2592000, that is, within 30 days
- Private $ sleepTime; // waiting for unlock time, in microseconds
- Private $ queueName; // queue name, unique value
- Private $ retryNum; // number of attempts
- Private $ MAXNUM; // maximum queue capacity
- Private $ canRewrite; // whether the write overwrite switch is enabled. the full content overwrites the original data from the header.
-
- Private $ HEAD; // The pointer position to be entered Next
- Private $ TAIL; // pointer position to be entered Next
- Private $ LEN; // existing queue length
-
- Const LOCK_KEY = '_ Fox_MQ_LOCK _'; // lock storage identifier
- Const LENGTH_KEY = '_ Fox_MQ_LENGTH _'; // storage ID of the current queue length
- Const VALU_KEY = '_ Fox_MQ_VAL _'; // queue key value storage identifier
- Const HEAD_KEY = '_ Fox_MQ_HEAD _'; // specifies the position of the HEAD pointer in the queue.
- Const TAIL_KEY = '_ Fox_MQ_TAIL _'; // specifies the position of the TAIL pointer in the queue.
-
- /*
- * Constructor
- * For the same $ queueName, the parameter values of the constructor must be consistent during instantiation. otherwise, pop and push will lead to disordered queue order.
- */
- Public function _ construct ($ queueName = '', $ maxqueue = 1, $ canRewrite = false, $ expire = 0, $ config = '')
- {
- If (empty ($ config )){
- Self: $ client = memcache_pconnect ('2017. 0.0.1 ', 127 );
- } Elseif (is_array ($ config) {// array ('host' => '2017. 0.0.1 ', 'port' => '2016 ')
- 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]: '2017. 0.0.1 ';
- $ Conf ['port'] = isset ($ tmp [1])? $ Tmp [1]: '20140901 ';
- Self: $ client = memcache_pconnect ($ conf ['host'], $ conf ['port']);
- }
- If (! Self: $ client)
- Return false;
- Ignore_user_abort (true); // when the customer disconnects, continue execution is allowed.
- Set_time_limit (0); // cancels the upper limit of script execution latency
- $ This-> access = false;
- $ This-& gt; 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;
-
- }
-
- // Obtain the beginning and end pointer information and length of the queue.
- 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 );
- }
-
-
- // Use memcache_add atomic lock
- 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) {// try to wait N times
- Return false;
- Break;
- }
- }
- Return $ this-> access = true;
- }
- Return false;
- }
-
- // Update the header pointer to the next position
- Private function incrHead ()
- {
- // $ This-> getHeadAndTail (); // obtain the latest pointer information. because the method bodies are called within the lock, this method has been called in the lock. this method is commented out in this row.
- $ This-> HEAD ++; // move the HEAD pointer down
- If ($ this-> HEAD >=$ this-> MAXNUM ){
- $ This-> HEAD = 0; // modify the boundary value
- }
- ;
- $ This-> LEN --; // Head movement is triggered by Pop, so the number is reduced.
- If ($ this-> LEN <0 ){
- $ This-> LEN = 0; // modify the boundary value
- }
- ;
- Memcache_set (self ::$ client, $ this-> queueName. self: HEAD_KEY, $ this-> HEAD, false, $ this-> expire); // update
- Memcache_set (self ::$ client, $ this-> queueName. self: LENGTH_KEY, $ this-> LEN, false, $ this-> expire); // update
-
- }
-
- // Update the tail pointer to the next position
- Private function incrTail ()
- {
-
- // $ This-> getHeadAndTail (); // obtain the latest pointer information. because the method bodies are called within the lock, this method has been called in the lock. this method is commented out in this row.
- $ This-> TAIL ++; // move the TAIL pointer down
- If ($ this-> TAIL >=$ this-> MAXNUM ){
- $ This-> TAIL = 0; // fixed the boundary value
- }
- ;
- $ This-> LEN ++; // Head movement is triggered by Push, so it is equivalent to an increase in the number.
- If ($ this-> LEN >=$ this-> MAXNUM ){
- $ This-> LEN = $ this-> MAXNUM; // fixed the length of the boundary value.
- }
- ;
- Memcache_set (self: $ client, $ this-> queueName. self: TAIL_KEY, $ this-> TAIL, false, $ this-> expire); // update
- Memcache_set (self ::$ client, $ this-> queueName. self: LENGTH_KEY, $ this-> LEN, false, $ this-> expire); // update
- }
-
-
- // Unlock
- Private function unLock ()
- {
- Memcache_delete (self ::$ client, $ this-> queueName. self: LOCK_KEY );
- $ This-> access = false;
- }
-
- // Determine whether the queue is full
- Public function isFull ()
- {
- // The value here is an approximate value because there is no lock in the external direct call, which is not very accurate. However, the internal call is credible because the lock exists in the front.
- If ($ this-> canRewrite)
- Return false;
- Return $ this-> LEN = $ this-> MAXNUM? True: false;
- }
-
- // Judge whether it is null
- Public function isEmpty ()
- {
- // The value here is an approximate value because there is no lock in the external direct call, which is not very accurate. However, the internal call is credible because the lock exists in the front.
- Return $ this-> LEN = 0? True: false;
- }
-
- Public function getLen ()
- {
- // The value here is an approximate value because there is no lock in the external direct call, which is not very accurate. However, the internal call is credible because the lock exists in the front.
- Return $ this-> LEN;
- }
-
- /*
- * Push value
- * @ Param mixed value
- * @ Return bool
- */
- Public function push ($ data = '')
- {
-
- $ Result = false;
- If (empty ($ data ))
- Return $ result;
-
- If (! $ This-> lock ()){
- Return $ result;
- }
-
- $ This-> getHeadAndTail (); // Get the latest pointer information
-
- If ($ this-> isFull () {// The concept of "Full" is available only when the write is not overwritten.
- $ This-> unLock ();
- Return false;
- }
-
- If (memcache_set (self: $ client, $ this-> queueName. self: VALU_KEY. $ this-> TAIL, $ data, MEMCACHE_COMPRESSED, $ this-> expire )){
- // After the push, it is found that the tail and the header overlap (at this time the pointer has not been moved), and there is still data not read by the Head on the right side, so move the Head pointer to avoid the tail pointer crossing the Head
- If ($ this-> TAIL ==$ this-> HEAD & $ this-> LEN> = 1 ){
- $ This-> incrHead ();
- }
- $ This-> incrTail (); // move the tail pointer
- $ Result = true;
- }
-
- $ This-> unLock ();
- Return $ result;
- }
-
-
- /*
- * Pop a value
- * @ Param [length] int queue length
- * @ 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; // read all
-
- If ($ this-> isEmpty ()){
- $ This-> unLock ();
- Return false;
- }
- // Modify the value after the length of the queue is exceeded.
- If ($ length> $ this-> LEN)
- $ Length = $ this-> LEN;
-
- $ Data = $ this-> popKeyArray ($ length );
- $ This-> unLock ();
- Return $ data;
- }
-
-
- /*
- * The value of a certain pop segment length
- * @ Param [length] int queue length
- * @ 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 );
- // When the value is extracted, it is found that the header and tail overlap (at this time the pointer has not been moved), and there is no data on the right side, that is, the last data in the queue is completely hollowed out, at this time, the pointer stays locally and the queue length changes to 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); // update
- Break;
- } Else {
- $ This-> incrHead (); // The beginning and end of the HEAD are not overlapped, or overlap, but there are still unread data, move the HEAD pointer to the next location to be read
- }
- }
- Return $ result;
- }
-
- /*
- * Reset the queue
- ** @ 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 );
- }
- }
-
- /*
- * Clear all memcache cache data
- * @ 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;
- }
-
-
- }
|