IdentifierGenerator
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
public interface IdentifierGenerator {Number nextId(Object entity);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) {return GlobalId.nextId();}
}
定义全局id生成器
public class GlobalId {private static SnowflakeId snowflakeId = null;private GlobalId() {}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;
@Slf4j
public final class SnowflakeId {private final long twepoch = 1288834974657L;private final int workerIdBits = 10;private final int maxWorkerId = ~(-1 << workerIdBits);private final int sequenceBits = 12;private final int workerIdShift = sequenceBits;private final int timestampLeftShift = sequenceBits + workerIdBits;private final int sequenceMask = ~(-1 << sequenceBits);private Long workerId;private AtomicLong sequence;private AtomicLong lastTimestamp;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);}public long nextId() {long timestamp = timeGen();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);}lastTimestamp.set(timestamp);return ((timestamp - twepoch) << timestampLeftShift)| (workerId << workerIdShift)| sequence.get();}protected synchronized long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {try {TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException ignore) {}timestamp = timeGen();}return timestamp;}protected long timeGen() {return System.currentTimeMillis();}private long generateWorkerId() {try {return generateWorkerIdBaseOnMac();} catch (Exception e) {return generateRandomWorkerId();}}private long generateRandomWorkerId() {return RandomUtil.randomInt(maxWorkerId + 1);}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;
@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);}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 {public static final String DEFAULT_WORKER_ID_ALGORITHM = "redis";public static final long MAX_WORKER_ID = ~(-1 << 10);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 {private String workerIdAlgorithm;
}