/**
* 初始化雪花工具类workId及datacenterId;
* 通过redis作为项目集群下唯一主键中心
*/
@Component
@SuppressWarnings("all")
@AutoConfigureAfter(value = {RedissonService.class})
public class SnowWorkAutoConfig implements InitializingBean {
public SnowWorkAutoConfig(RedisTemplate<String,String> redisTemplate) {
this.hashOperations = redisTemplate.opsForHash();
}
HashOperations<String,String,Integer> hashOperations;
private long maxWorkId;
private long maxDatacenterId;
private final String redisKey = RedisConstant.SNOW_SERVICE_ID;
public void init() {
try {
maxWorkId = getLong("maxWorkerId");
maxDatacenterId = getLong("maxDatacenterId");
}catch (Exception e){
maxWorkId = 31L;
maxDatacenterId = 31L;
}
String redissonKey = redisKey + ":LOCK";
RedissonUtil.safeRun(redissonKey,()->{
Result result = nextId();
new SnowflakeUtil(result.workId, result.dataCenterId);
});
}
/**
* @param filedName 字段名
* @return SnowflakeUtil 里面的参数值
*/
private long getLong(String filedName) throws NoSuchFieldException, IllegalAccessException {
Field declaredField = SnowflakeUtil.class.getDeclaredField(filedName);
declaredField.setAccessible(true);
return declaredField.getLong(null);
}
@Override
public void afterPropertiesSet() throws Exception {
init();
}
static class Result {
long workId;
long dataCenterId;
public Result(long workId, long dataCenterId) {
this.workId = workId;
this.dataCenterId = dataCenterId;
}
}
/**如果 Hash 已存在,判断 dataCenterId、workerId 是否等于最大值 31,
* 满足条件初始化 dataCenterId、workerId 设置为 0 返回
dataCenterId 和 workerId 的排列组合一共是 1024,在进行分配时,先分配 workerId
判断 workerId 是否 != 31,条件成立对 workerId 自增,并返回;
如果 workerId = 31,自增 dataCenterId 并将 workerId 设置为 0
dataCenterId、workerId 是一直向下推进的*/
private Result nextId(){
int defaultValue = 0;
String dataKey = "dataCenterId";
String workKey = "workId";
Supplier<Result> defaultReturn = ()->{
hashOperations.put(redisKey, dataKey,defaultValue);
hashOperations.put(redisKey, workKey,defaultValue);
return new Result(defaultValue, defaultValue);
};
if(hashOperations.hasKey(redisKey, workKey)){
long workV = hashOperations.get(redisKey, workKey);
long dataV = hashOperations.get(redisKey, dataKey);
long delta = 1;
if(workV>=maxWorkId){
if(dataV>=maxDatacenterId){
return defaultReturn.get();
}else {
hashOperations.put(redisKey, workKey,defaultValue);
return new Result(defaultValue, hashOperations.increment(redisKey, dataKey, delta));
}
}else {
return new Result(hashOperations.increment(redisKey, workKey, delta),dataV);
}
}
return defaultReturn.get();
}
}
public class RedissonUtil {
private static volatile RedissonService redissonService;
public static RedissonService getRedissonService() {
if(redissonService==null){
synchronized (RedissonUtil.class){
if(redissonService==null){
redissonService = SpringBeanUtils.getBean(RedissonService.class);
}
}
}
return redissonService;
}
public static void safeRun(String key,Runnable runnable){
safeRun(key,5*60,3*60,runnable);
}
/**
* @param key lock key
* @param waitTime 等待时长 秒
* @param leaseTime 自动释放时长 秒
* @param runnable 锁内方法体
*/
public static void safeRun(String key,long waitTime,long leaseTime,Runnable runnable){
try {
if(getRedissonService().tryLock(key,waitTime,leaseTime))
runnable.run();
}finally {
getRedissonService().unlock(key);
}
}
}
public class SnowflakeUtil {
private static final long twepoch = 1420041600000L;
private static final long workerIdBits = 5L;
private static final long datacenterIdBits = 5L;
private static final long maxWorkerId = 31L;
private static final long maxDatacenterId = 31L;
private static final long sequenceBits = 12L;
private static final long workerIdShift = 12L;
private static final long datacenterIdShift = 17L;
private static final long timestampLeftShift = 22L;
private static final long sequenceMask = 4095L;
private static long workerId;
private static long datacenterId;
private static long sequence = 0L;
private static long lastTimestamp = -1L;
public SnowflakeUtil(long workerId, long datacenterId) {
if (workerId <= 31L && workerId >= 0L) {
if (datacenterId <= 31L && datacenterId >= 0L) {
SnowflakeUtil.workerId = workerId;
SnowflakeUtil.datacenterId = datacenterId;
} else {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", 31L));
}
} else {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", 31L));
}
}
public static String nextId() {
long ret = nextLongId();
return String.valueOf(ret);
}
public static synchronized long nextLongId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
} else {
if (lastTimestamp == timestamp) {
sequence = sequence + 1L & 4095L;
if (sequence == 0L) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return timestamp - 1420041600000L << 22 | datacenterId << 17 | workerId << 12 | sequence;
}
}
protected static long tilNextMillis(long lastTimestamp) {
long timestamp;
for(timestamp = timeGen(); timestamp <= lastTimestamp; timestamp = timeGen()) {
}
return timestamp;
}
protected static long timeGen() {
return System.currentTimeMillis();
}
}
最后这个是项目内用到的雪花生成Util,请根据自己的项目情况进行灵活调整