hutool 获取当前机器的所有 IP 地址列表

爱站 2024-12-31 32 0条评论
55Link友情链接交易平台
摘要: 在开发过程中,获取当前机器的IP地址是一个非常常见的需求。无论是应用部署时需要知道服务监听的地址,还是在开发测试中需要确认应用的访问地址,都需要可靠地获取当前机器的IP地址信息。J...

在开发过程中,获取当前机器的 IP 地址是一个非常常见的需求。无论是应用部署时需要知道服务监听的地址,还是在开发测试中需要确认应用的访问地址,都需要可靠地获取当前机器的 IP 地址信息。

Java 标准库中提供 InetAddress 类来获取 IP 地址,但使用起来还是比较繁琐的。而 Hutool 作为一个强大的 Java 工具集,其中就提供非常便捷的 IP 地址获取方法。接下来,我们就详细探讨一下如何使用 Hutool 获取当前机器的所有 IP 地址,以及如何区分 IPv4 和 IPv6 地址。

Hutool 提供 NetUtil.getLocalIpv4s() NetUtil.getLocalIpv6s() 两个方法,分别用于获取当前机器的所有 IPv4 和 IPv6 地址。我们可以将这两个方法的调用组合起来,就可以获取所有的 IP 地址。示例代码如下:

List<String> ipv4List = NetUtil.getLocalIpv4s();List<String> ipv6List = NetUtil.getLocalIpv6s();// 合并 IPv4 和 IPv6 地址List<String> allIpList = new ArrayList<>(ipv4List);allIpList.addAll(ipv6List);System.out.println("所有 IP 地址列表: " + allIpList);

通过这段代码,我们可以获取当前机器的所有 IP 地址,包括 IPv4 和 IPv6 地址。需要注意的是,机器没有配置 IPv6 地址,那么 NetUtil.getLocalIpv6s() 方法会返回一个空列表。

有时我们可能只需要获取特定类型的 IP 地址,比如只需要 IPv4 地址或只需要 IPv6 地址。这时,我们可以使用 Hutool 提供的 NetUtil.isIpv4(String ip) NetUtil.isIpv6(String ip) 方法来判断 IP 地址的类型。示例代码如下:

List<String> allIpList = NetUtil.getLocalIpAddress();List<String> ipv4List = new ArrayList<>();List<String> ipv6List = new ArrayList<>();for (String ip : allIpList) {if (NetUtil.isIpv4(ip)) {ipv4List.add(ip);} else if (NetUtil.isIpv6(ip)) {ipv6List.add(ip);}}System.out.println("IPv4 地址列表: " + ipv4List);System.out.println("IPv6 地址列表: " + ipv6List);

在这个例子中,我们首先使用 NetUtil.getLocalIpAddress() 方法获取所有的 IP 地址,遍历这个列表,使用 NetUtil.isIpv4() NetUtil.isIpv6() 方法判断每个 IP 地址的类型,并将它们分别添加到 IPv4 和 IPv6 地址列表中。这样我们就可以分别获取 IPv4 和 IPv6 地址。

需要注意的是,对于某些特殊的 IP 地址,比如 loopback 地址 (127.0.0.1 和 ::1),Hutool 也将其识别为合法的 IP 地址。您只需要获取可用于访问的 IP 地址,可以在遍历 IP 地址列表时,额外判断一下地址是否为 loopback 地址。

除获取 IP 地址列表,Hutool 还提供一些其他的 IP 地址相关方法,比如:

这些方法都可以帮助我们更好地处理 IP 地址相关的需求。

Hutool 为我们提供非常便捷的 IP 地址获取和处理方法。使用这些方法,我们可以轻松地获取当前机器的所有 IP 地址,并且还能区分 IPv4 和 IPv6 地址。Hutool 还提供其他一些有用的 IP 地址相关方法,如判断 IP 地址是否为内网 IP、获取公网 IP 等。这些方法大大简化 IP 地址处理的开发工作,提高开发效率。


