はじめに
今日はロックについて話し、実装を示します。 リソースのシングルスレッド使用を保証する必要がある場合、各開発者は繰り返し問題に直面しています。
多くの場合、このようなロックを確保するために、特別なファイルの作成にスキームが使用され、その存在がリソースの使用の事実を決定します。
このアプローチは実装が非常に簡単ですが、いくつかの欠点があります。 欠点を特定できます:
- 多数のスレッドによるブロックの100%保証の欠如;
- ブロッキングは1つのサーバー内で機能します。
- そして最も不愉快なことは、何らかの理由でロックを設定したプロセスがそれを削除しなかった場合、残りのプロセスは手動でまたは他の方法でこのロックが解除されるまでこのリソースにアクセスできないことです。
いつロックが必要ですか?
ニーズが異なるたびに、基本的に、同時に繰り返されるアクションを排除し、ある種のリソースとの一貫した作業を確保し、均一な負荷を確保することになります。
自分でそれを行う方法は?
正しいロックを実装するには、
原子性と
トランザクション 性の原則を理解する必要があります。 この記事ではそれらを説明しません。 これらのトピックに関する多くの情報がすでにインターネット上にあります。
実装中に、ロックを操作する際の基本的な操作を決定しました。
- 一定時間ロックする
- 指定した時間後にロック解除
- ロックを延長
- ロックが傍受された場合に対処する
実際、ロックプロバイダーとして使用されるものはそれほど重要ではありません。ファイル、mysql、memcache、またはその他の便利なツールを使用できます。
Redisは私たちに近いので、Redisに上記の欠点のないロックメカニズムを実装しました。
ロックがどのように行われるか
本日、ほぼすべての行にコメントを追加し、使用例を示した「現状のまま」の実装を提供します。
実装はYiiフレームワークのプロジェクトで使用され、Rediskaライブラリを介してRedisに接続するために使用されます。 しかし、RediskaのようなYiiのタイは小さいため、このコードは任意のPHPプロジェクトで使用できます。
それでは、楽しい部分に移りましょう。
ベースロッククラス
<?php class Lock { static protected function getKey( $key ) { return $key; } static public function getLock( $key, $timeWait = 0, $maxExecuteTime = 3600 ) { throw new Lock_Exception('Not defined method getLock'); return false; } static protected function getCurrentProcessId() { static $myProcessId = false; if ( $myProcessId === false ) { $uname = posix_uname(); $mypid = getmypid(); $myProcessId = $uname['nodename'] . '_' . $mypid; } return $myProcessId; } static public function releaseLock( $key, $delayAfter = 0 ) { throw new Lock_Exception('Not defined method releaseLock'); return false; } static public function prolongate( $key, $timeProlongate ) { throw new Lock_Exception('Not defined method prolongate'); return false; } } class Lock_Exception extends Exception { } class Timeout_Lock_Exception extends Lock_Exception { } class LostLock_Timeout_Lock_Exception extends Timeout_Lock_Exception { }
クラスの再ロック
このクラスでは、Redisを使用してロックが作成されます。
<?php class RedisLock extends Lock { static protected function getKey( $key ) {
使用例
レポートを生成するスクリプトがあるとします。 そして、このスクリプトは複数のスレッドで同時に実行する意味がありません。 それらはすべて結果として同じ結果を生成しますが、それぞれが作業中に大量のリソースを必要とします。 このスクリプトは平均で40〜50分で実行されることがわかっているため、わずかなマージンを取り、ロックを60分間に設定します。
$lockKey = 'cron-report'; $timeWait = 0; $timeLock = 3600; if ( RedisLock::getLock( $lockKey, $timeWait, $timeLock ) ) {
私たちの実装があなたに役立つことを願っています。
質問やコメントを待っています。