All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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