spring System.currentTimeMillis()竞争条件

gpfsuwkq  于 2023-06-21  发布在  Spring
关注(0)|答案(3)|浏览(119)

在Java Sping Boot 项目中使用System.currentTimeMillis()作为唯一标识符是否会导致竞争条件,即有人可以在同一毫秒内命中API?这是什么机会。
有没有办法生成一个特定长度的唯一标识符(16-18字符),因为GUID的长度是36个字符。

brvekthn

brvekthn1#

我不会称之为竞争条件,但在同一毫秒内收到两个请求是可能的。这几率有多大?这取决于如何使用它。如果我想每毫秒发出很多请求,我可以。如果我很小心地不去做,那也是可能的。
您可能想使用UUID.randomUUID()。大多数数据库都支持这些,甚至可以为您生成它们。它们不会明显更贵。
当然,您可以使用类似大小的随机数,并以不同的格式对其进行编码。GUID使用十六进制,与base64之类的东西相比,它需要很多字符。

gpnt7bae

gpnt7bae2#

据猜测,一旦有1000个并发用户,机会就得到了保证。
为什么是16个字符?似乎是武断的。你可以使用一个随机数生成器,并保持一个超时Map,并保持跟踪用户的方式,我猜?
但是为什么不使用Spring支持的cookie会话ID呢?

vq8itlhq

vq8itlhq3#

System.currentTimeMillis()的Javadoc说:
以毫秒为单位返回当前时间。请注意,虽然返回值的时间单位是毫秒,但值的粒度取决于底层操作系统,可能会更大。例如,许多操作系统以几十毫秒为单位来测量时间。
即使您具有毫秒级的粒度,冲突也会以一定的频率发生。例如,假设有10个用户在同一秒内访问API。特定请求发生冲突的概率为10 / 1000 = 1%。如果碰撞导致用户可见的故障,则可能高得无法容忍。
如果你想要像UUID.randomUUID这样的东西,只是更短,为什么不从中汲取灵感呢?下面是它的实现:

public static UUID randomUUID() {
        SecureRandom ng = Holder.numberGenerator;

        byte[] randomBytes = new byte[16];
        ng.nextBytes(randomBytes);
        randomBytes[6]  &= 0x0f;  /* clear version        */
        randomBytes[6]  |= 0x40;  /* set to version 4     */
        randomBytes[8]  &= 0x3f;  /* clear variant        */
        randomBytes[8]  |= (byte) 0x80;  /* set to IETF variant  */
        return new UUID(randomBytes);
    }

所以基本上,它只是在某个地方有一个SecureRandom,向它请求16个随机字节,将这些字节转换为UUID标准的形式。您不需要UUID标准,并且需要少于16个字节。为什么不这样做呢?

class MyRandomIdGenerator {
        static SecureRandom generator = new SecureRandom();

        static long nextId() {
            return generator.nextLong();
        }
    }

最长的long是十进制中的19个字符。您可以使用十六进制表示法将其减少到16,或者以8字节的二进制格式存储它。
如果负ID困扰您,您可以始终使用

return generator.nextLong() >>> 1;

相关问题