雪花算法的机器id重复

通常雪花算法的机器id是根据网络ip地址计算得到的一个long类型值。从阿里的nacos扒下来的雪花算法实现:

    static {
        InetAddress address = NetUtil.getLocalAddress0();
        if (address == null) {
            throw new IllegalStateException("Cannot get LocalHost InetAddress, please check your network!");
        }
        byte[] ipAddressByteArray = address.getAddress();
        hostIp = address.getHostAddress();
        workerId = (((ipAddressByteArray[ipAddressByteArray.length - 2] & 0B11) << Byte.SIZE) + (
                ipAddressByteArray[ipAddressByteArray.length - 1] & 0xFF));
        log.info("snowflower workerId: {}", workerId);
    }

getLocalAddress0方法的实现 NetworkInterface的getInetAddress对象,返回的是Inet6Address

public static InetAddress getLocalAddress0() {
        InetAddress localAddress = null;


        // @since 2.7.6, choose the {@link NetworkInterface} first
        try {
            NetworkInterface networkInterface = findNetworkInterface();
            Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
            while (addresses.hasMoreElements()) {
                Optional<InetAddress> addressOp = toValidAddress(addresses.nextElement());
                if (addressOp.isPresent()) {
                    try {
                        if (addressOp.get().isReachable(100)) {
                            return addressOp.get();
                        }
                    } catch (IOException e) {
                        // ignore
                    }
                }
            }
        } catch (Throwable e) {
            log.warn("{}", e.getMessage(), e);
        }

        try {
            localAddress = InetAddress.getLocalHost();
            Optional<InetAddress> addressOp = toValidAddress(localAddress);
            if (addressOp.isPresent()) {
                return addressOp.get();
            }
        } catch (Throwable e) {
            log.warn("{}", e.getMessage(), e);
        }


        return localAddress;
    }

getValidNetworkInterfaces方法的实现:
返回53个主机上的网络接口,名字是lo eth1 eth2 … net0 net1 wlan0等等。这些ip地址都是ipv6的。NetworkInterface类。

    private static List<NetworkInterface> getValidNetworkInterfaces() throws SocketException {
        List<NetworkInterface> validNetworkInterfaces = new LinkedList<>();
        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
        while (interfaces.hasMoreElements()) {
            NetworkInterface networkInterface = interfaces.nextElement();
            validNetworkInterfaces.add(networkInterface);
        }
        return validNetworkInterfaces; 
    }

findNetworkInterface方法的实现:

public static NetworkInterface findNetworkInterface() {

        List<NetworkInterface> validNetworkInterfaces = Collections.emptyList();
        try {
            validNetworkInterfaces = getValidNetworkInterfaces();
        } catch (Throwable e) {
            log.warn("{}", e.getMessage(), e);
        }

        for (NetworkInterface networkInterface : validNetworkInterfaces) {
            Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
            while (addresses.hasMoreElements()) {
                Optional<InetAddress> addressOp = toValidAddress(addresses.nextElement());
                if (addressOp.isPresent()) {
                    try {
                        if (addressOp.get().isReachable(100)) {
                            return networkInterface;
                        }
                    } catch (IOException e) {
                        // ignore
                    }
                }
            }
        }

        return validNetworkInterfaces.get(0);
    }

上面的代码含义是获取主机的所有网络接口。然后从第一个网络接口开始,如果可访问(isReachable(100)),就将这个IP地址返回。相同的ip地址必然产生相同的机器id(workerId)

今天的坑是因为服务器不同,ipv4地址不同,但是ipv6地址相同。使用了ipv6地址生成的workerId,就必然相同了。影响了雪花算法。以及其它用workerid作分布式的业务。

因为只有测试环境的ipv6地址都相同;线上的不会相同,所以为了测试,在IDEA edit configuration里加上的-Djava.net.preferIPv4Stack=true,这时看到所有的接口用的ipv4。
例如lo的地址,不加这个option,是16个字节的0::1;加了就是4个字节的127.0.0.1

后期改进,可以先从文件读取workerId, 如果没有再生成,并保存到文件中。避免主机ip地址变更导致的workerid变更问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值