开箱即用轻量级雪花算法id生成器Java工具类

这篇具有很好参考价值的文章主要介绍了开箱即用轻量级雪花算法id生成器Java工具类。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.1 背景

在 Java后端研发过程中,对于分布式微服务来说,一般需要分布式 id生成.

这里分享一个非常好用且大多数情况下都可用的开箱即用轻量级雪花算法id生成器Java工具类。

这种方式生成的雪花算法生成器生成的唯一主键id,好处是不依赖第三方组件,轻量级,缺点是服务器的时钟不可以回拨,否则可能会造成生成的主键id冲突。

1.2 雪花算法id生成器Java工具类

雪花算法id生成器Java工具类源码

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
 
/**
 * id自增器(雪花算法)
 * @author renjie
 * @version 1.0.0
 */
public class SnowFlakeUtils {
    private final static long twepoch = 12888349746579L;
    /**
     * 机器标识位数
     */
    private final static long workerIdBits = 5L;
    /**
     * 数据中心标识位数
     */
    private final static long datacenterIdBits = 5L;
    /**
     * 毫秒内自增位数
     */
    private final static long sequenceBits = 12L;
    /**
     * 机器ID偏左移12位
     */
    private final static long workerIdShift = sequenceBits;
    /**
     * 数据中心ID左移17位
     */
    private final static long datacenterIdShift = sequenceBits + workerIdBits;
    /**
     * 时间毫秒左移22位
     */
    private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    /**
     * sequence掩码,确保sequnce不会超出上限
     */
    private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
    /**
     * 上次时间戳
     */
    private static long lastTimestamp = -1L;
    /**
     * 序列
     */
    private long sequence = 0L;
    /**
     * 服务器ID
     */
    private long workerId = 1L;
    private static long workerMask = -1L ^ (-1L << workerIdBits);
    /**
     * 进程编码
     */
    private long processId = 1L;
    private static long processMask = -1L ^ (-1L << datacenterIdBits);
 
    private static SnowFlakeUtils snowFlakeUtils = null;
 
    static{
        snowFlakeUtils = new SnowFlakeUtils();
    }
    public static synchronized long nextId(){
        return snowFlakeUtils.getNextId();
    }
 
    private SnowFlakeUtils() {
 
        //获取机器编码
        this.workerId=this.getMachineNum();
        //获取进程编码
        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
        this.processId=Long.valueOf(runtimeMXBean.getName().split("@")[0]).longValue();
 
        //避免编码超出最大值
        this.workerId=workerId & workerMask;
        this.processId=processId & processMask;
    }
 
