首页 > 编程语言 >java 分布式id生成工具类

java 分布式id生成工具类

时间:2023-09-06 16:02:34浏览次数:32  
标签:java private datacenterId long import id 分布式

import lombok.extern.slf4j.Slf4j;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 分布式高效有序ID生成
 *
 * <p>优化开源项目:https://gitee.com/yu120/sequence</p>
 */
@Slf4j
public final class IdGenerator {

    /**
     * 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
     */
    private static final long twepoch = 1288834974657L;
    /**
     * 机器标识位数
     */
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    private final long maxWorkerId = ~(-1L << workerIdBits);
    private final long maxDatacenterId = ~(-1L << datacenterIdBits);
    /**
     * 毫秒内自增位
     */
    private final long sequenceBits = 12L;
    private final long workerIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    /**
     * 时间戳左移动位
     */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private final long sequenceMask = ~(-1L << sequenceBits);

    private final long workerId;

    /**
     * 数据标识 ID 部分
     */
    private final long datacenterId;
    /**
     * 并发控制
     */
    private long sequence = 0L;
    /**
     * 上次生产 ID 时间戳
     */
    private long lastTimestamp = -1L;
    /**
     * IP 地址
     */
    private InetAddress inetAddress;

    public IdGenerator() {
        this.inetAddress = null;
        this.datacenterId = getDatacenterId(maxDatacenterId);
        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
    }

    public IdGenerator(InetAddress inetAddress) {
        this.inetAddress = inetAddress;
        this.datacenterId = getDatacenterId(maxDatacenterId);
        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
    }

