处理 1,000,000 个 ID 的九位数字为我们提供了三位数字(我们需要其他六位数字作为 0-999999 的 ID)。
我假设您有一个多服务器设置。为每个服务器分配一个三位数的服务器 ID,然后您可以在每个服务器内分配唯一的 ID 值,而不必担心它们之间的重叠。它可以只是内存中不断增加的值,除了在 JVM 重新启动后仍然存在,我们需要将最近分配的值回显到磁盘(嗯,到任何你想要存储它的地方——本地磁盘、内存缓存等等)。
为了确保您不会在每个请求上遇到文件/任何 I/O 的开销,您在块中分配 ID,将块的端点回显到存储。
所以它最终是:
- 给每个服务器一个 ID
- 在服务器上存储当天最后分配的值(例如文件)
- 让 ID 分配器在块中工作(一次 10 个 ID,100 个,等等)
- 分配块:
- 使用块中的 ID
- ID 将是,例如 12000000027 表示服务器 #12 分配的第 28 个 ID
- 当一天改变时(例如,午夜),扔掉你当前的块并为新的一天分配一个新的块
在伪代码中:
class IDAllocator {
Storage storage;
int nextId;
int idCount;
int blockSize;
long lastIdTime;
/**
* Creates an IDAllocator with the given server ID, backing storage,
* and block size.
*
* @param serverId the ID of the server (e.g., 12)
* @param s the backing storage to use
* @param size the block size to use
* @throws SomeException if something goes wrong
*/
IDAllocator(int serverId, Storage s, int size)
throws SomeException {
// Remember our info
this.serverId = serverId * 1000000; // Add a million to make life easy
this.storage = s;
this.nextId = 0;
this.idCount = 0;
this.blockSize = bs;
this.lastIdTime = this.getDayMilliseconds();
// Get the first block. If you like and depending on
// what container this code is running in, you could
// spin this out to a separate thread.
this.getBlock();
}
public synchronized int getNextId()
throws SomeException {
int id;
// If we're out of IDs, or if the day has changed, get a new block
if (idCount == 0 || this.lastIdTime < this.getDayMilliseconds()) {
this.getBlock();
}
// Alloc from the block
id = this.nextId;
--this.idCount;
++this.nextId;
// If you wanted (and depending on what container this
// code is running in), you could proactively retrieve
// the next block here if you were getting low...
// Return the ID
return id + this.serverId;
}
protected long getDayMilliseconds() {
return System.currentTimeMillis() % 86400000;
}
protected void getBlock()
throws SomeException {
int id;
synchronized (this) {
synchronized (this.storage.syncRoot()) {
id = this.storage.readIntFromStorage();
this.storage.writeIntToStroage(id + blocksize);
}
this.nextId = id;
this.idCount = blocksize;
}
}
}
...但同样,这是伪代码,您可能希望在其中投入一些主动的东西,这样您就不会在需要 ID 时阻塞 I/O 等待 ID。
以上是假设您已经拥有某种应用程序范围的单例,并且该IDAllocator
实例只是该单个实例中的数据成员。如果没有,您可以轻松地将上述设置为单例,方法是为其提供经典getInstance
方法并让它从环境中读取其配置,而不是将其作为构造函数的参数接收。