分布式id生成器

分布式高效ID生产黑科技(sequence)

package com.lynch.core.util; /**
 * Copyright (c) 2011-2020, hubin ([email protected]).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

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

/**
 * <p>
 * 分布式高效有序ID生产黑科技(sequence) <br>
 * 优化开源项目:http://git.oschina.net/yu120/sequence
 * </p>
 *
 * @author hubin
 * @date 2016-08-18
 */
public class Sequence {

    /* 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动) */
    private final long twepoch = 1288834974657L;
    private final long workerIdBits = 5L;/* 机器标识位数 */
    private final long datacenterIdBits = 5L;
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long maxDatacenterId = -1L ^ (-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 ^ (-1L << sequenceBits);

    private long workerId;

    /* 数据标识id部分 */
    private long datacenterId;
    private long sequence = 0L;/* 0,并发控制 */
    private long lastTimestamp = -1L;/* 上次生产id时间戳 */

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

    /**
     * @param workerId     工作机器ID
     * @param datacenterId 序列号
     */
    public Sequence(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            System.out.println(
                    String.format("worker Id can‘t be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            System.out.println(
                    String.format("datacenter Id can‘t be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    /**
     * <p>
     * 获取 maxWorkerId
     * </p>
     */
    protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
        StringBuilder mpid = new StringBuilder();
        mpid.append(datacenterId);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (name != null && "".equals(name)) {
            /*
             * GET jvmPid
             */
            mpid.append(name.split("@")[0]);
        }
        /*
         * MAC + PID 的 hashcode 获取16个低位
         */
        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * <p>
     * 数据标识id部分
     * </p>
     */
    protected static long getDatacenterId(long maxDatacenterId) {
        long id = 0L;
        try {
            InetAddress ip = InetAddress.getLocalHost();
            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
            if (network == null) {
                id = 1L;
            } else {
                byte[] mac = network.getHardwareAddress();
                if (null != mac) {
                    id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
                    id = id % (maxDatacenterId + 1);
                }
            }
        } catch (Exception e) {
            System.out.println(" getDatacenterId: " + e.getMessage());
        }
        return id;
    }

    /**
     * 获取下一个ID
     *
     * @return
     */
    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 - 3 随机数
            sequence = ThreadLocalRandom.current().nextLong(1, 3);
        }

        lastTimestamp = timestamp;

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

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

    protected long timeGen() {
        return SystemClock.now();
    }

    public static void main(String[] args) {
        Sequence s = new Sequence();
        System.out.println(s.nextId());
    }
}

原文地址:https://www.cnblogs.com/linjiqin/p/9588935.html

时间: 2024-08-02 21:36:31

分布式id生成器的相关文章

分布式ID生成器 zz

简介 这个是根据twitter的snowflake来写的.这里有中文的介绍. 如上图所示,一个64位ID,除了最左边的符号位不用(固定为0,以保证生成的ID都是正数),还剩余63位可用. 下面的代码与图中的位数分配略有不同,除了中间部分10bit工作机器id不变,时间戳和序列号的位数是可以根据自己的需求变化的,就是说,你可以把中间的工作机器ID往左挪一挪,或往右挪一挪. 代码 /// <summary> /// 64位ID生成器,最高位为符号位,始终为0,可用位数63. /// 实例编号占10

百度开源分布式id生成器uid-generator源码剖析

百度uid-generator源码 https://github.com/baidu/uid-generator snowflake算法 uid-generator是基于Twitter开源的snowflake算法实现的. snowflake将long的64位分为了3部分,时间戳.工作机器id和序列号,位数分配如下. 其中,时间戳部分的时间单位一般为毫秒.也就是说1台工作机器1毫秒可产生4096个id(2的12次方). 源码实现分析 与原始的snowflake算法不同,uid-generator支

业务系统需要什么样的ID生成器

业务系统需要什么样的ID生成器 ID 生成器在微博我们一直叫发号器,微博就是用这样的号来存储,而我微博里讨论的时候也都是以发号器为标签.它的主要目的确如平常大家理解的“为一个分布式系统的数据object产生一个唯一的标识”,但其实在一个真实的系统里可能也可以承担更多的作用.概括起来主要有以下几点: 唯一性 时间相关 粗略有序 可反解 可制造 下面我会分别讲每个作用后面的考虑和权衡,也会对比介绍一下业界已知的几种 ID 设计. 要唯一性,是否需要全局唯一? 说起全局唯一,通常大家都会在想到发号器服

zookeepeer ID生成器 (一)

目录 写在前面 1.1. ZK 的分布式命名服务 1.1.1. 分布式 ID 生成器的类型 UUID方案 1.1.2. ZK生成分布式ID 写在最后 疯狂创客圈 亿级流量 高并发IM 实战 系列 疯狂创客圈 Java 分布式聊天室[ 亿级流量]实战系列之 -25[ 博客园 总入口 ] 写在前面 ? 大家好,我是作者尼恩.目前和几个小伙伴一起,组织了一个高并发的实战社群[疯狂创客圈].正在开始高并发.亿级流程的 IM 聊天程序 学习和实战 ? 前面,已经完成一个高性能的 Java 聊天程序的四件大

我爱java系列之---【分布式ID生成解决方案:UUID、Redis生成id、snowflake】

唯一id生成方案: a. 使用UUID生成唯一主键: 优点: 全局唯一. 缺点: 因为生成的内容是字符串, 不能排序, 不能按照时间先后排序,因为生成的是字符串类型的id, 可读性差. b. 使用redis来生成全局唯一主键: 优点: redis是内存操作, 速度快, 生成的是数字, 可读性好, 并且可以按照生成的时间先后排序. 缺点: 如果整个系统没有用到redis技术, 那么这里使用redis会增加系统的技术复杂度.   应用服务器到redis服务器获取唯一id, 增加网络io. c. sn

Java分布式ID生成解决方案

分布式ID生成器 我们采用的是开源的twitter(  非官方中文惯称:推特.是国外的一个网站,是一个社交网络及微博客服务)  的snowflake算法(推特雪花算法). 封装为工具类,源码如下: package util; import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.NetworkInterface; /** * <p>名称:IdWorker.java&

一口气说出 9种 分布式ID生成方式,面试官有点懵了

摘自:https://www.cnblogs.com/chengxy-nds/p/12315917.html 整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 本文作者:程序员内点事原文链接:https://mp.weixin.qq.com/s?__biz=MzAxNTM4NzAyNg 更多精选 3万字总结,Mysql优化之精髓 为了不复制粘贴,我被逼着学会了JAVA爬虫 技术部突然宣布:JAVA开发人员全部要

9种分布式ID生成之 美团(Leaf)实战

整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 更多优选 一口气说出 9种 分布式ID生成方式,面试官有点懵了 面试总被问分库分表怎么办?你可以这样怼他 3万字总结,Mysql优化之精髓 为了不复制粘贴,我被逼着学会了JAVA爬虫 技术部突然宣布:JAVA开发人员全部要会接口自动化测试框架 Redis 5种数据结构及对应使用场景,全会面试要加分的 引言 前几天写过一篇<一口气说出 9种 分布式ID生成方式,面

Jedis使用总结【pipeline】【分布式的id生成器】【分布式锁【watch】【multi】】【redis分布式】(转)

前段时间细节的了解了Jedis的使用,Jedis是redis的java版本的客户端实现.本文做个总结,主要分享如下内容: [pipeline][分布式的id生成器][分布式锁[watch][multi]][redis分布式]好了,一个一个来.一. Pipeline官方的说明是:starts a pipeline,which is a very efficient way to send lots of command and read all the responses when you fin