import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.PostConstruct;
import org.perf4j.StopWatch;
import org.perf4j.slf4j.Slf4JStopWatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.alibaba.boot.hsf.annotation.HSFProvider;
/**
*
* @author 分段发号器service
*/
@HSFProvider(serviceInterface = SegmentService.class, serviceVersion = "1.0.0", serviceGroup = "biz-mdc")
@Service
@EnableScheduling
public class SegmentServiceImpl implements SegmentService, IDGen {
private Logger logger = LoggerFactory.getLogger(SegmentServiceImpl.class);
/**
* IDCache未初始化成功时的异常码
*/
private static final long EXCEPTION_ID_IDCACHE_INIT_FALSE = -1;
/**
* key不存在时的异常码
*/
private static final long EXCEPTION_ID_KEY_NOT_EXISTS = -2;
/**
* SegmentBuffer中的两个Segment均未从DB中装载时的异常码
*/
private static final long EXCEPTION_ID_TWO_SEGMENTS_ARE_NULL = -3;
/**
* 最大步长不超过100,0000
*/
private static final int MAX_STEP = 1000000;
/**
* 一个Segment维持时间为15分钟
*/
private static final long SEGMENT_DURATION = 15 * 60 * 1000L;
private ExecutorService service = new ThreadPoolExecutor(5, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), new UpdateThreadFactory());
@Autowired
private IDGen idGen;
@Autowired
private TIdGenMapper mapper;
private Map<String, SegmentBuffer> cache = new ConcurrentHashMap<String, SegmentBuffer>();
private volatile boolean initOK = false;
public static class UpdateThreadFactory implements ThreadFactory {
private static int threadInitNumber = 0;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "Thread-Segment-Update-" + nextThreadNum());
}
}
@PostConstruct
@Override
public boolean init() {
logger.info("发号器初始化开始");
// 确保加载到kv后才初始化成功
updateCacheFromDb();
initOK = true;
updateCacheFromDbAtEveryMinute();
logger.info("发号器初始化完成");
return initOK;
}
public Result getId(String key) {
return idGen.get(key);
}
private void updateCacheFromDbAtEveryMinute() {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("check-idCache-thread");
t.setDaemon(true);
return t;
}
});
service.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
updateCacheFromDb();
}
}, 60, 60, TimeUnit.SECONDS);
}
private void updateCacheFromDb() {
logger.info("缓存更新开始");
StopWatch sw = new Slf4JStopWatch();
try {
List<String> dbTags = mapper.getAllTags();
if (dbTags == null || dbTags.isEmpty()) {
return;
}
List<String> cacheTags = new ArrayList<String>(cache.keySet());
List<String> insertTags = new ArrayList<String>(dbTags);
List<String> removeTags = new ArrayList<String>(cacheTags);
// db中新加的tags灌进cache
insertTags.removeAll(cacheTags);
for (String tag : insertTags) {
SegmentBuffer buffer = new SegmentBuffer();
buffer.setKey(tag);
Segment segment = buffer.getCurrent();
segment.setValue(new AtomicLong(0));
segment.setMax(0);
segment.setStep(0);
cache.put(tag, buffer);
logger.info("将当前tag更新"+tag, tag, buffer);
mapper.updateMaxIdAndGetTIdGen(tag);
}
// cache中已失效的tags从cache删除
removeTags.removeAll(dbTags);
for (String tag : removeTags) {
cache.remove(tag);
logger.info("将失效tag从缓存中移除", tag);
}
} catch (Exception e) {
logger.warn("缓存更新异常", e);
} finally {
sw.stop("updateCacheFromDb");
}
}
@Override
public Result get(final String key) {
if (!initOK) {
return new Result(EXCEPTION_ID_IDCACHE_INIT_FALSE, Status.EXCEPTION);
}
if (cache.containsKey(key)) {
SegmentBuffer buffer = cache.get(key);
if (!buffer.isInitOk()) {
synchronized (buffer) {
if (!buffer.isInitOk()) {
try {
updateSegmentFromDb(key, buffer.getCurrent());
logger.info("创建buffer. Update key {} {} from db", key, buffer.getCurrent());
buffer.setInitOk(true);
} catch (Exception e) {
logger.warn("创建buffer {} 异常", buffer.getCurrent(), e);
}
}
}
}
return getIdFromSegmentBuffer(cache.get(key));
} else {
return new Result(EXCEPTION_ID_KEY_NOT_EXISTS, Status.EXCEPTION);
}
}
public void updateSegmentFromDb(String key, Segment segment) {
StopWatch sw = new Slf4JStopWatch();
SegmentBuffer buffer = segment.getBuffer();
TIdGen tIdGen;
if (!buffer.isInitOk()) {
// mapper.updateMaxIdAndGetTIdGen(key);
// tIdGen = mapper.getTIdGen(key);
tIdGen = transactionalTIdGen1(key);
buffer.setStep(tIdGen.getIncrement());
buffer.setMinStep(tIdGen.getIncrement());// tidgen中的step为DB中的step
} else if (buffer.getUpdateTimestamp() == 0) {
// mapper.updateMaxIdAndGetTIdGen(key);
// tIdGen = mapper.getTIdGen(key);
tIdGen = transactionalTIdGen1(key);
buffer.setUpdateTimestamp(System.currentTimeMillis());
buffer.setStep(tIdGen.getIncrement());
buffer.setMinStep(tIdGen.getIncrement());// tidgen中的step为DB中的step
} else {
long duration = System.currentTimeMillis() - buffer.getUpdateTimestamp();
int nextStep = buffer.getStep();
if (duration < SEGMENT_DURATION) {
if (nextStep * 2 > MAX_STEP) {
// do nothing
} else {
nextStep = nextStep * 2;
}
} else if (duration < SEGMENT_DURATION * 2) {
// do nothing with nextStep
} else {
nextStep = nextStep / 2 >= buffer.getMinStep() ? nextStep / 2 : nextStep;
}
logger.info("Key[{}], step[{}], duration[{}mins], nextStep[{}]", key, buffer.getStep(),
String.format("%.2f", ((double) duration / (1000 * 60))), nextStep);
TIdGen temp = new TIdGen();
temp.setBizTag(key);
temp.setIncrement(nextStep);
// mapper.updateMaxIdByCustomStepAndGetTIdGen(t);
// tIdGen = mapper.getTIdGen(t.getBizTag());
tIdGen = transactionalTIdGen2(temp);
buffer.setUpdateTimestamp(System.currentTimeMillis());
buffer.setStep(nextStep);
buffer.setMinStep(tIdGen.getIncrement());// idgen对象的step为DB中的step
}
// must set value before set max
long value = tIdGen.getCurrentValue() - buffer.getStep();
segment.getValue().set(value);
segment.setMax(tIdGen.getCurrentValue());
segment.setStep(buffer.getStep());
sw.stop("updateSegmentFromDb", key + " " + segment);
}
public Result getIdFromSegmentBuffer(final SegmentBuffer buffer) {
while (true) {
buffer.rLock().lock();
try {
final Segment segment = buffer.getCurrent();
if (!buffer.isNextReady() && (segment.getIdle() < 0.9 * segment.getStep())
&& buffer.getThreadRunning().compareAndSet(false, true)) {
service.execute(new Runnable() {
@Override
public void run() {
Segment next = buffer.getSegments()[buffer.nextPos()];
boolean updateOk = false;
try {
updateSegmentFromDb(buffer.getKey(), next);
updateOk = true;
logger.info("update segment {} from db {}", buffer.getKey(), next);
} catch (Exception e) {
logger.warn(buffer.getKey() + " updateSegmentFromDb exception", e);
} finally {
if (updateOk) {
buffer.wLock().lock();
buffer.setNextReady(true);
buffer.getThreadRunning().set(false);
buffer.wLock().unlock();
} else {
buffer.getThreadRunning().set(false);
}
}
}
});
}
long value = segment.getValue().getAndIncrement();
if (value < segment.getMax()) {
return new Result(value, Status.SUCCESS);
}
} finally {
buffer.rLock().unlock();
}
waitAndSleep(buffer);
buffer.wLock().lock();
try {
final Segment segment = buffer.getCurrent();
long value = segment.getValue().getAndIncrement();
if (value < segment.getMax()) {
return new Result(value, Status.SUCCESS);
}
if (buffer.isNextReady()) {
buffer.switchPos();
buffer.setNextReady(false);
} else {
logger.error("Both two segments in {} are not ready!", buffer);
return new Result(EXCEPTION_ID_TWO_SEGMENTS_ARE_NULL, Status.EXCEPTION);
}
} finally {
buffer.wLock().unlock();
}
}
}
private void waitAndSleep(SegmentBuffer buffer) {
int roll = 0;
while (buffer.getThreadRunning().get()) {
roll += 1;
if (roll > 10000) {
try {
TimeUnit.MILLISECONDS.sleep(10);
break;
} catch (InterruptedException e) {
logger.warn("Thread {} Interrupted", Thread.currentThread().getName());
break;
}
}
}
}
@Override
public Map<String, Object> getCache() {
Map<String, Object> result = new HashMap<String, Object>();
Map<String, SegmentBufferView> data = new HashMap<>();
if (this.idGen == null) {
throw new IllegalArgumentException("发号器未启用");
}
Map<String, SegmentBuffer> cache = this.cache;
for (Map.Entry<String, SegmentBuffer> entry : cache.entrySet()) {
SegmentBufferView sv = new SegmentBufferView();
SegmentBuffer buffer = entry.getValue();
sv.setInitOk(buffer.isInitOk());
sv.setKey(buffer.getKey());
sv.setPos(buffer.getCurrentPos());
sv.setNextReady(buffer.isNextReady());
sv.setMax0(buffer.getSegments()[0].getMax());
sv.setValue0(buffer.getSegments()[0].getValue().get());
sv.setStep0(buffer.getSegments()[0].getStep());
sv.setMax1(buffer.getSegments()[1].getMax());
sv.setValue1(buffer.getSegments()[1].getValue().get());
sv.setStep1(buffer.getSegments()[1].getStep());
data.put(entry.getKey(), sv);
}
logger.info("缓存列表 {}", data);
result.put("data", data);
return result;
}
@Override
public Map<String, Object> getDb() {
Map<String, Object> result = new HashMap<String, Object>();
if (idGen == null) {
throw new IllegalArgumentException("发号器未启用");
}
List<TIdGen> items = mapper.getAllTIdGen();
logger.info("数库据列表 {}", items);
result.put("items", items);
return result;
}
//@Transactional
public TIdGen transactionalTIdGen2(TIdGen t) {
mapper.updateMaxIdByCustomStepAndGetTIdGen(t);
TIdGen tIdGen = mapper.getTIdGen(t.getBizTag());
return tIdGen;
}
//@Transactional
public TIdGen transactionalTIdGen1(String key) {
mapper.updateMaxIdAndGetTIdGen(key);
TIdGen tIdGen = mapper.getTIdGen(key);
return tIdGen;
}
@Override
public Long getID(String key) {
Result result;
if (key == null || key.isEmpty()) {
throw new NoKeyException();
}
result = get(key);
if (result.getStatus().equals(Status.EXCEPTION)) {
throw new IDServerException(result.toString());
}
return result.getId();
}
/**
* 批量生成发号器
* @param key 发号器名称
* @param count 个数
* @return java.util.List<java.lang.Long>
* @date 2021/1/12 19:53
* @see String 入参:发号器名称
* @see int 入参:生成个数
* @see List<Long> 回参:生成结果
*/
@Override
public List<Long> listID(String key, int count) {
List<Long> listId = new ArrayList<>(count);
Result result;
if (key == null || key.isEmpty()) {
throw new NoKeyException();
}
for(int i = 0; i < count; i++){
result = get(key);
if (result.getStatus().equals(Status.EXCEPTION)) {
throw new IDServerException(result.toString());
}
listId.add(result.getId());
}
return listId;
}
@Override
public StartEndNum getIds(String key, Integer num) {
if (key == null || key.isEmpty()) {
throw new NoKeyException();
}
StartEndNum startEndNum = getListId(key,num - 1);
if (StringUtils.isEmpty(startEndNum.getEndNum()) || StringUtils.isEmpty(startEndNum.getStartNum())) {
for (int i = 0; i < 3; i++) {
if (StringUtils.isEmpty(startEndNum.getEndNum()) || StringUtils.isEmpty(startEndNum.getStartNum())) {
startEndNum = getListId(key, num - 1);
}else {
break;
}
}
}
if (StringUtils.isEmpty(startEndNum.getEndNum()) || StringUtils.isEmpty(startEndNum.getStartNum())) {
throw new IDServerException("编码获取失败");
}
return startEndNum;
}
/**
* 批量获取
*/
public StartEndNum getListId(final String key, Integer count) {
// 改对象中的init() 方法没有执行成功 就抛出异常
if (!initOK) {
return new StartEndNum(EXCEPTION_ID_IDCACHE_INIT_FALSE, Status.EXCEPTION);
}
if (cache.containsKey(key)) {
SegmentBuffer buffer = cache.get(key);
if (!buffer.isInitOk()) {
synchronized (buffer) {
if (!buffer.isInitOk()) {
try {
updateSegmentFromDbBatch(key, buffer.getCurrent(), count);
logger.info("创建buffer. Update key {} {} from db", key, buffer.getCurrent());
buffer.setInitOk(true);
} catch (Exception e) {
logger.warn("创建buffer {} 异常", buffer.getCurrent(), e);
}
}
}
}
return getIdFromSegmentBufferBatch(cache.get(key), count);
} else {
return new StartEndNum(EXCEPTION_ID_KEY_NOT_EXISTS, Status.EXCEPTION);
}
}
public StartEndNum getIdFromSegmentBufferBatch(final SegmentBuffer buffer,Integer num) {
while (true) {
buffer.rLock().lock();
try {
final Segment segment = buffer.getCurrent();
long now = segment.getIdle() - num;
if (!buffer.isNextReady() && (now < 0.9 * segment.getStep())
&& buffer.getThreadRunning().compareAndSet(false, true)) {
service.execute(new Runnable() {
@Override
public void run() {
Segment next = buffer.getSegments()[buffer.nextPos()];
boolean updateOk = false;
try {
updateSegmentFromDbBatch(buffer.getKey(), next,num);
updateOk = true;
logger.info("update segment {} from db {}", buffer.getKey(), next);
} catch (Exception e) {
logger.warn(buffer.getKey() + " updateSegmentFromDb exception", e);
} finally {
if (updateOk) {
buffer.wLock().lock();
buffer.setNextReady(true);
buffer.getThreadRunning().set(false);
buffer.wLock().unlock();
} else {
buffer.getThreadRunning().set(false);
}
}
}
});
}
long startValue = segment.getValue().getAndAdd(num + 1);
long endValue = startValue + num;
if (endValue > 0 && endValue < segment.getMax()) {
return new StartEndNum(0, Status.SUCCESS , startValue, endValue);
}
} finally {
buffer.rLock().unlock();
}
waitAndSleep(buffer);
buffer.wLock().lock();
try {
final Segment segment = buffer.getCurrent();
long startValue = segment.getValue().getAndAdd(num + 1);
long endValue = startValue + num;
if (endValue > 0 && endValue < segment.getMax()) {
return new StartEndNum(0, Status.SUCCESS , startValue, endValue);
}
if (buffer.isNextReady()) {
buffer.switchPos();
buffer.setNextReady(false);
} else {
logger.error("Both two segments in {} are not ready!", buffer);
return new StartEndNum(EXCEPTION_ID_TWO_SEGMENTS_ARE_NULL, Status.EXCEPTION);
}
} finally {
buffer.wLock().unlock();
}
}
}
public void updateSegmentFromDbBatch(String key, Segment segment, Integer num) {
StopWatch sw = new Slf4JStopWatch();
SegmentBuffer buffer = segment.getBuffer();
TIdGen tIdGen;
if (!buffer.isInitOk()) {
tIdGen = transactionalTIdGenBatch(key,num);
buffer.setStep((int) (tIdGen.getIncrement()));
buffer.setMinStep(tIdGen.getIncrement());// tidgen中的step为DB中的step
} else if (buffer.getUpdateTimestamp() == 0) {
tIdGen = transactionalTIdGenBatch(key, num);
buffer.setUpdateTimestamp(System.currentTimeMillis());
buffer.setStep(tIdGen.getIncrement());
buffer.setMinStep(tIdGen.getIncrement());// tidgen中的step为DB中的step
} else {
long duration = System.currentTimeMillis() - buffer.getUpdateTimestamp();
int nextStep = buffer.getStep();
if (duration < SEGMENT_DURATION) {
if (nextStep * 2 > MAX_STEP) {
// do nothing
} else {
nextStep = nextStep * 2;
}
} else if (duration < SEGMENT_DURATION * 2) {
// do nothing with nextStep
} else {
nextStep = nextStep / 2 >= buffer.getMinStep() ? nextStep / 2 : nextStep;
}
logger.info("Key[{}], step[{}], duration[{}mins], nextStep[{}]", key, buffer.getStep(),
String.format("%.2f", ((double) duration / (1000 * 60))), nextStep);
TIdGen temp = new TIdGen();
temp.setBizTag(key);
temp.setIncrement(nextStep);
tIdGen = transactionalTIdGenBatch(temp,num);
buffer.setUpdateTimestamp(System.currentTimeMillis());
buffer.setStep(nextStep);
buffer.setMinStep(tIdGen.getIncrement());// idgen对象的step为DB中的step
}
// must set value before set max
long value = tIdGen.getCurrentValue() - buffer.getStep() - num;
segment.getValue().set(value);
segment.setMax(tIdGen.getCurrentValue());
segment.setStep(buffer.getStep());
sw.stop("updateSegmentFromDb", key + " " + segment);
}
public TIdGen transactionalTIdGenBatch(String key,Integer num) {
//mapper.updateMaxIdAndGetTIdGen(key);
mapper.updateMaxIdByTagAndNum(key , num);
TIdGen tIdGen = mapper.getTIdGen(key);
return tIdGen;
}
public TIdGen transactionalTIdGenBatch(TIdGen t,Integer num) {
// mapper.updateMaxIdByCustomStepAndGetTIdGen(t);
mapper.updateMaxIdByTagAndNumAndCustomer(t.getBizTag() ,t.getIncrement(), num);
TIdGen tIdGen = mapper.getTIdGen(t.getBizTag());
return tIdGen;
}
/**
* @despection 方法说明:每晚十二点定时清理缓存中平台唯一码的segment数据
* @date 2021/4/27
* @author WLZ
*/
@Scheduled(cron = "0 0 0 * * ?")
public void ClearPlatUniqueCodeCache(){
UNIQUE_CODE_KEYS.forEach(p->{
cache.remove(p);
});
}
}
分段发号工具
于 2022-11-24 08:47:51 首次发布
![](https://img-home.csdnimg.cn/images/20240711042549.png)