雪花算法,什么情况下发生ID冲突?

分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。

有些时候我们希望能使用一种简单些的ID,并且希望ID能够按照时间有序生成。

什么是雪花算法

Snowflake中文的意思是雪花,所以常被称为雪花算法,是Twitter开源的分布式ID生成算法。

Twitter雪花算法生成后是一个64bit的long型的数值,组成部分引入了时间戳,基本保持了自增。

SnowFlake算法的优点:

高性能高可用:生成时不依赖于数据库,完全在内存中生成;

高吞吐:每秒钟能生成数百万的自增ID;

ID自增:存入数据库中,索引效率高。

SnowFlake算法的缺点:

依赖与系统时间的一致性,如果系统时间被回调,或者改变,可能会造成ID冲突或者重复。

雪花算法组成

snowflake结构如下图所示:

包含四个组成部分

不使用:1bit,最高位是符号位,0表示正,1表示负,固定为0。

时间戳:41bit,毫秒级的时间戳(41位的长度可以使用69年)。

标识位:5bit数据中心ID,5bit工作机器ID,两个标识位组合起来最多可以支持部署1024个节点。

序列号:12bit递增序列号,表示节点毫秒内生成重复,通过序列号表示唯一,12bit每毫秒可产生4096个ID。

通过序列号1毫秒可以产生4096个不重复ID,则1秒可以生成4096*1000=409wID。

默认的雪花算法是64bit,具体的长度可以自行配置。 如果希望运行更久,增加时间戳的位数;如果需要支持更多节点部署,增加标识位长度;如果并发很高,增加序列号位数。

总结:雪花算法并不是一成不变的,可以根据系统内具体场景进行定制。

雪花算法适用场景

因为雪花算法有序自增,保障了MySQL中B+Tree索引结构插入高性能。

所以,日常业务使用中,雪花算法更多是被应用在数据库的主键ID和业务关联主键。

雪花算法生成ID重复问题

假设:一个订单微服务,通过雪花算法生成ID,共部署三个节点,标识位一致。

此时有200并发,均匀散布三个节点,三个节点同一毫秒同一序列号下生成ID,那么就会产生重复ID。

通过上述假设场景,可以知道雪花算法生成ID冲突存在一定的前提条件:

服务通过集群的方式部署,其中部分机器标识位一致;

业务存在一定的并发量,没有并发量无法触发重复问题;

生成ID的时机:同一毫秒下的序列号一致。

标识位如何定义

如果能保证标识位不重复,那么雪花ID也不会重复。

通过上面的案例,知道了ID重复的必要条件。 如果要避免服务内产生重复的ID,那么就需要从标识位上动文章。

我们先看看开源框架中使用雪花算法,如何定义标识位。

Mybatis-Plusv3.4.2雪花算法实现类Sequence,提供了两种构造方法:无参构造,自动生成dataCenterId和workerId;有参构造,创建Sequence时明确指定标识位。

Hutoolv5.7.9参照了Mybatis-PlusdataCenterId和workerId生成方案,提供了默认实现。

一起看下Sequence的创建默认无参构造,如何生成dataCenterId和workerId:

publicstaticlonggetDataCenterId(longmaxDatacenterId){longid=1L;finalbyte[]mac=();if(null!=mac){id=((0xFF&(long)mac[-2])|(0x0000FF00&(((long)mac[-1])<<8)))>>6;id=id%(maxDatacenterId+1);}returnid;}

入参maxDatacenterId是一个固定值,代表数据中心ID最大值,默认值31。

为什么最大值要是31?因为5bit的二进制最大是,对应十进制数值31。

获取dataCenterId时存在两种情况,一种是网络接口为空,默认取1L;另一种不为空,通过Mac地址获取dataCenterId。

可以得知,dataCenterId的取值与Mac地址有关。

接下来再看看workerId:

