Skip to content

Instantly share code, notes, and snippets.

@kawa-
Created November 3, 2014 14:20
Show Gist options
  • Save kawa-/4dc2b8a9c1bd018abc32 to your computer and use it in GitHub Desktop.
Save kawa-/4dc2b8a9c1bd018abc32 to your computer and use it in GitHub Desktop.
ソースに書いてあるURLにあるRedisのLockを参考にPHP版を作ろうとしたが失敗したけれど捨てがたいからスニペットにしておいた
<?php
// http://ameblo.jp/principia-ca/entry-11770810115.html
// ここ↑にはJavaのRedisのLockが書いてあり、それを参考にPHP版を作ろうとしたが上手く行かず。
// try {} finallyとか、exceptionがなんかだめな感じ(´・ω・`)
define('LOCK_KEY_PREFIX', 'lock:');
define('LOCK_EXPIRE_SEC', 30);
define('LOCK_SLEEP_MILL', 10);
class RedisLockTimeoutException extends Exception {
public function __construct() {
parent::__construct('Redis lock time out.', -1);
}
}
class RedisLock {
private $redis;
private $lockKey;
private $lockMills;
private $isLocked = FALSE;
private $lockedValue = '';
function __construct(Redis $redis, $lockKey, $lockMills) {
$this->redis = $redis;
$this->lockKey = LOCK_KEY_PREFIX . $lockKey;
$this->lockMills = $lockMills;
}
public function lock() {
if ($this->isLocked === TRUE) {
return;
}
// ロック待機時間
$max = round(microtime(true) * 1000) + $this->lockMills;
while (TRUE) {
$now = round(microtime(true) * 1000);
$value = (string) $now;
// ロック試行
$result = $this->redis->setnx($this->lockKey, $value);
// ロックが成功したらexpireを設定してデッドロック防止
if ($result === TRUE) {
$this->redis->expire($this->lockKey, LOCK_EXPIRE_SEC);
$this->isLocked = TRUE;
$this->lockedValue = $value;
break;
}
// ロック待機時間を過ぎたら終了
if ($max <= $now) {
$locked = $this->redis->get($this->lockKey);
if ($locked === FALSE) {
$elaspedTime = $now - (int) $locked;
// expireが効いていなかったら削除してデッドロック防止
if ($elaspedTime >= LOCK_EXPIRE_SEC * 1000) {
$this->redis->del($this->lockKey);
}
}
throw new RedisLockTimeoutException();
}
// ロックできなかったらsleepして再試行
usleep(LOCK_SLEEP_MILL * 1000);
}
}
public function unlock() {
if ($this->isLocked === FALSE) {
return;
}
// ロックの値を取得
$value = $this->redis->get($this->lockKey);
// このインスタンスでロックされたものなら削除
if ($this->lockedValue === $value) {
$this->redis->del($this->lockKey);
}
$this->isLocked = FALSE;
$this->lockedValue = '';
}
}
class RedisLockFactory {
private $lockTime;
private $redis;
public function RedisLockFactory(Redis $redis, $lockTime) {
$this->redis = $redis;
$this->lockTime = $lockTime;
}
public function getLock($lockKey) {
return new RedisLock($this->redis, $lockKey, $this->lockTime);
}
}
$redis = new Redis();
$redis->connect('127.0.0.1', 26379, 1.0);
$lockFactory = new RedisLockFactory($redis, 10000);
for ($i = 0; $i < 10; $i++) {
// ロック取得
$lock = $lockFactory->getLock('hoge');
// ロック実行
$lock->lock();
try {
// 排他制御を行いたい更新処理
update();
sleep(3);
} catch (RedisLockTimeoutException $exc) {
var_dump($exc);
}
$lock->unlock();
sleep(1);
}
function update(){
echo 'hoge';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment