序文 前回の記事ではRedisの分散ロックの原理と欠点について書きましたが、それだけでは不十分だと感じました。 Redisson フレームワークについては簡単に紹介しただけで、具体的な原則については説明していませんでした。旧正月が終わったら何もすることがないので、Redisson のソースコードを勉強してみるのもいいかもしれません。 気まぐれではありましたが、よく調べてみると、Redisson のソースコードを解釈する作業量は依然としてかなり多いことがわかりました。多数の Java 並行処理クラスが使用されており、Redis コンポーネントとのリモート呼び出しを実装するための通信ツールとして Netty が挙げられます。これらすべての知識ポイントを説明するのは現実的ではありません。この記事の焦点は主に Redisson 分散ロックの実装原理にあるため、ネットワーク通信と同時実行原理のコード解釈についてはあまり詳しく説明しません。至らぬ点がありましたらご容赦ください。 Redis のパブリッシュとサブスクライブ 前述したように、分散ロックのコア機能は実際にはロック、ロック解除、ロック タイムアウトの設定の 3 つです。これら 3 つの機能は、Redisson 分散ロックの原理に関する私たちの研究の方向性でもあります。 学習する前に、Redis のパブリッシュ/サブスクライブ機能に関する知識ポイントを理解する必要があります。 Redis のパブリッシュ/サブスクライブ (pub/sub) は、送信者 (pub) がメッセージを送信し、サブスクライバー (sub) がメッセージを受信するためのメッセージ通信モードです。パブリッシャーは指定されたチャネル(チャンネル)にメッセージを送信することができ、サブスクライバーはチャネルを購読することでメッセージを受信できるため、複数のクライアント間での通信効果が得られます。 サブスクリプション コマンドは SUBSCRIBE channel [channel ...] であり、1 つ以上のチャネルをサブスクライブできます。 PUBLISH コマンドを介して新しいメッセージがチャネルに送信されると、サブスクライバーは次のようにメッセージを受信します。 2 つのクライアントを開き、1 つはチャネル channel1 をサブスクライブし、もう 1 つが PUBLISH を通じてメッセージを送信すると、サブスクライブしたクライアントがそれを受信します。このモードでは、異なるクライアント間の通信を実現できます。 この通信モードの素晴らしい使用シナリオについては詳しく説明しません。オンラインで確認できます。私たちの主人公は依然としてレディソンです。ウォーミングアップが終わったら、メインコースの提供です。 Redisson ソースコード Redisson を使用してロックする前に、まず RLock インスタンス オブジェクトを取得する必要があります。このオブジェクトを使用すると、lock メソッドと tryLock メソッドを呼び出してロック機能を完了できます。
対応するホストを構成した後、RLock オブジェクトを作成できます。 RLock は、特定の同期装置が実装する必要があるインターフェースです。 redisson.getLock()を呼び出すと、プログラムはデフォルトの同期エグゼキュータRedissonLockを初期化します。 ここでいくつかのパラメータが初期化されます。 commandExecutor : 非同期エグゼキューター。 Redisson のすべてのコマンドは...Executor を通じて実行されます。 id : 初期化時に UUID を使用して作成された一意の ID。 internalLockLeaseTime : ロックを取得するまでの待機時間。構成クラスで定義されているデフォルトの時間は 30 秒です。 同時に、図には getEntryName メソッドもマークされています。このメソッドは、「ID: ロック名」という文字列を返します。これは、対応するロックを保持している現在のスレッドの識別子を表します。これらのパラメータは、後続のソース コード分析で頻繁に表示されるため、記録しておく必要があります。 初期化について説明した後、ロックとロック解除のソースコードの学習を開始できます。 ロック Redisson には、tryLock と lock という 2 つのロック メソッドがあります。使用方法の違いは、tryLock ではロックの有効期限のleaseTime と待機時間のwaitTime を設定できることです。コア処理ロジックは同様です。まずはtryLockから始めましょう。 ロックを試みる コードが少し長いです。 。 。写真にするのは不便なので、そのまま貼り付けておきます。
コードはまだかなり長いですが、プロセスはたった 2 つのステップです。スレッドがロックを取得して正常に戻るか、または、ロックの取得に失敗し、待機時間が経過していない場合は、ロックを取得するためにループを継続し、ロックが解除されるかどうかを監視します。 ロックを取得するメソッドは tryAcquire です。渡されるパラメータは、ロック保持時間、時間単位、および現在のスレッドを表す ID です。コードに従ってコールスタックを表示すると、tryAcquireAsync というメソッドが呼び出されます。
続けて、tryLockInnerAsync メソッドのソース コードを見てみましょう。
基礎となる呼び出しスタックは次のとおりです。コマンドを直接操作し、Lua スクリプトに統合し、netty ツール クラスを呼び出して redis と通信し、ロックを取得する機能を実現します。 このスクリプト コマンドは非常に興味深いので、簡単に説明しましょう。
コマンドのロジックは複雑ではありませんが、作者の設計は非常に思慮深いものであると言わざるを得ません。データの保存には、Redis のハッシュ構造が使用されます。現在のスレッドがすでにロックを保持していることが判明した場合、hincrby コマンドを使用して値に 1 を追加します。 value の値によって、ロックが解除されたときに unlock コマンドが呼び出される回数が決定され、それによってロックの再入効果が実現されます。 以下の図にコマンドの各ステップに対応するロジックをマークしましたので、ご覧ください。 コードを続行しましょう。上記のコマンドによれば、スレッドがロックを取得すると、tryLock メソッドは直接 true を返し、すべて正常になります。 取得できない場合は、ロックの残り有効期限が返されます。今回の目的は何ですか? tryLock メソッドのデッド ループに戻りましょう。 ここでは、waitTime とキーの残りの有効期限を比較し、2 つのうち小さい方の値を取得し、Java の Semaphore セマフォの tryAcquire メソッドを使用してスレッドをブロックします。 では、Semaphore セマフォを制御するのは誰で、いつリリースできるのでしょうか?ここで上記に戻る必要があります。上記で投稿した tryLock コードに次の段落があることを覚えておいてください。
サブスクリプション ロジックは明らかに subscribe メソッドにあります。メソッド呼び出しチェーンに従って、PublishSubscribe.java に入ります。 このコードの目的は、現在のスレッドの threadId を AsyncSemaphore に追加し、redis の公開およびサブスクリプション機能を通じて実装される redis リスナーを設定することです。 リスナーは Redis からメッセージを受信すると、現在のスレッドに関連する情報を取得します。ロックが解除されたというメッセージであれば、Semaphore を操作する(つまり release メソッドを呼び出す)ことで、ブロックされていた領域を直ちに解放します。 解放後もスレッドは実行を継続し、タイムアウトしたかどうかを判断します。タイムアウトになっていない場合は、次のループに入り、再度ロックを取得します。ロックを取得すると true を返します。そうでない場合は、プロセスが続行されます。 ここで説明すると、ループが発生する理由は、ロックが複数のクライアントによって同時に競合される可能性があるためです。ブロッキングが解除された直後はスレッドがロックを取得できない可能性がありますが、スレッドの待機時間は経過していません。このとき、ロックを取得するにはループを再度実行する必要があります。 これは tryLock がロックを取得するプロセス全体です。フローチャートを描くと次のようになります。 ロック tryLock に加えて、ロックを取得するために lock を直接呼び出すこともよくあります。 lock のロック取得プロセスは基本的に tryLock と同じです。違いは、ロックではロック有効期限パラメータが手動で設定されないことです。このメソッドの呼び出しチェーンは、ロックを取得するために tryAcquire メソッドまで実行されます。違いは、ロジックのこの部分まで実行されることです。 このコードは次の 2 つのことを行います。 1. 有効期限を30秒に設定してロックを取得します 2. リスナーを起動します。ロックが取得されたことが判明した場合は、スケジュールされたタスクを開始して、ロックの有効期限を継続的に更新します。 有効期限を更新する方法は、scheduleExpirationRenewal です。ソースコードは次のとおりです:
コードフローは比較的シンプルです。基本的には、時間指定のタスクを開始し、internalLockLeaseTime / 3 (この時間は 10 秒) ごとに、現在のスレッドによってロックがまだ保持されているかどうかを確認します。その場合は、有効期限 internalLockLeaseTime を 30 秒にリセットします。 これらのスケジュールされたタスクは、ConcurrentHashMap オブジェクトの expireRenewalMap に保存され、保存されるキーは「スレッド ID: キー名」です。対応する現在のスレッド キーが expirationRenewalMap に存在しないことが判明した場合、スケジュールされたタスクは実行されません。これはその後のロック解除においても重要な操作となります。 上記のコードは、Redisson のいわゆる「ウォッチドッグ」プログラムであり、手動でロックを解除する前に期限が切れないように、非同期スレッドを使用して定期的に検出して実行します。 残りのロジックは基本的に tryLock() と同じです。見ればわかりますよ。 ロック解除 ロックする方法があれば、ロックを解除する方法もあります。 Redisson 分散ロックのロックを解除するための上位レベルの呼び出しメソッドは unlock() であり、デフォルトではパラメーターは渡されません。
ロック解除関連のコマンド操作は、unlockInnerAsync メソッドで定義されます。 これは別の長い Lua スクリプトの文字列であり、前のロック スクリプトよりも少し複雑です。しかし、それは問題ではありません。簡単に整理してみましょう。コマンドのロジックはおおよそ次のようになります。 1. ロックが存在するかどうかを判断します。そうでない場合は、publish コマンドを使用してメッセージを公開し、ロックを解除します。加入者はそれを受け取った後、ロックを取得する次のステップに進むことができます。 2. ロックは存在しますが、現在のスレッドによって保持されていないため、nil が返されます。 3. 現在のスレッドがロックを保持します。 hincrby コマンドを使用して、ロックの再入回数を 1 減らします。次に、再入回数が 0 より大きいかどうかを判断します。大きい場合は、ロックの有効期限を更新して 0 を返します。それ以外の場合は、ロックを削除し、ロックを解除するメッセージを公開して 1 を返します。 スレッドがロックを完全に解放すると、cancelExpirationRenewal() メソッドを呼び出してウォッチドッグの拡張スレッドをキャンセルします。
これはロックを解除するプロセスです。どうですか?これは比較的シンプルで、ロックを追加するためのコードよりもはるかに読みやすいです。もちろん、シンプルですが、分散ロックのプロセス全体を理解していただくために、フローチャートを描くことに苦労しました (この時点で、トリプルクリックしていただく必要がありますね、笑)。 レッドロック 以上がRedisson分散ロックの原理の説明です。一般的には、Lua スクリプトを使用して基本的な set コマンドを統合し、ロック機能を実装するだけです。これは、多くの Redis 分散ロック ツールの設計原則でもあります。さらに、Redisson はロック効果を実現するための「RedLock アルゴリズム」の使用もサポートしています。このツール クラスは RedissonRedLock です。 使い方も非常に簡単です。複数の Redisson ノードを作成します。これらの無関係なノードは完全な分散ロックを形成できます。
RedLock アルゴリズムの原理については詳しく説明しません。ご興味がございましたら、前回の記事を読んだり、オンラインで検索したりしてみてください。つまり、Redis インスタンスの単一障害点の問題をある程度効果的に防ぐことができますが、完全に信頼できるわけではありません。どちらの設計が使用されても、Redis 自体はロックの強力な一貫性を保証することはできません。 古いことわざにもあるように、ケーキを食べてケーキも残すことはできませんが、これはパフォーマンスとセキュリティにも当てはまります。 Redis の強力なパフォーマンスと使いやすさは、日常の分散ロックのニーズを満たすのに十分です。ビジネス シナリオでロックのセキュリティ リスクを許容できない場合、最も確実な方法は、ビジネス レイヤーでべき等処理を実行することです。 要約する この記事のソースコード分析を読んだ後、Redisson 分散ロックの設計について十分に理解できたと思います。もちろん、ソース コードについては説明していますが、主な焦点は依然として分散ロックの原則にあります。プロセスに関連しない一部のコードは、逐語的に解釈されません。興味があれば自分で読んでみてください。ソース コードの多くの箇所で、いくつかの基本的な並行処理ツールとネットワーク通信の有用性が示されています。彼らから学ぶことは非常にやりがいがあります。 最後に、Redisson のコメントが本当に少ないことにまだ不満を言いたいです。 。 。 。 。 。 |
>>: マルチクラウド戦略が組織のクラウドへの移行を簡素化する方法
クラウド セキュリティは、今日のテクノロジー業界で流行語となっていますが、それには十分な理由がありま...
今日は安価な海外VPS、特にローエンドと低価格シリーズのVPSについてお話ししましょう。市場の変化、...
SEO 初心者、あるいは永遠に SEO 初心者である人々がいます。彼らは目覚めない限り、決して成長で...
今日はクラウド コンピューティング、ビッグ データ、人工知能についてお話します。これら 3 つの単語...
このテーマについて書こうと思ったのは少し偶然でした。先週末、エコノミスト誌の記事を閲覧中に業界関連の...
序文最近、分散トランザクションに関するブログ投稿をいくつか読んで、メモを取りました。ハハハ〜データベ...
Linovus Holdings Inc 傘下の VPS ブランドである VortexNode では...
みなさんこんにちは。私は平凡なSEOスタッフの謝凱です。最近、Baiduのデータが再び異常になりまし...
書くことはあまりないので、メモリの少ないVPSでメモリ不足の問題を解決する方法についてお話ししましょ...
現在、dogyunは特別プロモーションを開始しました。すべてのコンピュータールームのすべてのエラステ...
スパムニュースの取り締まりから628 Big Kサイト、そして822の発表まで、Baiduは今年一度...
Admin5.comの10月12日の報道によると、最近サプライヤーがWeiboでVanclが商品に対...
⚠️パブリッククラウドのセキュリティインシデント⚠️過去 6 か月間に、小売業界ではパブリック ...
昨日、Baiduが更新され、関連する検索が削除されました。 Baidu は他の場所でもこの機能を提供...