[[414221]] この記事はWeChatの公開アカウント「UP Technology Control」から転載したもので、著者はconan5566です。この記事を転載する場合は、UP Technology Controlの公式アカウントまでご連絡ください。 シングルプロセスシステムでは、複数のスレッドが同時に変数(可変共有変数)を変更できる場合、変数の同時変更を排除するために、変数またはコードブロックを同期して、変数を変更するときに線形に実行できるようにする必要があります。 同期の本質はロックを通じて実現されます。一度に 1 つのスレッドだけが同じコード ブロックを実行できるようにするには、どこかにマークを付ける必要があります。このマークはすべてのスレッドに表示される必要があります。マークが存在しない場合は設定可能です。後続の他のスレッドは、マークがすでに存在していることを検出すると、マークを持つスレッドが同期コード ブロックを終了し、マークをキャンセルするまで待機してから、マークの設定を試行します。このマークはロックとして理解できます。 すべてのスレッドがマークを見ることができる限り、異なる場所でロックを実装する方法も異なります。たとえば、Java では、同期によってオブジェクト ヘッダーにマークが設定されます。 Lock インターフェースの実装クラスは、基本的に volitile によって変更される int 変数にすぎず、これにより各スレッドが int の可視性とアトミックな変更を持つことが保証されます。 Linux カーネルは、マーキングにミューテックスやセマフォなどのメモリ データも使用します。 実際には、メモリ データをロックに使用することに加えて、相互に排他的なロックも使用できます (相互に排他的なケースのみを考慮します)。例えば、フローテーブル内のシリアル番号と冪等性チェックの時間を組み合わせて、解除されないロックとみなしたり、特定のファイルの存在をロックとして利用したりすることができます。マークを変更するときにのみ、アトミック性とメモリの可視性を確保する必要があります。 1 配布とは何ですか?分散CAP理論は次のように語っています。 一貫性、可用性、およびパーティション耐性を同時に満たす分散システムはありません。最大で 2 つしか満たすことができません。 現在、多くの大規模な Web サイトやアプリケーションが分散形式で展開されています。分散シナリオにおけるデータの一貫性の問題は常に重要なトピックです。 CAP 理論に基づくと、多くのシステムは設計の初期段階でこれら 3 つの間でトレードオフを行う必要があります。インターネット分野のほとんどのシナリオでは、システムの高可用性と引き換えに強力な一貫性を犠牲にする必要があり、システムでは結果的一貫性のみを確保する必要があることがよくあります。 分散シナリオ これは主に、複数の同一サービスが同時に開始されるクラスター モードを指します。 多くのシナリオでは、データの最終的な一貫性を保証するために、分散トランザクションや分散ロックなど、それをサポートする多くの技術的ソリューションが必要です。多くの場合、メソッドは同じスレッドによってのみ同時に実行できるようにする必要があります。スタンドアロン環境では、Java が提供する並行性 API を通じてこの問題を解決できますが、分散環境ではそれほど簡単ではありません。 - 分散シナリオとスタンドアロン シナリオの最大の違いは、マルチスレッドではなくマルチプロセスであることです。
- 複数のスレッドがヒープ メモリを共有できるため、メモリをタグの保存場所として簡単に使用できます。プロセスは同じ物理マシン上に存在しない可能性もあるため、マークはすべてのプロセスが参照できる場所に保存する必要があります。
分散ロックとは何ですか? - 分散モデルでデータのコピーが 1 つしかない場合 (または制限されている場合)、特定の時点でデータを変更するプロセスの数を制御するためにロック テクノロジが必要です。
- スタンドアロン モードのロックと比較すると、プロセスの可視性を確保するだけでなく、プロセスとロック間のネットワークの問題も考慮する必要があります。 (分散環境では問題が複雑になる主な理由は、ネットワークの遅延と信頼性の低さを考慮する必要があるためだと思います...大きな落とし穴です)
- 分散ロックではタグをメモリに保存できますが、メモリは特定のプロセスによって割り当てられるのではなく、Redis や Memcache などのパブリック メモリによって割り当てられます。データベースやファイルなどを使用してロックを作成する場合、タグが相互に排他的である限り、実装は単一のマシンの場合と同じです。
2 どのような分散ロックが必要ですか?分散アプリケーション クラスターでは、同じメソッドは 1 台のマシン上の 1 つのスレッドによってのみ同時に実行されることが保証されます。 このロックは再入可能ロックである必要があります(デッドロックを回避するため) このロックはブロッキング ロックとして最適です (ビジネス ニーズに基づいてこのロックを使用するかどうかを検討してください) このロックは公平なロックとして最適です (ビジネス ニーズに基づいてこのロックを使用するかどうかを検討してください) 可用性の高いロック取得およびロック解放機能 ロックの取得と解放のパフォーマンスが向上しました コードの実装 - パブリックインターフェイス IDistributedLock
- {
- ILockResult ロック(文字列リソースキー);
- ILockResult ロック(文字列リソースキー、TimeSpan 有効期限);
- ILockResult ロック(文字列リソースキー、有効期限の TimeSpan、待機時間の TimeSpan、再試行時間の TimeSpan);
- ILockResult Lock(文字列 resourceKey、TimeSpan expiryTime、TimeSpan waitTime、TimeSpan retryTime、CancellationToken cancellationToken);
- タスク<ILockResult> LockAsync(文字列リソースキー);
- Task<ILockResult> LockAsync(文字列リソースキー、TimeSpan 有効期限);
- Task<ILockResult> LockAsync(文字列リソースキー、有効期限の時間範囲、待機時間の時間範囲、再試行時間の時間範囲);
- Task<ILockResult> LockAsync(文字列 resourceKey、TimeSpan expiryTime、TimeSpan waitTime、TimeSpan retryTime、CancellationToken cancellationToken);
- }
-
- パブリックインターフェイス ILockResult: IDisposable
- {
- 文字列 LockId { 取得; }
- bool IsAcquired { 取得; }
- int ExtendCount { 取得; }
- }
- クラス EndPoint:RedLock.RedisLockEndPoint
- {
- プライベート読み取り専用文字列 _connectionString;
- パブリックエンドポイント(文字列接続文字列)
- {
- _connectionString = 接続文字列;
- //139.196.40.252、パスワード=xstudio、デフォルトデータベース = 9
- var接続= connectionString.Split( ',' );
- var dict = 新しい辞書<文字列, 文字列>();
- foreach (var 項目in 繋がり)
- {
- var keypar = item.Split( '=' );
- (keypar.長さ>1)の場合
- {
- dict[キーパー[0]] = キーパー[1];
- }
- }
- this.EndPoint = 新しい System.Net.DnsEndPoint(接続[0], 6379);
- if (dict.TryGetValue( "password" , out string password ))
- {
- this.Password =パスワード;
- }
- if (dict.TryGetValue( "defaultDatabase" , out string defaultDatabase) && int .TryParse(defaultDatabase, out 整数 データベース))
- {
- RedisDatabase =データベース;
- }
- }
- }
- [エクスポート(typeof(IDistributedLock))]
- クラス InnerLock: IDistributedLock
- {
- プライベート静的Lazy<RedLock.RedisLockFactory> _factory;
-
- 静的InnerLock()
- {
- _factory = new Lazy<RedisLockFactory>(() => new RedisLockFactory(新しい EndPoint(ConfigurationManager.AppSettings[ "Redis" ])), System.Threading.LazyThreadSafetyMode.ExecutionAndPublication);
- }
- パブリックILockResult Lock(文字列リソースキー)
- {
- 新しい LockResult(_factory.Value.Create (resourceKey, TimeSpan.FromDays (1)))を返します。
- }
-
- パブリックILockResult ロック(文字列リソースキー、TimeSpan 有効期限)
- {
- 新しい LockResult(_factory.Value.Create ( resourceKey , expiryTime))を返します。
- }
-
- パブリックILockResult ロック (文字列リソース キー、有効期限の時間、待機時間の時間、再試行時間の時間)
- {
- 新しい LockResult(_factory.Value.Create (resourceKey, expiryTime, waitTime, retryTime))を返します。
- }
-
- パブリックILockResult Lock(文字列 resourceKey、TimeSpan expiryTime、TimeSpan waitTime、TimeSpan retryTime、CancellationToken cancellationToken)
- {
- 新しい LockResult(_factory.Value.Create (resourceKey、expiryTime、waitTime、retryTime、cancelationToken))を返します。
- }
-
- パブリック非同期タスク<ILockResult> LockAsync(文字列リソースキー)
- {
- var result = _factory.Value.CreateAsync(resourceKey, TimeSpan.FromDays(1)) を待機します。
- 新しいLockResult(result)を返します。
- }
-
- パブリック非同期タスク<ILockResult> LockAsync(文字列リソースキー、タイムスパン有効期限)
- {
- var result = _factory.Value.CreateAsync(resourceKey, expiryTime) を待機します。
- 新しいLockResult(result)を返します。
- }
-
- パブリック非同期 Task<ILockResult> LockAsync(文字列リソースキー、有効期限の時間範囲、待機時間の時間範囲、再試行時間の時間範囲)
- {
- var result = await _factory.Value.CreateAsync(resourceKey、expiryTime、waitTime、retryTime);
- 新しいLockResult(result)を返します。
- }
-
- パブリック非同期 Task<ILockResult> LockAsync(文字列リソースキー、有効期限時間、待機時間時間、再試行時間時間、キャンセルトークンキャンセルトークン)
- {
- var result = await _factory.Value.CreateAsync(resourceKey、expiryTime、waitTime、retryTime、cancelationToken);
- 新しいLockResult(result)を返します。
- }
- }
-
- クラス LockResult: ILockResult
- {
- プライベート IRedisLock _lock;
- パブリックLockResult(IRedisLock redisLock)
- {
- _lock = redisLock;
- }
-
- パブリック文字列 LockId => _lock.LockId;
-
- パブリックブール IsAcquired => _lock.IsAcquired;
-
- 公共 int ExtendCount => _lock.ExtendCount;
-
- パブリックvoid Dispose()
- {
- _lock.Dispose();
- }
- }
https://github.com/samcook/RedLock.net https://github.com/StackExchange/StackExchange.Redis/ |