小菜鸟

java菜鸟号正在起航

IP 地址存储方案:IPv4 与 IPv6 的高效处理方式

在开发中,IP 地址(尤其是用户访问日志、设备管理场景)需要高效存储和查询。直接存储字符串形式的 IP 不仅占用空间大,还不利于范围查询(如判断某个 IP 是否在指定网段内)。本文将详细讲解 IPv4 和 IPv6 地址的数字化存储方案,包括转换原理、代码实现及应用场景。

IPv4 地址的存储:使用 long 类型

IPv4 地址由 4 个 0-255 的整数组成(如 192.168.1.1),总长度为 32 位(4×8 位),刚好可以用一个 64 位的 long 类型存储。

转换原理

将 IPv4 地址的 4 个分段视为 32 位二进制数的 4 个字节,按从左到右的顺序拼接后转换为十进制整数。例如:

  • IP 地址 192.168.1.1 可拆分为 19216811
  • 转换为二进制:11000000.10101000.00000001.00000001
  • 拼接后为 32 位二进制数,转换为十进制即 3232235777

代码实现

public class IPv4Converter {  

    /**  
     * 将 IPv4 字符串转换为 long 类型  
     * @param ipString IPv4 地址(如 "192.168.1.1")  
     * @return 对应的 long 值,转换失败返回 -1  
     */  
    public static long ipToLong(String ipString) {  
        // 拆分 IP 地址为 4 个分段  
        String[] segments = ipString.split("\\.");  
        if (segments.length != 4) {  
            return -1; // 格式错误  
        }  

        long result = 0;  
        try {  
            // 每个分段左移相应位数后累加(第一段占最高 8 位,第四段占最低 8 位)  
            result += Long.parseLong(segments[0]) << 24;  // 第一段:左移 24 位  
            result += Long.parseLong(segments[1]) << 16;  // 第二段:左移 16 位  
            result += Long.parseLong(segments[2]) << 8;   // 第三段:左移 8 位  
            result += Long.parseLong(segments[3]);        // 第四段:不左移  
            return result;  
        } catch (NumberFormatException e) {  
            return -1; // 分段包含非数字  
        }  
    }  

    /**  
     * 将 long 类型转换为 IPv4 字符串  
     * @param ipLong 用 long 存储的 IPv4 地址  
     * @return 对应的 IPv4 字符串  
     */  
    public static String longToIp(long ipLong) {  
        // 从 long 中提取 4 个分段(通过右移和与运算)  
        StringBuilder ip = new StringBuilder();  
        ip.append((ipLong >>> 24)).append(".");                // 提取最高 8 位  
        ip.append((ipLong >>> 16) & 0xFF).append(".");         // 提取次高 8 位(&0xFF 确保只取 8 位)  
        ip.append((ipLong >>> 8) & 0xFF).append(".");          // 提取次低 8 位  
        ip.append(ipLong & 0xFF);                              // 提取最低 8 位  
        return ip.toString();  
    }  

    // 测试  
    public static void main(String[] args) {  
        String ip = "255.255.255.255";  
        long ipLong = ipToLong(ip);  
        System.out.println(ipLong); // 输出:4294967295  
        System.out.println(longToIp(ipLong)); // 输出:255.255.255.255  
    }  
}

优势与应用

阅读全文 »

Elasticsearch 集群发现机制:Zen Discovery 详解

Elasticsearch 集群的节点发现机制是确保节点能够自动识别集群成员、选举主节点并维持集群一致性的核心模块。默认采用 Zen Discovery 机制,支持单播(Unicast)和多播(Multicast)两种方式,其中单播是生产环境的推荐配置。

Zen Discovery 核心功能

Zen Discovery 负责集群的以下关键流程:

  1. 节点发现:新节点加入集群时,通过预配置的节点列表找到集群中的现有节点。
  2. 主节点选举:当集群无主节点或主节点故障时,自动选举新的主节点。
  3. 集群状态同步:主节点与其他节点同步集群元数据(如索引、分片分布)。

节点发现方式

单播(Unicast):生产环境首选

单播是通过预先配置的节点列表(种子节点)进行节点发现,避免多播的网络广播开销和安全风险,是 Elasticsearch 的默认推荐方式。

配置方式

elasticsearch.yml 中指定种子节点列表:

discovery.zen.ping.unicast.hosts:
  - "node1.example.com:9300"  # 节点1的地址和Transport端口(默认9300)
  - "node2.example.com:9300"  # 节点2的地址和端口
  - "192.168.1.100:9300"      # 可直接使用IP
工作原理
  1. 新节点启动时,向 discovery.zen.ping.unicast.hosts 中的种子节点发送 ping 请求。
  2. 种子节点回应集群中的其他节点信息,新节点据此加入集群。
  3. 无需配置所有节点,只需包含集群中少量节点(如主节点候选节点),新节点会自动发现其他成员。

多播(Multicast):不推荐用于生产

多播通过网络广播寻找集群节点,无需预先配置节点列表,但存在以下问题:

  • 网络广播占用带宽,不适合大规模集群。
  • 存在安全风险(非集群节点可能收到广播)。
  • 部分网络环境(如云服务)禁用多播。
配置方式(已过时,6.x 后逐步移除)
discovery.zen.ping.multicast.enabled: true  # 启用多播(默认关闭)
discovery.zen.ping.multicast.group: "224.2.2.4"  # 多播组地址
discovery.zen.ping.multicast.port: 54328  # 多播端口

核心配置参数

节点发现与 ping 配置

  • discovery.zen.ping_timeout:节点间 ping 超时时间(默认 3s)。网络较慢时可增大(如 10s),避免误判节点故障。
  • discovery.zen.join_timeout:节点加入集群的超时时间(默认 60s),为 ping_timeout 的 20 倍。
  • discovery.zen.ping.unicast.concurrent_connects:单播时的最大并发连接数(默认 10),节点较多时可适当增大。

主节点选举与脑裂防护

  • discovery.zen.minimum_master_nodes:防止脑裂(集群分裂为多个子集群)的关键参数,需设置为 (主节点候选数 / 2) + 1

    例如:

    • 3 个主节点候选时,值为 2;
    • 5 个主节点候选时,值为 3。
      确保只有超过半数的主节点候选同意,才能选举新主节点。
  • discovery.zen.no_master_block:无主节点时的阻塞策略(默认 write):

    • write:阻塞写入操作,允许读取;
    • all:阻塞所有操作。

故障检测

  • discovery.zen.fd.ping_interval:主节点检测其他节点的频率(默认 1s)。
  • discovery.zen.fd.ping_retries:ping 失败重试次数(默认 3 次)。
  • discovery.zen.fd.ping_timeout:ping 超时时间(默认 30s),超时则认为节点故障。

节点加入集群的流程

  1. 新节点启动:读取 elasticsearch.yml 中的 cluster.name 和单播种子节点。
  2. 发送 ping 请求:向种子节点发送 ping,获取集群现有节点列表。
  3. 加入集群:与主节点通信,验证集群名称是否一致,成功后加入集群。
  4. 分片分配:主节点根据集群状态,为新节点分配分片(如副本分片)。

最佳实践

  1. 单播优先:生产环境禁用多播,仅使用单播配置种子节点(建议包含所有主节点候选)。
  2. 脑裂防护:严格设置 discovery.zen.minimum_master_nodes,主节点候选数建议为奇数(3、5 个)。
  3. 超时参数调优:在网络不稳定的环境(如跨机房),增大 ping_timeoutfd.ping_timeout,避免节点误判。
  4. 种子节点冗余:种子节点列表至少包含 2 个节点,防止单节点故障导致新节点无法加入。

Elasticsearch 集群主节点(Master)选举机制详解

主节点(Master)是 Elasticsearch 集群的核心管理者,负责维护集群元数据(如索引创建 / 删除、分片分配)、协调节点加入 / 退出等。主节点的选举机制直接影响集群的稳定性,尤其是在节点故障或网络波动时,合理的选举配置可避免 “脑裂” 等严重问题。

主节点选举的触发场景

主节点选举在以下情况触发:

  1. 集群首次启动:无主节点时,所有候选主节点(node.master: true)参与选举。
  2. 现有主节点故障:节点检测到主节点失联(如网络中断、进程崩溃),触发重新选举。
  3. 主节点主动退出:主节点因负载过高或配置变更主动下线。

选举核心流程

候选主节点资格

只有配置 node.master: true 的节点才有资格参与选举(主节点候选节点),其他节点(如纯数据节点)仅参与投票,不竞选主节点。

选举步骤

  1. Ping 阶段:候选节点通过 Zen Discovery 机制互相发送 ping 请求,交换集群状态和节点信息。
  2. 投票阶段:候选节点根据节点 ID、集群状态版本等信息,向 “最优” 候选节点投票(通常优先选择现有集群状态最新的节点)。
  3. 确认主节点:当某个候选节点获得 超过 discovery.zen.minimum_master_nodes 数量的投票时,成为新主节点。
  4. 集群同步:新主节点与其他节点同步集群元数据,确保全集群状态一致。

