当前位置: 首页 > news >正文

使用分布式ID作为MybatisID生成器

IdentifierGenerator

import com.baomidou.mybatisplus.core.toolkit.IdWorker;/*** Id生成器接口** @author  nieqiuqiu* @since 2019-10-15* @since 3.3.0*/
public interface IdentifierGenerator {/*** 生成Id** @param entity 实体* @return id*/Number nextId(Object entity);/*** 生成uuid** @param entity 实体* @return uuid*/default String nextUUID(Object entity) {return IdWorker.get32UUID();}
}

自定义实现IdentifierGenerator

import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import org.springframework.stereotype.Component;/***/
@Component
public class CustomIdGenerator implements IdentifierGenerator {@Overridepublic Long nextId(Object entity) {//返回生成的id值即可.return GlobalId.nextId();}
}

定义全局id生成器

/*** 全局id生成器*/
public class GlobalId {private static SnowflakeId snowflakeId = null;private GlobalId() {}/*** 初始化workerId** @param workerId*/public static void init(Long workerId) {snowflakeId = new SnowflakeId(workerId);}public static long nextId() {if (snowflakeId == null) {throw new NullPointerException("GlobalId is not initialized!");}return snowflakeId.nextId();}
}

Java实现雪花算法


import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;/*** 雪花ID生成类*/
@Slf4j
public final class SnowflakeId {/*** 开始时间截*/private final long twepoch = 1288834974657L;/*** 机器id所占的位数*/private final int workerIdBits = 10;/*** 支持的最大机器id,结果是1023 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)*/private final int maxWorkerId = ~(-1 << workerIdBits);/*** 序列在id中占的位数*/private final int sequenceBits = 12;/*** 机器ID向左移12位*/private final int workerIdShift = sequenceBits;/*** 时间截向左移22位(5+5+12)*/private final int timestampLeftShift = sequenceBits + workerIdBits;/*** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)*/private final int sequenceMask = ~(-1 << sequenceBits);/*** 工作机器ID(0~31)*/private Long workerId;/*** 毫秒内序列(0~4095)*/private AtomicLong sequence;/*** 上次生成ID的时间截*/private AtomicLong lastTimestamp;//==============================Constructors=====================================/*** 构造函数** @param workerId 工作ID (0~31)*/public SnowflakeId(Long workerId) {if (workerId == null) {workerId = generateWorkerId();}if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));}this.workerId = workerId;this.sequence = new AtomicLong(0);this.lastTimestamp = new AtomicLong(-1L);}// ==============================Methods==========================================/*** 获得下一个ID (该方法是线程安全的)** @return SnowflakeId*/public long nextId() {long timestamp = timeGen();//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常long lastTimestampLocal = lastTimestamp.get();if (timestamp < lastTimestampLocal) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestampLocal - timestamp));}//如果是同一时间生成的,则进行毫秒内序列if (lastTimestampLocal == timestamp) {//毫秒内序列溢出if (sequence.getAndIncrement() == sequenceMask){//阻塞到下一个毫秒,获得新的时间戳timestamp = tilNextMillis(lastTimestampLocal);sequence.set(0L);}}//时间戳改变,毫秒内序列重置else {sequence.set(0L);}//上次生成ID的时间截lastTimestamp.set(timestamp);//移位并通过或运算拼到一起组成64位的IDreturn ((timestamp - twepoch) << timestampLeftShift)| (workerId << workerIdShift)| sequence.get();}/*** 阻塞到下一个毫秒,直到获得新的时间戳** @return 当前时间戳*/protected synchronized long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {try {TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException ignore) {// don't care}timestamp = timeGen();}return timestamp;}/*** 返回以毫秒为单位的当前时间** @return 当前时间(毫秒)*/protected long timeGen() {return System.currentTimeMillis();}/*** auto generate workerId, try using mac first, if failed, then randomly generate one** @return workerId*/private long generateWorkerId() {try {return generateWorkerIdBaseOnMac();} catch (Exception e) {return generateRandomWorkerId();}}private long generateRandomWorkerId() {return RandomUtil.randomInt(maxWorkerId + 1);}/*** use lowest 10 bit of available MAC as workerId** @return workerId* @throws Exception when there is no available mac found*/private long generateWorkerIdBaseOnMac() throws Exception {Enumeration<NetworkInterface> all = NetworkInterface.getNetworkInterfaces();while (all.hasMoreElements()) {NetworkInterface networkInterface = all.nextElement();boolean isLoopback = networkInterface.isLoopback();boolean isVirtual = networkInterface.isVirtual();if (isLoopback || isVirtual) {continue;}byte[] mac = networkInterface.getHardwareAddress();if (mac == null) {log.warn("WorkerId 获取mac地址失败,使用随机数代替");return generateRandomWorkerId();}return ((mac[4] & 0B11) << 8) | (mac[5] & 0xFF);}throw new RuntimeException("no available mac found");}
}

workId计算,解决服务存在多个实例导致ID重复

import io.lettuce.core.RedisException;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;/*** {@link GlobalId}Redis方式初始化*/
@Configuration
@RequiredArgsConstructor
public class GlobalIdAutoConfiguration implements InitializingBean {@Autowiredprivate GlobalIdProperties globalIdProperties;@Overridepublic void afterPropertiesSet() throws Exception {Long workId = null;if (GlobalIdConst.DEFAULT_WORKER_ID_ALGORITHM.equals(globalIdProperties.getWorkerIdAlgorithm())) {try {workId = RedisUtils.increment(GlobalIdConst.GLOBALID_WORKERID);}// 当计数器递增至Long.MAX_VALUE时抛异常;此时从上一次的下一个余数开始继续计数catch (RedisException ex) {workId = RedisUtils.incrBy(GlobalIdConst.GLOBALID_WORKERID, 0);if (workId != Long.MAX_VALUE) {workId = RedisUtils.increment(GlobalIdConst.GLOBALID_WORKERID);} else {workId = (Long.MAX_VALUE % GlobalIdConst.MAX_WORKER_ID) + 1;RedisUtils.set(GlobalIdConst.GLOBALID_WORKERID, workId);}}workId = workId % GlobalIdConst.MAX_WORKER_ID;}GlobalId.init(workId);}
}

常量类

public class GlobalIdConst {/*** WorkerId生成默认算法*/public static final String DEFAULT_WORKER_ID_ALGORITHM = "redis";/*** 雪花算法workId最大的值(位数10位)*/public static final long MAX_WORKER_ID = ~(-1 << 10);/*** globalid workerId计算器key*/public static final String GLOBALID_WORKERID = "globalId:workerId";
}

workId生成算法配置类


import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Data
@ConfigurationProperties(prefix = "global.id")
public class GlobalIdProperties {/*** 全局ID WorkerId生成算法(支持redis或auto)* 如果配置为auto,则通过计算网卡mac信息生成* 默认算法为redis*/private String workerIdAlgorithm;
}

相关文章:

  • Eliciting Causal Abilities in Large Language Models for Reasoning Tasks
  • Kotlin 边界限制
  • vant Dialog组件调用的坑
  • 【Hive入门】Hive数据模型与存储格式深度解析:从理论到实践的最佳选择
  • 前端基础之《Vue(8)—内置组件》
  • Python文本的基本操作:with语句
  • Tomcat 8 启动闪退解决方案:版本差异与调试技巧详解
  • uv pip install 的本质是什么?
  • 08-IDEA企业开发工具-集成AI插件通义灵码
  • mybatis xml中特殊字符处理
  • Java基础:网络编程UDPTCP详解
  • Vite vs Webpack 优势对比
  • 车载信息安全架构 --- 汽车网络安全
  • 基于GA遗传优化TCN-BiGRU注意力机制网络模型的时间序列预测算法matlab仿真
  • 穿越链路的旅程:深入理解计算机网络中的数据链路层
  • WebSocket是h5定义的,双向通信,节省资源,更好的及时通信
  • 栈和队列学习记录
  • 【playwright】学习--持续汇总
  • onlyoffice历史版本功能实现,版本恢复功能,编辑器功能实现 springboot+vue2
  • 视频监控从安装到优化的技术指南,视频汇聚系统EasyCVR智能安防系统构建之道
  • 长三角与粤港澳大湾区融合发展,无锡何以成为窗口?
  • 牛市早报|特朗普称或将“大幅降低”对华关税,外交部回应
  • 山东一季度GDP为23466亿元,同比增长6.0%
  • 融入长三角一体化发展,苏南名城镇江的优势和机遇何在
  • 明日出征!航天员详细信息来啦
  • 言短意长|大学校门到底应不应该开放?