publicstaticlonggetWorkerId(longdatacenterId,longmaxWorkerId){finalStringBuildermpid=newStringBuilder();(datacenterId);try{(());}catch(UtilExceptionigonre){//ignore}return(()()&0xffff)%(maxWorkerId+1);}

入参maxWorkderId也是一个固定值,代表工作机器ID最大值,默认值31;datacenterId取自上述的getDatacenterId方法。

name变量值为PID@IP,所以name需要根据@分割并获取下标0,得到PID。

通过MAC+PID的hashcode获取16个低位,进行运算,最终得到workerId。

分配标识位

Mybatis-Plus标识位的获取依赖Mac地址和进程PID,虽然能做到尽量不重复,但仍有小几率。

标识位如何定义才能不重复?有两种方案:预分配和动态分配。

预分配

应用上线前,统计当前服务的节点数,人工去申请标识位。

这种方案,没有代码开发量,在服务节点固定或者项目少可以使用,但是解决不了服务节点动态扩容性问题。

动态分配

通过将标识位存放在Redis、Zookeeper、MySQL等中间件,在服务启动的时候去请求标识位,请求后标识位更新为下一个可用的。

通过存放标识位,延伸出一个问题:雪花算法的ID是服务内唯一还是全局唯一。

以Redis举例,如果要做服务内唯一,存放标识位的Redis节点使用自己项目内的就可以;如果是全局唯一,所有使用雪花算法的应用,要用同一个Redis节点。

两者的区别仅是不同的服务间是否公用Redis。 如果没有全局唯一的需求,最好使ID服务内唯一,因为这样可以避免单点问题。

服务的节点数超过1024,则需要做额外的扩展;可以扩展10bit标识位,或者选择开源分布式ID框架。

动态分配实现方案

Redis存储一个Hash结构Key,包含两个键值对:dataCenterId和workerId。

在应用启动时,通过Lua脚本去Redis获取标识位。 dataCenterId和workerId的获取与自增在Lua脚本中完成,调用返回后就是可用的标示位。

具体Lua脚本逻辑如下:

第一个服务节点在获取时,Redis可能是没有snowflake_work_id_key这个Hash的,应该先判断Hash是否存在,不存在初始化Hash,dataCenterId、workerId初始化为0;

如果Hash已存在,判断dataCenterId、workerId是否等于最大值31,满足条件初始化dataCenterId、workerId设置为0返回;

dataCenterId和workerId的排列组合一共是1024,在进行分配时,先分配workerId;

判断workerId是否!=31,条件成立对workerId自增,并返回;如果workerId=31,自增dataCenterId并将workerId设置为0。

dataCenterId、workerId是一直向下推进的,总体形成一个环状。 通过Lua脚本的原子性,保证1024节点下的雪花算法生成不重复。 如果标识位等于1024,则从头开始继续循环推进。

开源分布式ID框架

Leaf和Uid都有实现雪花算法,Leaf额外提供了号段模式生成ID。

美团Leaf:网络Uid:雪花算法可以满足大部分场景,如无必要,不建议引入开源方案增加系统复杂度。

回顾总结

文章通过图文并茂的方式帮助读者梳理了一遍什么是雪花算法,以及如何解决雪花算法生成ID冲突的问题。

关于雪环算法生成ID冲突问题,文中给了一种方案:分配标示位;通过分配雪花算法的组成标识位,来达到默认1024节点下ID生成唯一。

可以去看Hutool或者Mybatis-Plus雪花算法的具体实现,帮助大家更好的理解。

雪花算法不是万能的,并不能适用于所有场景。 如果ID要求全局唯一并且服务节点超出1024节点,可以选择修改算法本身的组成,即扩展标识位,或者选择开源方案:LEAF、UID。

创作不易,文章看完有帮助,点关注支持一下,祝好。

公众号:龙台的技术笔记GitHub:acmenlt
文章版权及转载声明:

作者:爱站本文地址:http://www.awz.cc/post/10466.html发布于 2024-12-31
文章转载或复制请以超链接形式并注明出处爱网站

赞(0