1

ファイルに書き込む必要があるメッセージを継続的に受信するという要件があります。新しいメッセージを受信するたびに、別のファイルに書き込む必要があります。私が望むのは、ファイル名として使用される一意の識別子を生成することです。また、メッセージの順序も維持したいと考えています。つまり、ファイル名として生成される識別子は常にインクリメンタルでなければなりません。

UUID.randomUUID()を使用してファイル名を生成していましたが、このアプローチの問題は、UUID が識別子のランダム性のみを保証し、インクリメンタルではないことです。その結果、ファイルの順序が失われています (最初に生成されたファイルをリストの最初に表示する必要があります)。

既知のアプローチ

  1. System.currentTimeMillis() を使用できますが、同時に複数のメッセージを受信できます。

2.もう1つのアプローチは、静的なlong値を実装し、ファイルが作成されるたびにそれを増やし、long値をファイル名として使用することです。しかし、私はこのアプローチについて確信が持てません。また、私の問題に対する適切な解決策ではないようです。これよりもはるかに優れたソリューションがあると思います。

誰かがこの問題のより良い解決策を提案してくれれば、高く評価されます。

4

3 に答える 3

5

サーバーの再起動間でも id 値を均一に上昇させたい場合は、システム時間に基づいて値を設定するか、最後に使用された ID を保持する精巧で堅牢なロジックを用意する必要があります。堅牢性を単独で達成することは難しくありませんが、パフォーマンスとスケーラブルな方法でそれを達成することは難しいことに注意してください。

冗長サーバー クラスタ内の複数のノード間で ID を一意にする必要がある場合は、さらに複雑なロジックが必要になります。これには、すべてのボックスがアクセスを同期する永続ストアが確実に含まれます。もちろん、このパフォーマンスを実現することはさらに困難です。

私が見ることができる最良のオプションは、かなり長いIDを持つことです。これにより、これらの部分の余地があります。

  1. System.currentTimeMillis長期的な一意性 (再起動後)。
  2. System.nanotimeより細かい粒度のために;
  3. 各サーバー ノードの一意の ID (プラットフォーム固有の方法で決定されます)。

メソッドは、生成された最後の値を記憶し、重複の場合は再試行する必要があります。nanoTimeただし、次のクロック ティックまで何度も再試行する必要はありません。

ポイント 3 のないコードのスケッチ (単一ノードの実装):

private static long lastNanos;
public static synchronized String uniqueId() {
  for (;/*ever*/;) {
    final long n = System.nanoTime();
    if (n == lastNanos) continue;
    lastNanos = n;
    return "" + System.currentTimeMillis() + n;
  }
}
于 2012-10-16T09:10:47.157 に答える
0

同じ要件があり、適切な解決策を見つけました。Twitter Snowflake は、単純なアルゴリズムを使用して、並べ替え可能な 64 ビット (長い) idを生成します。Snowflake は Scala で書かれていますが、アプローチは単純で、Java コードで簡単に使用できます。

id は以下で構成されます: タイムスタンプ- 41 ビット (ミリ秒の精度でカスタム エポックを指定すると 69 年になります)。 マシン ID - 10 ビット (MAC アドレスはハードウェア ID として使用できます)。 シーケンス番号- 12 ビット - マシンごとに 4096 ごとにロールオーバー (同じミリ秒でロールオーバーを回避するための保護あり)

数式は次のようになります。((timestamp - customEpoch) << timestampShift) | (machineId << machineIdShift) | sequenceNumber;

各コンポーネントのシフトは、ID のビット位置によって異なります。

詳細な説明とソース コードは、github で見つけることができます。

ツイッタースノーフレーク

Snowflake アルゴリズムの基本的な Java 実装

于 2013-01-30T08:49:26.653 に答える
0

よし、両手を上げて。私の最後の答えはかなり不安定だったので、削除しました。

このサイトの精神に沿って、別の方法を試してみようと思いました。

これらのメッセージを 1 つのファイルに保存している場合は、ファイルのサイズから一意の ID を作成するなどの方法を試すことができますか?

メッセージをファイルに書き込む前は、その id がファイルの現在のサイズである可能性があります。

これらのメッセージを多数のファイルで一意にする必要がある場合は、ファイル名 + サイズを ID として追加できます。

同期のアツアツは別の日に譲ります。しかし、これらすべてを、物事を追跡する同期化されたオブジェクトにまとめることができます。

また、ファイルに書き込まれたメッセージは将来削除されないと想定しています。

追加の注意: 構築時に (または create メソッドを介して) ファイルを開くメッセージ処理オブジェクトを作成できます。このオブジェクトはファイルの初期サイズを取得し、これが一意の ID として使用されます。各メッセージが (同期された方法で) 追加されると、id はメッセージのサイズだけインクリメントされます。これにより、パフォーマンスの問題が解決されます。複数の JVM/Node が同じファイルにアクセスする場合は機能しません。

スケルトンのアイデア:

public class MessageSink {
    private long id = 0;

    public MessageSink(String filename) {
       id = ... get file size ..
    }

    public synchronized addMessage(Message msg) {
       msg.setId(id);
       .. write to file + flush ..
       .. or add to stack of messages that need to be written to file
       .. at a later stage.
       id = id + msg.getSize();
    }

    public void flushMessages() {
       .. open file
       .. for each message in stack write ...
       .. flush and close file
    }
}
于 2012-10-16T10:19:05.423 に答える