私は数日間これに取り組んできました、そして私はいくつかの解決策を見つけましたが、それらのどれも信じられないほど単純または軽量ではありません。問題は基本的にこれです。10台のマシンのクラスターがあり、それぞれがマルチスレッドESBプラットフォームで同じソフトウェアを実行しています。同じマシン上のスレッド間の並行性の問題はかなり簡単に処理できますが、異なるマシン上の同じデータの並行性はどうでしょうか?
基本的に、ソフトウェアはWebサービスを介して、あるビジネスから別のビジネスに顧客のデータをフィードする要求を受け取ります。ただし、顧客は他のシステムにまだ存在していてもいなくてもかまいません。そうでない場合は、Webサービスメソッドを使用して作成します。したがって、ある種のテストと設定が必要ですが、他のマシンが競合状態を引き起こさないようにロックするために、ある種のセマフォが必要です。以前、リモートの顧客が1人のローカルの顧客に対して2回作成される状況がありましたが、これはあまり望ましくありません。
概念的に試してみたソリューションは次のとおりです。
フォールトトレラントな共有ファイルシステムを使用して、お客様に応じて各マシンによってチェックされる「ロック」ファイルを作成します。
データベース内の特別なテーブルを使用し、テーブル全体をロックして、ロックレコードの「テストと設定」を行います。
スケーリングを支援するがハブアンドスポークモデルを使用するオープンソースサーバーソフトウェアであるTerracottaを使用します。
EHCacheを使用して、メモリ内の「ロック」の同期レプリケーションを行います。
このような問題を経験したのは私だけだとは思えません。どのように解決しましたか?社内で何かを調理したり、お気に入りのサードパーティ製品を持っていますか?
Hazelcast 分散ロックの使用を検討してください。超軽量で簡単。
Java.util.concurrent.locks.Lock lock = Hazelcast.getLock ("mymonitor");
lock.lock ();
try {
// do your stuff
}finally {
lock.unlock();
}
Hazelcast-分散キュー、マップ、セット、リスト、ロック
私たちはテラコッタを使っているので、投票したいと思います。
私はHazelcastをフォローしていて、別の有望なテクノロジーのように見えますが、使ったことがないので投票できません。聞いたところ、P2Pベースのシステムを使用していることがわかっているので、実際には大規模に信頼しません。スケーリングのニーズ。
しかし、Yahooから出てきて、Hadoopの傘の下で動いているZookeeperについても聞いたことがあります。いくつかの新しいテクノロジーを冒険的に試してみたい場合は、調整だけに重点を置いた非常に無駄のない平均的なものであるため、これは本当に大きな可能性を秘めています。私はビジョンと約束が好きですが、それでもまだ緑が多すぎるかもしれません。
Terracottaは「階層化」モデルに近いです。すべてのクライアントアプリケーションはTerracotta Server Arrayと通信します(さらに重要なのは、スケールについては互いに通信しないことです)。 Terracotta Server Arrayは、スケールと可用性の両方でクラスター化できます(ミラーリング、可用性、およびストライプ化、スケール)。
いずれにせよ、おそらくテラコッタは、POJO同期/待機/通知を使用するか、またはReentrantReadWriteLockなどのJava.util.concurrentプリミティブを使用することにより、単一のJVMで行うのと同じ方法で、クラスター全体で並行性を表現する機能を提供します、CyclicBarrier、AtomicLong、FutureTaskなど。
Terracotta Cookbook には、これらのプリミティブの使用法を示す簡単なレシピがたくさんあります。
例として、ReentrantReadWriteLockの例を投稿します(「テラコッタ」バージョンのロックはありません。通常のJava ReentrantReadWriteLockを使用するだけです)。
import Java.util.concurrent.locks.*;
public class Main
{
public static final Main instance = new Main();
private int counter = 0;
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(true);
public void read()
{
while (true) {
rwl.readLock().lock();
try {
System.out.println("Counter is " + counter);
} finally {
rwl.readLock().unlock();
}
try { Thread.currentThread().sleep(1000); } catch (InterruptedException ie) { }
}
}
public void write()
{
while (true) {
rwl.writeLock().lock();
try {
counter++;
System.out.println("Incrementing counter. Counter is " + counter);
} finally {
rwl.writeLock().unlock();
}
try { Thread.currentThread().sleep(3000); } catch (InterruptedException ie) { }
}
}
public static void main(String[] args)
{
if (args.length > 0) {
// args --> Writer
instance.write();
} else {
// no args --> Reader
instance.read();
}
}
}
Redisson を使用することをお勧めします。 Java.util.Lock
を含む30以上の分散データ構造とサービスを実装します。使用例:
Config config = new Config();
config.addAddress("some.server.com:8291");
Redisson redisson = Redisson.create(config);
Lock lock = redisson.getLock("anyLock");
lock.lock();
try {
...
} finally {
lock.unlock();
}
redisson.shutdown();
Memcachedを非常に高速で分散されたRAMログを保持するためのストレージとして使用することについてアドバイスをしましたが、EHCacheは同様のプロジェクトですが、Java中心です。
アトミック更新を使用することが確実である限り、どちらを使用してもかまいません(memcachedはそれらをサポートしていますが、EHCacheについては不明です)。これは、最もスケーラブルなソリューションです。
関連するデータポイントとして、GoogleはBigTableなどのいくつかのシステムのルートとして、RAMベースの高速分散ロックストレージである「Chubby」を使用しています。
コンテキスト全体を理解しているかどうかはわかりませんが、これをサポートする単一のデータベースがあるようです。データベースのロックを利用しないのはなぜですか?顧客の作成が単一のINSERTの場合、データベースは制約の1つに違反する2番目のINSERTを拒否するため(例:顧客名がユニークな例)。
「顧客の挿入」操作がアトミックではなく、ステートメントのバッチである場合は、最初のINSERTを導入(または使用)して、(必要なUNIQUEness制約を使用して)顧客を識別する簡単な基本レコードを作成し、すべての操作を行います。同じトランザクション内の他の挿入/更新。繰り返しになりますが、データベースは一貫性を処理し、同時に行われる変更はいずれかが失敗する原因となります。
私はCoherenceで多くの作業を行い、分散ロックを実装するためのいくつかのアプローチを可能にしました。素朴なアプローチは、参加しているすべてのノードで同じ論理オブジェクトをロックするように要求することでした。 Coherenceの用語では、これはレプリケートされたキャッシュのキーをロックすることでした。ノードを追加するとネットワークトラフィックが直線的に増加するため、このアプローチはそれほどうまく拡張しません。より賢明な方法は、分散キャッシュを使用することでした。クラスター内の各ノードはキー空間の一部を自然に担当するため、このようなキャッシュでキーをロックすると、常に最大で1つのノードとの通信が必要になります。このアイデアに基づいて独自のアプローチを展開することもできますが、Coherenceを利用することもできます。それは本当にあなたの夢のスケーラビリティツールキットです。
さらに、ネットワーク障害が発生した場合に適切に機能するためには、半ノード程度のマルチノードネットワークベースのロックメカニズムを合理的に洗練する必要があります。
分散ロックの場合は Cacheonix を検討することもできます。ここで述べた他のものとは異なり、CacheonixはReadWriteロックをサポートし、必要に応じて読み取りから書き込みへのロックエスカレーションを行います。
ReadWriteLock rwLock = Cacheonix.getInstance().getCluster().getReadWriteLock();
Lock lock = rwLock.getWriteLock();
try {
...
} finally {
lock.unlock();
}
完全な開示:私はCacheonix開発者です。
すでにデータベースに接続しているので、別のインフラストラクチャを追加する前に、 JdbcSemaphore を見てください。これは簡単に使用できます。
JdbcSemaphore semaphore = new JdbcSemaphore(ds, semName, maxReservations);
boolean acq = semaphore.acquire(acquire, 1, TimeUnit.MINUTES);
if (acq) {
// do stuff
semaphore.release();
} else {
throw new TimeoutException();
}
spf4j ライブラリの一部です。
ロックと解放という2つの方法で単純なRMIサービスを作成しました。どちらの方法もキーを受け取ります(私のデータモデルではUUIDをpkとして使用したため、ロックキーでもありました)。
RMIは一元化されているため、これに対する優れたソリューションです。 EJBを使用してこれを行うことはできません(特に、呼び出しがどのマシンに到達するかわからないため、クラスター内)。さらに、それは簡単です。
それは私のために働いた。
単一の顧客へのリクエストが常に同じサーバーにマッピングされるように負荷分散を設定できる場合は、ローカル同期を介してこれを処理できます。たとえば、顧客ID mod 10を使用して、10個のノードのどれを使用するかを見つけます。
一般的なケースでこれを実行したくない場合でも、ノードはこの特定のタイプのリクエストに対して互いにプロキシすることができます。
ユーザーが十分に統一されている場合(つまり、ユーザー数が多い場合)、1つのノードが過負荷になった場所でホットスポットがポップアップすることを期待しない場合でも、これはかなり適切にスケーリングするはずです。