com.ineunet.knife.seq.service.impl.IdSequenceServiceImpl Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2013-2018 iNeunet OpenSource and the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.ineunet.knife.seq.service.impl;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ineunet.knife.jdbc.JdbcDao;
import com.ineunet.knife.jdbc.annotation.Table;
import com.ineunet.knife.seq.IdSequence;
import com.ineunet.knife.seq.service.IdSequenceService;
import com.ineunet.knife.util.concurrent.NamedThreadFactory;
/**
*
* @author [email protected]
* Created on 2015年6月13日
*/
public class IdSequenceServiceImpl implements IdSequenceService {
private static final Logger log = LoggerFactory.getLogger(IdSequenceServiceImpl.class);
@Resource
private JdbcDao jdbcDao;
private Map idSequences = new ConcurrentHashMap<>();
private Map atomicIds = new ConcurrentHashMap<>();
// 写锁
private Lock lock = new ReentrantLock();
/*
* 标记label对应记录的状态,如果队列中有申请号段的任务,就不再申请造成积压和号段的浪费.
* 如果对应的label值是2,说明已经有两个同一label的号段申请任务了
*/
private Map labelStatus = new ConcurrentHashMap<>();
private ExecutorService executor;
private int defaultInterval = 1000;
public IdSequenceServiceImpl() {}
public IdSequenceServiceImpl(JdbcDao jdbcDao) {
this(jdbcDao, 1000);
}
public IdSequenceServiceImpl(JdbcDao jdbcDao, int defaultInterval) {
this(jdbcDao, defaultInterval, 1);
}
public IdSequenceServiceImpl(JdbcDao jdbcDao, int defaultInterval, int updateExecutors) {
this.jdbcDao = jdbcDao;
this.defaultInterval = defaultInterval;
if (updateExecutors < 1) updateExecutors = 1;
executor = Executors.newFixedThreadPool(updateExecutors, new NamedThreadFactory("idSeq"));
}
@PostConstruct
void init() {
}
@Override
public long nextId(final String label) {
IdSequence idSeq = idSequences.get(label);
if (idSeq == null) {
idSeq = getRecord(label);
if (idSeq == null) {
// 还没有就创建
lock.lock();
if (idSeq == null) {
try {
String group = "default";
int idx = label.indexOf(".");
if (idx > 0) {
group = label.substring(0, idx);
}
idSeq = new IdSequence(group, label, defaultInterval);
Long id = jdbcDao.createIncre(idSeq);
idSeq.setId(id);
idSequences.put(label, idSeq);
atomicIds.put(label, new AtomicLong(1));
} finally {
lock.unlock();
}
}
} else {
lock.lock();
try {
if (!idSequences.containsKey(label)) {
idSequences.put(label, idSeq);
atomicIds.put(label, new AtomicLong(idSeq.getNextId()));
updateNextId(idSeq);
}
} finally {
lock.unlock();
}
}
}
// 以上是第一次使用系统时,内存中甚至DB中还没有数据
AtomicLong atomicId = atomicIds.get(label);
if (atomicId.get() + idSeq.getInterval() / 2 > idSeq.getNextId()) {
// if (log.isDebugEnabled()) {
// log.debug("----> atomicId.get() + idSeq.getInterval() / 2={}, idSeq.getNextId()={}", atomicId.get() + idSeq.getInterval() / 2, idSeq.getNextId());
// }
// 检查,禁止重复申请
int tasks = getUpdateTasks(label);
if (tasks != 0) {
// if (log.isDebugEnabled()) {
// log.debug("there has {} task in queue. label=" + label, tasks);
// }
} else {
// 异步申请
final IdSequence entity = (IdSequence) idSeq;
incrementUpdateTask(label);
executor.submit(() -> {
updateNextId(entity);
decrementUpdateTask(label);
});
}
} else if (atomicId.get() > idSeq.getNextId()) {
try {
if (log.isDebugEnabled()) log.debug("sleep(20)");
Thread.sleep(20);
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
return nextId(label);
}
return atomicId.incrementAndGet();
}
private void updateNextId(IdSequence idSeq) {
idSeq.setNextId(idSeq.getNextId() + idSeq.getInterval());
updateSeq(idSeq);
}
private IdSequence getRecord(String label) {
List recs;
try {
recs = jdbcDao.findByColumn("label", label, IdSequence.class);
} catch (Exception e) {
if (e != null && e.getMessage().contains("knife_id_sequence' doesn't exist") || e instanceof org.springframework.jdbc.BadSqlGrammarException) {
// table not exist, create it
lock.lock();
try {
try {
jdbcDao.findByColumn("label", label, IdSequence.class);
} catch (Exception e1) {
String createTable = "CREATE TABLE `knife_id_sequence` ("
+ "`id` bigint(20) NOT NULL AUTO_INCREMENT,"
+ "`group` varchar(32) NOT NULL,"
+ "`label` varchar(64) DEFAULT NULL,"
+ "`interval` int(11) DEFAULT 1000,"
+ "`nextId` bigint(20) DEFAULT NULL,"
+ "`updateTime` datetime DEFAULT NULL,"
+ "PRIMARY KEY (`id`)"
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='全局id配置表';";
jdbcDao.execute(createTable);
}
} finally {
lock.unlock();
}
recs = jdbcDao.findByColumn("label", label, IdSequence.class);
} else {
throw new RuntimeException("find knife_id_sequence by label(" + label + ") error.", e);
}
}
if (recs.isEmpty()) {
return null;
}
return recs.get(0);
}
private void updateSeq(IdSequence seq) {
Map props = new HashMap<>();
props.put("nextId", seq.getNextId());
props.put("updateTime", new Date());
jdbcDao.updateById(seq.getClass().getAnnotation(Table.class).name(), seq.getId(), props);
}
// 队列中添加任务,对应的计数器加一
private void incrementUpdateTask(String label) {
AtomicInteger hasTask = labelStatus.get(label);
if (hasTask == null) {
labelStatus.put(label, new AtomicInteger(1));
} else {
hasTask.incrementAndGet();
}
}
// 队列中任务完成后,对应的计数器减一
private void decrementUpdateTask(String label) {
AtomicInteger hasTask = labelStatus.get(label);
if (hasTask != null) {
hasTask.decrementAndGet();
}
}
private int getUpdateTasks(String label) {
AtomicInteger hasTask = labelStatus.get(label);
if (hasTask != null) {
return hasTask.get();
}
return 0;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy