跨线程生成唯一的序列号



我需要从多个线程生成一个唯一的序列号。 我在下面创建了简单的类,它似乎有效,但我不确定我是否可以依赖序列号是唯一的。

此外,如果数字超过 999999,我需要能够让数字恢复为 0。 我不希望它会滚动很长时间,因为该方法每天可能被调用不到 100 次。 我知道系统在有机会达到999999之前会定期关闭进行维护。

GetSequenceNumber方法将从 BizTalk 映射中的 xslt 转换调用,并且可以同时多次调用该方法(在同一 BizTalk 主机实例中)。

在我的开发系统上,它似乎可以正常工作并生成不同的值,即使 BizTalk 同时多次调用该方法也是如此。 开发系统只有一个 BizTalk 主机实例正在运行。

但是,在生产系统上,有两台服务器。 我是否正确认为此方法无法保证跨服务器的唯一性,因为它们在不同的应用程序域中运行?

我不能使用 GUID,因为序列号限制为 16 个字符。

public class HelperMethods
{
private static int sequenceNumber = 0;
public string GetSequenceNumber()
{
string result = null;
int seqNo = Interlocked.Increment(ref sequenceNumber);
result = string.Format("{0:MMddHHmmss}{1:000000}", DateTime.Now, seqNo);
return result;
}
}

我想我可以使用服务器计算机名称并添加一些任意字符,这样即使一台服务器上生成的序列号与另一台服务器上生成的序列号相同,它仍然会有所不同,但我不确定它会有多独特。 像这样:

string seqNumber = (MachineName == "Blah" ? "A" : "B") + GetSequenceNumber();

有人对如何创建唯一的序列号有任何建议吗? 它不必是完全独特的,我只需要碰撞是非常不可能的。 另外,如果以线程安全的方式将数字重置为 0,我如何将数字重置回 1000000?

这应该可以很好地返回 16 个字符的唯一字符串。它基于一个独特的Guid。由于Guid在使用时转换为 24 个字符的字符串Convert.ToBase64String因此它使用 XOR 将字节折叠在自身上,以确保唯一性分布在所需的 16 个字符中。

Guid gd = Guid.NewGuid();
byte[] ba = gd.ToByteArray();
ba = ba.Zip(ba.Reverse(), (b0, b1) => (byte)(b0 ^ b1)).ToArray();
string mostLikelyUnique16 = Convert.ToBase64String(ba).Substring(0, 16);

我得到的结果像8QBIi7JpCeHhCWmy.

不能保证唯一性,但我认为它会相当不错,特别是考虑到您的要求允许偶尔发生碰撞。

我做了一个简单的测试,产生了 100 万个值,没有任何碰撞。

如果事先知道服务器数量并且不是很大,则可以将最高有效数字分配给"服务器ID",该ID在每个服务器的配置中设置。因此,例如,在 10 台服务器集中,其中一台只能处理 3000000-3999999 范围内的数字。对于一组 100 台服务器中的另一个实例,范围为 4200000-4299999。

最主要的是在配置中具有区分服务器的信息。

你在这里会遇到几个问题。

BizTalk 管道和业务流程在单独的应用程序域中运行,业务流程应用程序域有时会在业务流程的水化之间中断。 即使目前此映射仅在业务流程xor管道中运行,在某些时候有人可能会将其放在另一个(或另一个主机实例)上,并且您的解决方案会蓬勃发展。 当您开始在多服务器环境中运行任何东西时,请忘记它。

BizTalk 映射的底线是,它们通常不应以使用外部资源的任何方式不确定。 如果您只想生成 GUID(不确定),那没关系。 如果要跨多个映射生成序列,这不是一个好主意。 您必须依赖某些外部系统(它实际上应该是外部的,即使是您正在运行的 BizTalk 应用程序域才能真正正常工作 - 例如单一实例 WCF 服务或使用 SQL ServerSEQUENCE),这在映射中通常是一个坏主意(尤其是将在多个系统上并行执行的映射)。

那你能做什么呢?

  1. 选择另一种方法来标识已取消批处理的消息。 也许是批处理 ID 和单个 ID - 也许是两个 GUID(或者至少派生自 GUID 的东西,它仍然足以满足唯一性)。
  2. 在批处理之前或传出时,通过有序传递端口或单一实例业务流程(实际上是单一实例队列)对消息进行排序。 这有一些明显的缺点,但你可以用它获得一点创意 - 首先映射整个批次以对其进行排序,然后进行去批次;或者重新设计您的去批过程,以便在消息去批时在消息中构造一个序列;或者将所有内容放入 SQL (2012+) 表中,并带有一个根据您的要求循环的SEQUENCE,并将其与您的序列一起拉出。
  3. 重新考虑您的要求。 请求这样做的最终系统是什么? 为什么你认为你需要在地图上做这件事?

最新更新