    public synchronized long getNextId() {
        //获取时间戳
        long timestamp = timeGen();
        //如果时间戳小于上次时间戳则报错
        if (timestamp < lastTimestamp) {
            try {
                throw new Exception("Clock moved backwards.  Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //如果时间戳与上次时间戳相同
        if (lastTimestamp == timestamp) {
            // 当前毫秒内,则+1,与sequenceMask确保sequence不会超出上限
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 当前毫秒内计数满了,则等待下一秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }
        lastTimestamp = timestamp;
        // ID偏移组合生成最终的ID,并返回ID
        long nextId = ((timestamp - twepoch) << timestampLeftShift) | (processId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
        return nextId;
    }
 
    /**
     * 再次获取时间戳直到获取的时间戳与现有的不同
     * @param lastTimestamp
     * @return 下一个时间戳
     */
    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }
 
    private long timeGen() {
        return System.currentTimeMillis();
    }
 
    /**
     * 获取机器编码
     * @return
     */
    private long getMachineNum(){
        long machinePiece;
        StringBuilder sb = new StringBuilder();
        Enumeration<NetworkInterface> e = null;
        try {
            e = NetworkInterface.getNetworkInterfaces();
        } catch (SocketException e1) {
            e1.printStackTrace();
        }
        while (e.hasMoreElements()) {
            NetworkInterface ni = e.nextElement();
            sb.append(ni.toString());
        }
        machinePiece = sb.toString().hashCode();
        return machinePiece;
    }
}

注意事项
这个雪花算法实现是一个常见的分布式唯一ID生成器,但它也有一些潜在的问题和优化空间:文章来源地址https://www.toymoban.com/news/detail-729200.html

  • 唯一性保证
    • 该算法的唯一性依赖于每个节点的机器ID(workerId)和数据中心ID(processId)。如果这些ID在不同节点上配置不正确,可能导致ID冲突。
    • 唯一性还依赖于时钟同步。如果时钟回拨(Clock Drift)或不稳定,可能会导致生成的ID不唯一。
  • 时钟回拨问题:
    • 该算法没有显式处理时钟回拨情况。如果系统时钟发生回拨,可能会导致生成的ID不连续,甚至出现重复。
    • 优化:您可以考虑在时钟回拨检测到时采取适当的措施,例如等待或重新生成ID。
  • 长期运行问题
    • 如果您的应用长期运行,可能会达到每毫秒生成的ID数量的限制,尤其是在高并发情况下。
    • 优化:可以根据实际需求调整位数,以提高每毫秒生成ID的上限。
  • 可扩展性问题
    • 该算法中的位数分配不一定适用于所有应用。如果您的应用需要更多的数据中心或机器节点,需要重新分配位数。
    • 优化:可以根据需求增加位数分配的灵活性,以支持更多的数据中心或机器。
  • 不适用于移动设备:
    • 该算法依赖于机器ID和数据中心ID的配置,对于移动设备等无法确定唯一ID的环境不适用。
    • 优化:可以考虑在这种情况下使用其他唯一ID生成策略。
  • 异常处理
    • 该算法在出现异常情况时没有提供良好的处理机制,例如时钟回拨异常。
    • 优化:添加适当的异常处理,以确保生成的ID在异常情况下也是正确的。
  • 总的来说,这个雪花算法实现在大多数情况下都能正常工作,但在特定情况下可能会出现问题。
  • 优化的策略会根据应用的具体需求而变化,可以根据需要进行调整,以确保生成的ID满足唯一性、连续性和性能要求。
  • 如果您需要更高级的解决方案,还可以考虑使用分布式ID生成服务或其他专用工具。

到了这里,关于开箱即用轻量级雪花算法id生成器Java工具类的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 图像分类】【深度学习】【轻量级网络】【Pytorch版本】EfficientNet_V2模型算法详解

    EfficientNet_V2是由谷歌公司的Tan, Mingxing等人《EfficientNetV2: Smaller Models and Faster Training【 ICML-2021】》【论文地址】一文中提出的改进模型,在EfficientNet_V1的基础上,引入渐进式学习策略、自适应正则强度调整机制使得训练更快,进一步关注模型的推理速度与训练速度。 随着模型

    2024年01月25日
    浏览(39)
  • 【图像分类】【深度学习】【轻量级网络】【Pytorch版本】ShuffleNet_V1模型算法详解

    ShuffleNet_V1是由旷视科技的Zhang, Xiangyu等人在《ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices【CVPR-2018】》【论文地址】一文中提出的模型,是一种采用了逐点组卷积和通道混洗的轻量级CNN网络,在保持精度的同时大大降低了计算成本。 一般的卷积是全通道卷

    2024年01月20日
    浏览(36)
  • 轻量级实时跟踪算法NanoTrack在瑞芯微RK3588上的部署以及使用

    文章目录 前言 一、模型转换 1.环境配置 2.模型解构 二、rk3588平台使用 1.模型初始化 2.推理 github: https://github.com/Try2ChangeX/NanoTrack_RK3588_python: python版本基于rk3588的NanoTrack,每秒可达120FPS 主要参考: SiamTrackers/NanoTrack at master · HonglinChu/SiamTrackers · GitHub GitHub - rockchip-linux/rknn-tool

    2024年02月13日
    浏览(47)
  • git轻量级服务器gogs、gitea,非轻量级gitbucket

    本文来源:git轻量级服务器gogs、gitea,非轻量级gitbucket, 或 gitcode/gogs,gitea.md 结论: gogs、gitea很相似 确实轻, gitbucket基于java 不轻, 这三者都不支持组织树(嵌套组织 nested group) 只能一层组织。 个人用,基于gogs、gitea,两层结构树 简易办法: 把用户当成第一层节点、该用户的

    2024年02月07日
    浏览(52)
  • 轻量灵动: 革新轻量级服务开发

    从 JDK 8 升级到 JDK 17 可以让你的应用程序受益于新的功能、性能改进和安全增强。下面是一些 JDK 8 升级到 JDK 17 的最佳实战: 1.1、确定升级的必要性:首先,你需要评估你的应用程序是否需要升级到 JDK 17。查看 JDK 17 的新特性、改进和修复的 bug,以确定它们对你的应用程序

    2024年02月07日
    浏览(35)
  • 轻量级 HTTP 请求组件

    Apache HttpClient 是著名的 HTTP 客户端请求工具——现在我们模拟它打造一套简单小巧的请求工具库, 封装 Java 类库里面的 HttpURLConnection 对象来完成日常的 HTTP 请求,诸如 GET、HEAD、POST 等等,并尝试应用 Java 8 函数式风格来制定 API。 组件源码在:https://gitee.com/sp42_admin/ajaxjs/tr

    2024年02月01日
    浏览(48)
  • C++轻量级单元测试框架

    单元测试是构建稳定、高质量的程序、服务或系统的必不可少的一环。通过单元测试,我们可以在开发过程中及时发现和修复代码中的问题,提高代码的质量和可维护性。同时,单元测试也可以帮助我们更好地理解代码的功能和实现细节,从而更好地进行代码重构和优化。

    2023年04月25日
    浏览(52)
  • 一种轻量级定时任务实现

    现在市面上有各式各样的分布式定时任务,每个都有其独特的特点,我们这边的项目因为一开始使用的是分布式开源调度框架TBSchedule,但是这个框架依赖ZK, 由于ZK的不稳定性和项目老旧无人维护 ,导致我们的定时任务会偶发出现异常,比如:任务停止、任务项丢失、任务不

    2024年02月14日
    浏览(27)
  • Tomcat轻量级服务器

    目录 1.常见系统架构  C-S架构 B-S架构 2.B-S架构系统的通信步骤 3.常见WEB服服务器软件 4.Tomcat服务器的配置 下载安装 环境变量配置 测试环境变量是否配置成功 测试Tomcat服务器是否配置成功  Tomcat窗口一闪而过的解决步骤 Tomcat解决乱码 介绍: C-S架构即Client/Server(客户端/服务

    2023年04月14日
    浏览(115)
  • Kotlin 轻量级Android开发

    Kotlin 是一门运行在 JVM 之上的语言。 它由 Jetbrains 创建,而 Jetbrains 则是诸多强大的工具(如知名的 Java IDE IntelliJ IDEA )背后的公司。 Kotlin 是一门非常简单的语言,其主要目标之一就是提供强大语言的同时又保持简单且精简的语法。 其主要特性如下所示: 轻量级:这一点对

    2024年02月07日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包