一个简易版的分布式全局ID生成器实现思路

提到全局ID生成器,市面上已有很多成熟的解决方案,比如:MySQL的自增神器、jdk的UUID、Redis的id、zookeeper的id、Twitter的snowflake、美团的Leaf等等。

本文主要是自己想倒腾一下^_^~

下面进入正文,实现的ID有两个功能,防止时间的回拨,ID自增。

直接上代码

import cn.cosmosx.base.helper.SpringContextHolder;
import cn.cosmosx.base.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

/**
 * ID生成器
 */
@Slf4j
@Component
public final class IDGenerator implements InitializingBean {

    /**
     * 生成全局流水号
     */
    public static String generateGlobalSerial() {
        return IDRule.GLOBAL_SERIAL.generate();
    }

    /**
     * 生成资源请求ID
     */
    public static String generateRequestId() {
        return IDRule.REQUEST_ID.generate();
    }

    /**
     * 生成短ID
     */
    public static String generateMiniId() {
        return IDRule.MINI_ID.generate();
    }

    /**
     * 初始化缓存中的流水号
     */
    public static void initialize(RedisService redisService) {
        if (redisService == null) {
            log.error("RedisService bean is null, not initialize IDRules");
            return;
        }
        for (IDRule rule : IDRule.values()) {
            Integer serialSuffix = redisService.get(rule.name());
            rule.getSuffix().set(serialSuffix == null ? 0 : serialSuffix);
            log.info("Initialize {}: {}", rule.name(), rule.getSuffix().get());
        }
    }

    /**
     * 持久化
     */
    public static void persist(RedisService redisService) {
        if (redisService == null) {
            log.error("RedisService bean is null, not persist IDRules");
            return;
        }
        for (IDRule rule : IDRule.values()) {
            redisService.set(rule.name(), rule.getSuffix().get());
            log.info("Persist {}: {}", rule.name(), rule.getSuffix().get());
        }
    }


    @Override
    public void afterPropertiesSet() {
        IDGenerator.initialize(SpringContextHolder.getBean(RedisService.class));
    }

}

核心代码

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.text.DecimalFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

/**
 * ID生成规则
 */
@Slf4j
@Getter
enum IDRule {
    /**
     * 全局流水号标识(1位“G”)+系统ID(2位)+时间戳(14位)+序号(7位)24位长度
     */
    GLOBAL_SERIAL("全局流水号", "GQS", new DecimalFormat("0000000"), 9999999),

    /**
     * 资源请求ID:"REQUEST_ID" + 17位时间戳 + 6位自增序列号
     */
    REQUEST_ID("资源请求ID", "REQUEST", new DecimalFormat("000000"), 999999, Unit.MILLIS_SECONDS),

    /**
     * 资源请求ID:"MI" + 23位时间戳 + 2位自增序列号
     */
    MINI_ID("短ID", "MI", new DecimalFormat("000"), 999),
    ;
    /**
     * 锁
     */
    private final ReentrantLock lock;
    /**
     * 描述
     */
    private final String name;
    /**
     * 前缀
     */
    private final String prefix;
    /**
     * 后缀
     */
    private final AtomicInteger suffix;
    /**
     * 后缀格式化
     */
    private final DecimalFormat suffixFormat;
    /**
     * 后缀最大值
     */
    private final int suffixMax;
    /**
     * 时间戳格式化精确单位
     */
    private final Unit unit;
    /**
     * 上一个时间轮(单位:ms)
     */
    private LocalDateTime lastTimeWheel;
    /**
     * 步长,默认为单机,步长为1。(服务器数量与步长保持一致。如2台服务器,步长为2)
     */
    private int step = 1;

    IDRule(String name, String prefix, DecimalFormat suffixFormat, int suffixMax) {
        // 默认时间戳到秒级别,共14位
        this(name, prefix, suffixFormat, suffixMax, Unit.SECONDS);
    }

    IDRule(String name, String prefix, DecimalFormat suffixFormat, int suffixMax, Unit unit) {
        this(new ReentrantLock(), name, prefix, new AtomicInteger(0), suffixFormat, suffixMax, unit);
    }

    IDRule(ReentrantLock lock, String name, String prefix, AtomicInteger suffix, DecimalFormat suffixFormat, int suffixMax, Unit unit) {
        this.lock = lock;
        this.name = name;
        this.prefix = prefix;
        this.suffix = suffix;
        this.suffixFormat = suffixFormat;
        this.suffixMax = suffixMax;
        this.unit = unit;
    }

    /**
     * 取当前服务器时间戳(ms)
     */
    LocalDateTime ts() {
        return LocalDateTime.now();
    }

