一般情况下我们都用Memcache作为一个分布式的key/value缓存服务器,其实Memcache也可以实一些外门邪道的功能比如作为分布式锁来用。
原理其实非常简单就是memecach_add的时候,如果添加的key已经存在那么后面的添加就会失败。设想在高并发的场景下,如果存在被竞争的资源,我们就可以利用这个小trick来对资源加锁。知道了原理实现起来非常简单,下面是我初步实现的代码。
<?php
/**
* 锁服务(用Memcache模拟锁)
* Author: tomheng<zhm20070928@gmail.com>
* gist: https://gist.github.com/tomheng/6149779
*/
class Lock{
private $mc = null;
private $key_prefix = "memcache_lock_service_key_";
private $all_lock_names = array();
private $expiration = 60; //one min
private $max_block_time = 15; //最长的阻塞时间
/**
* [__construct description]
*/
public function __construct(){
if(function_exists('memcache_init')){
$this->mc = memcache_connect('memcache_host', 11211);
}
}
/**
* [get_key description]
* @param [type] $name [description]
* @return [type] [description]
*/
private function get_key($name){
$key = $this->key_prefix.$name;
return $key;
}
/**
* 捕获锁
* @param [type] $name [description]
* @return [type] [description]
*/
public function begin($name, $block = true)
{
if(!$this->mc || !$name){
return false;
}
$max_block_time = $this->max_block_time;
$key = $this->get_key($name);
do{
$re = memcache_add($this->mc, $key, 1, false, $this->expiration);
if($re == true){
$this->all_lock_names[$name] = 1;
//$this->debug();
break;
}else{
//dolog('Lock failed '.$name);
}
//echo '#'.PHP_EOL;
//sleep(1);
}while($block && $max_block_time-- && !sleep(1));
return $re;
}
/**
* 释放锁
*/
public function release($name){
if(!$this->mc || !$name){
return false;
}
$key = $this->get_key($name);
$re = memcache_delete($this->mc, $key);
if($re == true){
unset($this->all_lock_names[$name]);
}
return $re;
}
/**
* 释放所有的锁
*/
public function __destruct(){
if(!$this->mc){
return false;
}
foreach ($this->all_lock_names as $name => $value) {
# code...
$this->release($name);
}
}
/**
* 调试
* @return [type] [description]
*/
public function debug(){
var_dump($this->all_lock_names);
foreach ($this->all_lock_names as $name => $value) {
$key = $this->get_key($name);
if($this->mc){
$value = memcache_get($this->mc, $key);
}else{
$value = "no such lock ";
}
echo "Lock name:$key, value:{$value}".PHP_EOL;
}
}
}