    /**
     * 有参构造器
     *
     * @param workerId     工作机器 ID
     * @param datacenterId 序列号
     */
    public IdGenerator(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    /**
     * 获取 maxWorkerId
     */
    private long getMaxWorkerId(long datacenterId, long maxWorkerId) {
        StringBuilder mpid = new StringBuilder();
        mpid.append(datacenterId);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (name != null && name.length() > 0) {
            // 获取jvm进程id
            mpid.append(name.split("@")[0]);
        }
        /*
         * MAC + PID 的 hashcode 获取16个低位
         */
        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * 数据标识id部分
     */
    private long getDatacenterId(long maxDatacenterId) {
        long id = 0L;
        try {
            if (null == this.inetAddress) {
                this.inetAddress = InetAddress.getLocalHost();
            }
            NetworkInterface network = NetworkInterface.getByInetAddress(this.inetAddress);
            if (null == network) {
                id = 1L;
            } else {
                byte[] mac = network.getHardwareAddress();
                if (null != mac) {
                    id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
                    id = id % (maxDatacenterId + 1);
                }
            }
        } catch (SocketException | UnknownHostException e) {
            log.warn(" getDatacenterId: ", e);
        }
        return id;
    }

    /**
     * 获取下一个 ID
     *
     * @return 下一个 ID
     */
    public synchronized long nextId() {
        long timestamp = timeGen();
        //闰秒
        if (timestamp < lastTimestamp) {
            long offset = lastTimestamp - timestamp;
            if (offset <= 5) {
                try {
                    wait(offset << 1);
                    timestamp = timeGen();
                    if (timestamp < lastTimestamp) {
                        throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            } else {
                throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
            }
        }

        if (lastTimestamp == timestamp) {
            // 相同毫秒内,序列号自增
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 同一毫秒的序列数已经达到最大
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 不同毫秒内,序列号置为 1 - 2 随机数
            sequence = ThreadLocalRandom.current().nextLong(1, 3);
        }

        lastTimestamp = timestamp;

        // 时间戳部分 | 数据中心部分 | 机器标识部分 | 序列号部分
        return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * 反解id的时间戳部分
     */
    public static long parseIdTimestamp(long id) {
        return (id >> 22) + twepoch;
    }

}


标签:java,private,datacenterId,long,import,id,分布式
From: https://blog.51cto.com/u_14583021/7387854

相关文章

  • 无涯教程-JavaScript - SECOND函数
    描述SECOND函数返回时间值的秒数。第二个数字以0(零)到59之间的整数形式给出。语法SECOND(serial_number)争论Argument描述Required/OptionalSerial_number您想找到包含秒数的时间。时间可以输入为-引号内的文本字符串(如"6:45PM")十进制数(如0.78125,代表......
  • Java获取请求者的真实ip地址
    在Java中,可以通过HttpServletRequest对象的getRemoteAddr()方法获取请求者的真实IP地址。以下是一个简单的示例:importjavax.servlet.http.HttpServletRequest;publicStringgetRealIpAddress(HttpServletRequestrequest){StringipAddress=request.getHeader("X-Fo......
  • Revit API创建几何实体Solid并找到与之相交的元素
    几何实体的创建方法之一:构成封闭底面,指定拉伸方向与拉伸高度。GeometryCreationUtilities//自创几何实体相交法[TransactionAttribute(Autodesk.Revit.Attributes.TransactionMode.Manual)]publicclassFindIntersectWallsByGeometry:IExternalCommand{publicResult......
  • .NET 序列化生成 JavaScriptSerializer Poc
    dot.NET安全矩阵星球群有位师傅问起如何才能生成和ysoserial一样的JavaScriptSerializer序列化poc,同Json.NET一样序列化使用了ObjectDataProvider类,ObjectInstance属性绑定实例化的Process对象,这里没有使用MethodParameters属性传递参数,而是使用ProcessStartInfo类FileName和Argum......
  • Java 与 Jvm
    JVM是跨语言的平台我们平常所说的Java字节码指的是使用Java语言编写的程序,通过Java编译器编译而成的字节码文件,但是,Java虚拟机根本不关心运行在其内部的程序是何种语言编写的,它只关心字节码文件,也就是说Java虚拟机拥有语言无关性,并不会单纯的与Java语言终身绑定......
  • 《Java架构师的第一性原理》65系统架构之架构设计方法论
     4规范(Musthave)规范一:非数据服务做到无状态,避免同一集群内的节点间有功能差异;做到实例可以被随时停止、重启、增加,并且完全不依赖于本地磁盘或者内存规范二:服务具备优雅重启规范三:服务提供的API建议采用http\grpc,json\pb规范,不建议其他自定义格式规范四......
  • 另一个开源数据库管理工具HeidiSQL的使用及注意事项(轻量级替代navicate)
    1.此工具支持的数据库类型比较有限,主要有 MariaDB,MySQL,MicrosoftSQL,PostgreSQLandSQLite2.此工具占用资源较少,运行速度较快,当然缺点是功能也相对较少,但基本的建表查询等功能都有.3.可以与DBeaver进行互补,结合使用,比如其导出数据功能比DBeaver好用;创建表字段和......
  • ​​Android平台GB28181历史视音频文件下载规范探讨及技术实现
    技术背景上篇blog,我们提到了Android平台GB28181历史视音频文件检索规范探讨及技术实现,文件检索后,GB28181平台侧,可以针对文件列表进行回放或下载操作,本文主要探讨视音频文件下载相关。规范解读视音频文件下载基本要求SIP服务器接收到媒体接收者发送的视音频文件下载请求后向媒体流......
  • Android官方资料--Inside OTA Packages
    InsideOTAPackagesINTHISDOCUMENTEdifysyntaxBuilt-infunctionsThesystembuildstheupdaterbinaryfrombootable/recovery/updater andusesitinanOTApackage.Thepackageitselfisa.zipfile(ota_update.zip,incremental_ota_update.zip)that......
  • Android内存优化案例——不合适和高性能的写法(一)
    安卓内存优化是一个很重要的话题,有很多方面可以考虑,比如避免内存泄漏、减少内存抖动、优化图片加载、使用缓存和对象池等。下面我举一些代码案例,分别展示不合适的写法和高性能的写法。1.避免使用枚举类型。枚举类型会占用更多的内存,因为它是一个类对象,而不是一个基本类型。如果......