    /**
     * 生成最终生成的序列号
     */
    public final String generate() {
        lock.lock();
        try {
            int suffix = suffix();
            String ts = unit.getFormat().format(handleTs(suffix));
            return prefix + ts + suffixFormat.format(suffix);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 处理时间戳
     *
     * @param suffix - 后缀
     * @return - newTs
     */
    private LocalDateTime handleTs(int suffix) {
        // 初始化时间戳
        LocalDateTime ts = initTs(suffix);
        // 时钟回拨处理
        if (isClockCallback(ts)) {
            ts = ts();
        }
        // 重置时间轮
        setLastTimeWheel(ts);
        return ts;
    }

    /**
     * 初始化时间戳
     *
     * @param suffix - 自增序列号
     */
    private LocalDateTime initTs(int suffix) {
        LocalDateTime ts = ts();
        if (getLastTimeWheel() == null) {
            setLastTimeWheel(ts);
            return ts;
        }
        // 时间戳不变,后缀序列号归零处理,延迟1个单位时间
        if (suffix == step && unit.getFormat().format(getLastTimeWheel()).equals(unit.getFormat().format(ts))) {
            log.warn("{} auto-increment-serial is reset:{}.", this.name(), suffix);
            sleepNanoSeconds(getUnit());
            ts = ts();
        }
        return ts;
    }

    /**
     * 时钟是否回拨
     * 承受时钟回拨的误差范围最大为500ms
     *
     * @param newTs - 当前服务器时间戳
     */
    private boolean isClockCallback(LocalDateTime newTs) {
        final LocalDateTime last = getLastTimeWheel();
        if (newTs.isAfter(last) || newTs.isEqual(last)) {
            return false;
        }
        long diffMs = Duration.between(last, newTs).toMillis();
        if (diffMs - 500L <= 0) {
            log.warn("{} clock time is callback, differ:{}ms.", this.name(), suffix);
            try {
                TimeUnit.MILLISECONDS.sleep(diffMs);
            } catch (InterruptedException ignored) {
            }
            return true;
        }
        throw new RuntimeException("Current server clock is callback, differ:" + diffMs + "ms.");
    }

    /**
     * 生成自增序列号
     */
    private int suffix() {
        int s = suffix.addAndGet(step);
        if (s > suffixMax) {
            // 重置自增序列号
            suffix.set(0);
            s = suffix.addAndGet(step);
        }
        return s;
    }

    private void sleepNanoSeconds(Unit unit) {
        // 睡眠纳秒数
        long nanosSeconds;
        switch (unit) {
            case SECONDS:
                nanosSeconds = (long) Math.pow(10, 9);
                break;
            case MILLIS_SECONDS:
                nanosSeconds = (long) Math.pow(10, 6);
                break;
            case MICROS_SECONDS:
                nanosSeconds = (long) Math.pow(10, 3);
                break;
            default:
                nanosSeconds = 1L;
                break;
        }
        try {
            // 睡眠 range ns
            TimeUnit.NANOSECONDS.sleep(nanosSeconds);
        } catch (InterruptedException ignored) {
        }
    }

    void setLastTimeWheel(LocalDateTime millisSeconds) {
        this.lastTimeWheel = millisSeconds;
    }

    /**
     * 设置自增序列号步长,服务器数量与步长保持一致。如2台服务器,步长为2。以此类推
     *
     * @param step - 步长
     */
    public void setStep(int step) {
        this.step = step;
    }

    /**
     * 时间戳格式化精确单位
     */
    @Getter
    @AllArgsConstructor
    enum Unit {
        SECONDS(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")),
        MILLIS_SECONDS(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")),
        MICROS_SECONDS(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSSSSS")),
        NANOS_SECONDS(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSSSSSSSS"));
        private final DateTimeFormatter format;
    }
}

露干!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面我为您提供一个简单的分布式 id 生成器的 Go 代码实现,该生成器可以生成整型的 id。 ```go package main import ( "fmt" "math/rand" "sync" "time" ) // 定义一个全局的唯一 id 生成器 var generator *IDGenerator // 定义 id 生成器结构体 type IDGenerator struct { lastID int64 // 上一个生成的 id machineID int64 // 机器 id sequence int64 // 序列号 timestamp time.Time // 上一次生成 id 的时间戳 mutex sync.Mutex } // 生成一个新的 id func (g *IDGenerator) NewID() int64 { g.mutex.Lock() defer g.mutex.Unlock() // 获取当前时间戳 now := time.Now() // 如果当前时间小于上一次生成 id 的时间戳,则等待时间戳更新 if now.Before(g.timestamp) { time.Sleep(time.Millisecond) now = time.Now() } // 如果当前时间等于上一次生成 id 的时间戳,则序列号加 1 if now.Equal(g.timestamp) { g.sequence++ } else { // 否则,重置序列号为 0 g.sequence = 0 } // 更新 id 生成时间戳 g.timestamp = now // 生成 id id := (now.UnixNano()/1000000)<<22 | (g.machineID << 12) | g.sequence // 如果生成的 id 等于上一个生成的 id,则序列号加 1 if g.lastID == id { g.sequence++ id = (now.UnixNano()/1000000)<<22 | (g.machineID << 12) | g.sequence } // 更新上一个生成的 id g.lastID = id // 返回生成的 id return id } // 初始化 id 生成器 func NewIDGenerator(machineID int64) *IDGenerator { return &IDGenerator{ lastID: 0, machineID: machineID, sequence: 0, timestamp: time.Unix(0, 0), mutex: sync.Mutex{}, } } func main() { // 初始化 id 生成器 generator = NewIDGenerator(int64(rand.Intn(1024))) // 循环生成 id for { id := generator.NewID() fmt.Println(id) time.Sleep(time.Millisecond) } } ``` 上述代码实现一个分布式 id 生成器,其中每个 id 由时间戳、机器 id 和序列号组成。在初始化 id 生成器时,需要指定机器 id。在生成 id 时,会根据当前时间戳、机器 id 和序列号计算出一个整型的 id,并保证生成的 id 具有唯一性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流沙QS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值