#原理 来自于Twitter的snowflake算法,国内实现较好的是百度的uid-generator项目,开源地址:github.com/baidu/uid-g… 基本原理就是把一个64位的long类型的值按位进行拆分,39位表示时间戳,5位表示业务线,5位表示机器码,3位表示机房,12位表示自增的sequence,然后按位或组装依赖形成10进制的数,就是唯一的ID。 #变化
public class OrderNoGenerator {
private final static String ORDER_CENTER_WORKID_PREFIX = "order_center_workId";//机器号ID的前缀
private static final String CURRENT_MAX_ID = "_current_max_id";//当前最大的机器ID
private static final String INIT_LOCK = "_init_lock";//初始化锁
private static final int MAX_WORKER_ID = 99;//机器号增长到99将重新归0
//机器号的超时时间,防止服务挂掉机器ID仍然长期存在于Redis
private static final int WORK_ID_LIVE_TIME = 60 * 5;
//同一时间段中,单机自增
private static AtomicInteger sequence = new AtomicInteger(0);
private static Long workId;//当前机器的机器号
private static volatile String lastTimeStamp;//标识时间段
public static String nextOrderNo(OrderSourceEnum orderSourceEnum) {
String timeStamp = DateUtils.dateFormat(DateUtils.ORDER_NO_PREFIX_PATTERN);
String sequence = StringUtils.leftPad(String.valueOf(getSequence(timeStamp)), 4, '0');
String workIdStr = StringUtils.leftPad(String.valueOf(workId), 2, '0');
return MessageFormat.format("{0}{1}{2}{3}", timeStamp, workIdStr, sequence, orderSourceEnum.getCode());
}
//单机序列生成,加锁防止并发问题
private static synchronized int getSequence(String timeStamp) {
if (timeStamp != null && !timeStamp.equals(lastTimeStamp)) {
lastTimeStamp = timeStamp;
sequence = new AtomicInteger(0);
return sequence.get();
}
return sequence.incrementAndGet();
}
//机器启动时初始化机器码,需要加分布式锁
public static Long intWorkId() {
String lockKey = ORDER_CENTER_WORKID_PREFIX + INIT_LOCK;
if (!tryLock(lockKey, 1)) {
throw new BusinessException("OrderNoGenerator.getWorkId 加锁异常");
}
Long workId = nextWorkId();
RedisUtils.set(ORDER_CENTER_WORKID_PREFIX + ":" + workId, workId, WORK_ID_LIVE_TIME);
unLock(lockKey);
return workId;
}
private static void unLock(String lockKey) {
RedisUtils.del(lockKey);
}
//分布式锁,尝试加三次
private static boolean tryLock(String lockKey, int tryTimes) {
Boolean setNX = RedisUtils.setNX(lockKey, "lock");
if (!setNX) {
log.info("加锁失败,睡眠10s.lockKey={},tryTimes={}", lockKey, tryTimes);
if (tryTimes > 3) {
return false;
}
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
log.error("OrderNoGenerator.tryLock,sleep 异常:", e);
}
return tryLock(lockKey, ++tryT);imes
}
return true;
}
private static Long nextWorkId() {
String maxIdKey = ORDER_CENTER_WORKID_PREFIX + CURRENT_MAX_ID;
//从Redis中申请一个机器码,一定范围内机器码是递增的
Long workId = RedisUtils.getAndIncrement(maxIdKey, 0L);
//如果机器码大于99,则重新从0开始
if (workId > MAX_WORKER_ID) {
RedisUtils.set(maxIdKey, 0L);
return nextWorkId();
}
//如果机器码已存在,则递归重新获取
if (RedisUtils.exists(ORDER_CENTER_WORKID_PREFIX + ":" + workId)) {
return nextWorkId();
}
return workId;
}
@Component
public static class WorkIdInitialize implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {//服务启动时调用
workId = intWorkId();
}
}
@Component
private static class WorkIdKeepLive {
@Scheduled(cron = "0 0/5 * * * *")
public void reLiveWorkId() {//当前机器码在Redis会过期,每隔一段时间重新set一次,相当于心跳
try {
RedisUtils.set(ORDER_CENTER_WORKID_PREFIX + ":" + workId, workId, WORK_ID_LIVE_TIME);
} catch (Exception e) {
log.error("redis operation is unavailable", e);
}
}
}
}