1

我有要求我不断收到需要写入文件的消息。每次收到新消息时,都需要将其写入单独的文件中。我想要的是生成一个唯一标识符以用作文件名。我也想保留消息的顺序。我的意思是,作为文件名生成的标识符应该始终是增量的。

我使用UUID.randomUUID()来生成文件名,但这种方法的问题是 UUID 只保证标识符的随机性,而不是增量。结果,我失去了文件的顺序(我希望首先生成的文件应该出现在列表的首位)。

已知方法

  1. 可以使用 System.currentTimeMillis() 但我可以同时接收多条消息。

2.另一种方法是实现静态长值并在要创建文件时将其递增,并将长值用作文件名。但我不确定这种方法。此外,这似乎不是解决我的问题的正确方法。我认为可能有比这个更好的解决方案。

如果有人可以建议我更好地解决这个问题,将不胜感激。

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

好的,我举手。我的最后一个答案相当不稳定,我已将其删除。

与网站的精神保持一致,我想我会尝试不同的战术。

如果您说您将这些消息保存在单个文件中,那么您可以尝试使用文件大小创建唯一 ID 之类的方法?

在将消息写入文件之前,它的 id 可能是文件的当前大小。

如果这些消息需要在多个文件中是唯一的,您可以添加文件名 + 大小作为 id。

我会把同步的烫手山芋留到另一天。但是您可以将所有这些都包装在一个同步对象中,以跟踪事物。

另外,我假设将来不会删除写入文件的任何消息。

附加说明:您可以创建一个消息处理对象,该对象在构造时(或通过创建方法)打开文件。该对象将获取文件的初始大小,并将其用作唯一 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 回答
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上找到:

推特雪花

雪花算法的基本 Java 实现

于 2013-01-30T08:49:26.653 回答