1.需求:生成新节点时,为节点取三位数ID,要求ID顺序增长。节点变动时当前号码作废,新的号码补充到最近的缺口中。
2.实现:
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RDeque;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* @author Hongyi Zheng
* @date 2021/2/19
*/
@Component("atomicSeqOperation")
@Slf4j
public class AtomicSeqOperation implements SeqOperation {
/**
* 最大获取锁时间
*/
private static final long WAIT_TIME = 3;
/**
* 锁释放时间
*/
private static final long LEASE_TIME = 60;
/**
* redis key前缀
*/
private static final String PROJ = "bd-chain-manager:";
private final RedissonClient redissonClient;
public AtomicSeqOperation(@Qualifier("redisson") RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
/**
* <p>sync</p> 新增节点时生成3位序列号(补充空闲列表)
*
* @param pe enumeration obj
* @return sync 3 figures digital sequence
*/
public long syncGetSeq(PrimEnum pe, int level) {
String[] prims = prims(pe, level);
String lock = prims[0];
String counter = prims[1];
String freelist = prims[2];
RLock rLock = redissonClient.getLock(lock);
RDeque<Long> deque = redissonClient.getDeque(freelist);
long longSeq = 0;
try {
if (!rLock.isLocked() && rLock.tryLock(WAIT_TIME, LEASE_TIME, TimeUnit.SECONDS)) {
if (deque.isEmpty()) {
longSeq = adder(counter);
} else {
longSeq = deque.pop();
}
rLock.unlock();
}
} catch (InterruptedException e) {
log.warn("synchronized sequence getter exception:{}", ExceptionUtils.getStackTrace(e));
Thread.currentThread().interrupt();
} finally {
if (rLock.isHeldByCurrentThread()) {
rLock.unlock();
}
}
return longSeq;
}
/**
* <p>sync</p> 节点变动时获取新的序列号
*
* @param curr 当前节点编码
* @param pe 节点类型
* @param level 节点层级
* @return 新的3位编码
*/
public long syncModSeq(long curr, PrimEnum pe, int level) {
if (curr < 1000) {
return 0;
}
//对1000取模
curr = curr % 1000;
String[] prims = prims(pe, level);
String lock = prims[0];
String counter = prims[1];
String freelist = prims[2];
RLock rLock = redissonClient.getLock(lock);
long result = 0;
try {
RDeque<Long> deque = redissonClient.getDeque(freelist);
if (!rLock.isLocked() && rLock.tryLock(WAIT_TIME, LEASE_TIME, TimeUnit.SECONDS)) {
if (deque.isEmpty()) {
deque.push(curr);
result = adder(counter);
} else {
result = deque.pop();
}
rLock.unlock();
}
} catch (InterruptedException e) {
log.warn("synchronized sequence modification exception:{}", ExceptionUtils.getStackTrace(e));
Thread.currentThread().interrupt();
} finally {
if (rLock.isHeldByCurrentThread()) {
rLock.unlock();
}
}
return result;
}
/**
* 获取序列号
*
* @return 返回序列号
*/
private long adder(String counter) {
long longSeq = -1;
try {
RAtomicLong atomicLong = redissonClient.getAtomicLong(counter);
longSeq = atomicLong.addAndGet(1);
} catch (Exception e) {
log.warn("Sequence counter exception, seq = {}, exception:{}", longSeq, ExceptionUtils.getStackTrace(e));
}
return longSeq;
}
private String lockFormat(PrimEnum pe, int level) {
return PROJ +
pe +
"_" +
PrimEnum.LOCK + "_" + level;
}
private String freelistFormat(PrimEnum pe, int level) {
return PROJ +
pe +
"_" +
PrimEnum.FREELIST + "_" + level;
}
private String counterFormat(PrimEnum pe, int level) {
return PROJ +
pe +
"_" +
PrimEnum.COUNTER + "_" + level;
}
private String[] prims(PrimEnum pe, int level) {
return new String[]{lockFormat(pe, level), counterFormat(pe, level), freelistFormat(pe, level)};
}
@Override
public long reformCode(int preLevel, int level, long code, long newPc, PrimEnum pe) {
long last = syncModSeq(code, pe, level);
//如果是根目录
if (level == 1) {
switch (pe) {
case CATEGORY:
return 101 * 1000 + last;
case PRODUCT:
return 102 * 1000 + last;
default:
break;
}
}
return newPc * 1000 + last;
}
@Override
public long formNewCode(int level, long parentCode, PrimEnum pe) {
long subCode = syncGetSeq(pe, level);
if (subCode != 0) {
return parentCode * 1000 + subCode;
}
return 0;
}
@Override
public long formKeycode(PrimEnum pe, long seq) {
switch (pe) {
case CATEGORY:
return 101 * 1_000_000_00L + seq;
case PRODUCT:
return 102 * 1_000_000_00L + seq;
default:
return 0;
}
}
@Override
public long peekByKey(String key) {
if (key.contains(PrimEnum.COUNTER.name())) {
return redissonClient.getAtomicLong(key).get();
} else if (key.contains(PrimEnum.FREELIST.name())) {
Long value = (Long) redissonClient.getDeque(key).peekFirst();
return value == null ? 0 : value;
} else {
return 0;
}
}
}
/**
* @author Hongyi Zheng
* @date 2021/2/19
*/
public interface SeqOperation {
/**
* 重编码
*
* @param preLevel 移动前的层级
* @param level 移动后的层级
* @param code 移动前的编码
* @param newPc 移动后父节点编码
* @param pe 从属类型 (大类产品,标准产品)
* @return 返回重编的编码
*/
long reformCode(int preLevel, int level, long code, long newPc, PrimEnum pe);
/**
* 生成一个新的序列号
*
* @param level 序列号层级
* @param parentCode 父节点编号
* @param pe 枚举类型
* @return 返回一个固定长度的序列号
*/
long formNewCode(int level, long parentCode, PrimEnum pe);
/**
* 生成新的内码
*
* @param pe 枚举类型
* @param seq 8位序列号
* @return 枚举不存在时返回0
*/
long formKeycode(PrimEnum pe, long seq);
long peekByKey(String key);
}
/**
* 序列号生成器参数基本类型枚举
*
* @author Hongyi Zheng
* @date 2021/2/19
*/
public enum PrimEnum {
/**
* 行业
*/
INDUSTRY,
/**
* 大类产品
*/
CATEGORY,
/**
* 标准产品
*/
PRODUCT,
/**
* 锁
*/
LOCK,
/**
* 序列号空闲列表
*/
FREELIST,
/**
* 序列号计数器
*/
COUNTER
}