关键配置参数解析

discovery.zen.ping_timeout

  • 作用:控制候选节点间 ping 请求的超时时间(默认 3s)。

  • 场景:网络延迟较高时(如跨机房集群),需调大该值(如 10s~30s),避免因短暂网络波动误判节点故障,导致不必要的重新选举。

  • 配置示例:

    discovery.zen.ping_timeout: 10s

阅读全文 »

数据结构的四大存储方式:特性、适用场景与性能对比

数据结构的存储方式直接影响其操作效率(如增删查改),不同存储方式适用于不同的数据结构和业务场景。常见的存储方式包括顺序存储、链式存储、索引存储和散列存储,它们各有优劣,本文详细解析其原理、特性及适用场景。

顺序存储方式:连续空间的线性布局

核心原理

顺序存储将数据元素存放在连续的物理存储单元中,逻辑上的相邻元素在物理位置上也相邻,通过元素的物理位置关系体现逻辑关系。
典型示例:数组(如 Java 的int[]、C++ 的std::array)。

关键特性

  • 存储结构:依赖一块连续的内存空间,需预先分配固定大小。
  • 访问效率:通过下标直接访问元素(arr[i]),时间复杂度 O(1)(随机访问能力)。
  • 增删效率:
    • 插入 / 删除中间元素时,需移动后续所有元素(如数组中间插入元素),时间复杂度 O(n)
    • 尾部插入 / 删除效率高(无需移动元素),时间复杂度 O(1)
  • 空间利用率:无额外存储开销(不存储指针 / 索引),但可能因预分配过大导致空间浪费。

适用场景

阅读全文 »

Dubbo 配置详解:服务提供者与消费者核心配置

Dubbo 的配置是实现服务注册、发现与远程调用的核心,通过 XML、注解或 API 等方式,可灵活定义服务提供者、消费者、注册中心等关键信息。本文基于 XML 配置方式,详细解析服务提供者与消费者的核心配置项及使用场景。

Dubbo 配置的核心原则

Dubbo 配置遵循 “约定优于配置” 原则,核心目标是:

  • 明确服务边界(提供者暴露哪些接口,消费者引用哪些接口);
  • 指定服务注册中心(服务地址的存储与发现);
  • 配置服务治理规则(如缓存、超时、负载均衡等)。

配置优先级:方法级配置 > 接口级配置 > 全局配置(更具体的配置会覆盖全局设置)。

服务提供者配置(Provider)

服务提供者的核心任务是暴露服务接口并注册到注册中心,供消费者发现和调用。以下是 XML 配置示例及详解:

1. 完整配置示例

<!-- 1. 定义应用名称(唯一标识提供者) -->
<dubbo:application name="provider01" />

<!-- 2. 配置注册中心(服务地址存储位置) -->
<dubbo:registry 
    address="zookeeper://localhost:2181"  <!-- 注册中心地址支持多注册中心逗号分隔-->
    timeout="3000"                       <!-- 注册中心连接超时时间(毫秒) -->
    username="admin"                     <!-- 注册中心认证用户名(若开启) -->
    password="123456"                    <!-- 注册中心认证密码 -->
/>

<!-- 3. 配置协议(通信方式) -->
<dubbo:protocol 
    name="dubbo"       <!-- 协议名称dubbo默认高性能二进制协议)、httphessian等 -->
    port="20880"       <!-- 服务端口(默认20880,-1表示随机端口) -->
    threads="200"      <!-- 服务处理线程池大小 -->
/>

<!-- 4. 定义服务实现类(Bean) -->
<bean id="demoService" class="com.zhanghe.study.dubbo_provider.service.DemoServiceImpl" />

<!-- 5. 暴露服务接口 -->
<dubbo:service 
    interface="com.zhanghe.study.service.DemoService"  <!-- 服务接口全类名必填-->
    ref="demoService"                                  <!-- 服务实现类的Bean ID(必填) -->
    version="1.0.0"                                    <!-- 服务版本(区分不同版本接口) -->
    group="user"                                       <!-- 服务分组(区分同一接口的不同实现) -->
    timeout="5000"                                     <!-- 接口级超时时间(毫秒) -->
    retries="2"                                        <!-- 失败重试次数(不包括第一次调用) -->
    loadbalance="roundrobin"                           <!-- 负载均衡策略:roundrobin(轮询)、random等 -->
    registry="N/A"                                     <!-- 不注册到注册中心(仅本地调用时使用) -->
/>

2. 核心配置项详解

阅读全文 »
0%