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

使用分布式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;
}
http://www.xdnf.cn/news/1430.html

相关文章:

  • 【NVIDIA】Isaac Sim 4.5.0 Franka 机械臂参数解析
  • QT软件安装(12)
  • Sentinel源码—9.限流算法的实现对比一
  • 黑马点评redis改 part 5
  • 面向 C# 初学者的完整教程
  • 千问2.5-VL-7B的推理、微调、部署_笔记2
  • MyBatis中的@Param注解-如何传入多个不同类型的参数
  • .NET 6 + Dapper + User-Defined Table Type
  • 缓存与数据库一致性方案
  • 数据分析:用Excel做周报
  • Android开发常用外部组件及使用指南(上)
  • maple实现移位算法
  • 智驭未来:NVIDIA自动驾驶安全白皮书与实验室创新实践深度解析
  • Dart Flutter数据类型详解 int double String bool list Map
  • 亚远景-基于ASPICE标准的汽车软件过程优化路径
  • 电路中的DGND、GROUND、GROUND_REF的区别,VREF、VCC、VDD、VEE和VSS的区别?
  • Django 实现电影推荐系统:从搭建到功能完善(附源码)
  • AutoGPT超详细教程
  • SQL 时间转换的CONVERT()函数应用说明
  • Sentinel源码—9.限流算法的实现对比二
  • HSTL详解
  • 容器化-Docker-进阶
  • 高光谱相机在工业检测中的应用:LED屏检、PCB板缺陷检测
  • 软件项目实施全流程及交付物清单
  • 【教学类-102-17】蝴蝶三色图(用最大长宽作图,填入横板和竖版共16个WORD单元格模版大小,制作大小图)
  • Lodash 初学指南(适用于 Vue 3)​
  • 如何将IDP映射属性添加,到accountToken中 方便项目获取登录人信息
  • centos7部署k8s集群
  • 介绍 IntelliJ IDEA 快捷键操作
  • OpenBayes 一周速览|EasyControl 高效控制 DiT 架构,助力吉卜力风图像一键生成;TripoSG 单图秒变高保真 3D 模型