
com.github.javaclub.cdl.client.sequence.SequenceDO Maven / Gradle / Ivy
The newest version!
package com.github.javaclub.cdl.client.sequence;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.commons.lang3.StringUtils;
import com.github.javaclub.cdl.client.config.DbConfigManager;
import com.github.javaclub.cdl.client.group.SGroupDataSource;
/**
* Sequence ID
*/
public class SequenceDO {
private String table; // sequence表名称
private static String nameColumn = "name"; // seqid名称列名,必须是unique key
private String name; // seqid名称
private static String valueColumn = "value"; // seqid值列名
private long interval = 200l; // 每次递增的id段
private DbConfigManager dbConfigManager;
private List groupList;
private Map MAX_ID_MAP = new ConcurrentHashMap();
private Map SEQ_ID_MAP = new ConcurrentHashMap();
private Map dsMap = new ConcurrentHashMap();
private Map inActiveGroupName = new ConcurrentHashMap();
private ReadWriteLock lock = new ReentrantReadWriteLock();
private AtomicBoolean inited = new AtomicBoolean();
private AtomicInteger rid = new AtomicInteger();
public void init() throws SQLException {
if (inited.compareAndSet(false, true)) {
if (groupList == null || groupList.isEmpty()) {
throw new RuntimeException("not found db group for sequence ID");
}
for (String group : groupList) {
SGroupDataSource sGroupDataSource = dsMap.get(group);
if (sGroupDataSource == null) {
sGroupDataSource = new SGroupDataSource();
sGroupDataSource.setDbConfigManager(dbConfigManager);
sGroupDataSource.setGroupName(group);
sGroupDataSource.init();
dsMap.put(group, sGroupDataSource);
}
if (null == getIdFromDB(group)) {
initId2DB(group);
}
}
}
}
public long nextSeqId() throws SQLException {
if(!inited.get()){
throw new SQLException("init method not be invoked at first, check your spring bean");
}
String groupName = getGroupName();
if (SEQ_ID_MAP.get(groupName) != null && MAX_ID_MAP.get(groupName) != null) {
try {
lock.readLock().lock();
long id = SEQ_ID_MAP.get(groupName).addAndGet(getStep());
if (id < MAX_ID_MAP.get(groupName)) {
return id;
}
} finally {
lock.readLock().unlock();
}
}
try {
long nextid = updateSeqId(groupName);
inActiveGroupName.remove(groupName);
return nextid;
} catch (SQLException e) {
inActiveGroupName.put(groupName, System.currentTimeMillis());
String retryGroupName = getGroupName();
if (!groupName.equals(retryGroupName)) {
long nextid = updateSeqId(retryGroupName);
inActiveGroupName.remove(retryGroupName);
return nextid;
} else {
throw new SQLException("failed to updateSeqId");
}
}
}
private String getGroupName() {
if (groupList.size() == 1) {
return groupList.get(0);
}
if (rid.get() > Integer.MAX_VALUE - 10000) {
rid.set(0);
}
int index = rid.incrementAndGet() % groupList.size();
String groupName = groupList.get(index);
int retry = 3;
while (retry-- > 0) {
// 3s内不会选择非正常节点
Long lastInActiveTime = inActiveGroupName.get(groupName);
if (lastInActiveTime != null && (System.currentTimeMillis() - lastInActiveTime < 3 * 1000l)) {
index = rid.incrementAndGet() % groupList.size();
groupName = groupList.get(index);
} else {
break;
}
}
return groupName;
}
private int getInitId(String groupName) {
int index = 1;
for (String group : groupList) {
if (group.equals(groupName)) {
return index;
}
index++;
}
return index;
}
private int getStep() {
return groupList.size();
}
private synchronized long updateSeqId(String groupName) throws SQLException {
if (SEQ_ID_MAP.get(groupName) != null) {
long id = SEQ_ID_MAP.get(groupName).addAndGet(getStep());
if (id < MAX_ID_MAP.get(groupName)) {
return id;
}
}
Long seqId = getIdFromDB(groupName);
if (seqId == null) {
throw new RuntimeException("1 not found seq column or table");
}
long addId = interval * getStep();
long curId = seqId;
long maxId = seqId + addId;
if (!updateId2DB(groupName, seqId + addId, seqId)) { // 更新冲突时
seqId = getIdFromDB(groupName);
if (seqId == null) {
throw new RuntimeException("2 init seq id failed");
}
curId = seqId;
maxId = seqId + addId;
if (!updateId2DB(groupName, seqId + addId, seqId)) { // 更新冲突时
throw new RuntimeException("3 update seq id failed");
}
}
try {
lock.writeLock().lock();
MAX_ID_MAP.put(groupName, maxId);
SEQ_ID_MAP.put(groupName, new AtomicLong());
SEQ_ID_MAP.get(groupName).set(curId);
} finally {
lock.writeLock().unlock();
}
return curId;
}
private Long getIdFromDB(String groupName) throws SQLException {
Connection connection = null;
try {
SGroupDataSource sGroupDataSource = dsMap.get(groupName);
connection = sGroupDataSource.getConnection();
StringBuilder sql = new StringBuilder();
sql.append("select ");
sql.append(valueColumn);
sql.append(" from ");
sql.append(table);
sql.append(" where ");
sql.append(nameColumn);
sql.append(" = ?");
PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
preparedStatement.setString(1, name);
ResultSet rs = preparedStatement.executeQuery();
if (rs.next()) {
return rs.getLong(1);
}
} finally {
if (connection != null) {
connection.close();
}
}
return null;
}
private void initId2DB(String groupName) throws SQLException {
Connection connection = null;
try {
SGroupDataSource sGroupDataSource = dsMap.get(groupName);
connection = sGroupDataSource.getConnection();
StringBuilder sql = new StringBuilder();
sql.append("insert into ");
sql.append(table);
sql.append("(");
sql.append(nameColumn);
sql.append(" , ");
sql.append(valueColumn);
sql.append(") values( ?,? )");
PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
preparedStatement.setString(1, name);
preparedStatement.setLong(2, getInitId(groupName));
preparedStatement.execute();
} finally {
if (connection != null) {
connection.close();
}
}
}
private boolean updateId2DB(String groupName, long maxId, long lastMaxId) throws SQLException {
Connection connection = null;
try {
SGroupDataSource sGroupDataSource = dsMap.get(groupName);
connection = sGroupDataSource.getConnection();
StringBuilder sql = new StringBuilder();
sql.append("update ");
sql.append(table);
sql.append(" set ");
sql.append(valueColumn);
sql.append(" = ? ");
sql.append(" where ");
sql.append(nameColumn);
sql.append(" = ? and ");
sql.append(valueColumn);
sql.append(" = ?");
PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
preparedStatement.setLong(1, maxId);
preparedStatement.setString(2, name);
preparedStatement.setLong(3, lastMaxId);
preparedStatement.execute();
return 1 == preparedStatement.getUpdateCount();
} finally {
if (connection != null) {
connection.close();
}
}
}
public void setTable(String table) {
this.table = table;
}
public void setName(String name) {
this.name = name;
}
public void setInterval(long interval) {
this.interval = interval;
}
public void setDbConfigManager(DbConfigManager dbConfigManager) {
this.dbConfigManager = dbConfigManager;
}
public void setGroups(String groups) {
if (StringUtils.isBlank(groups)) {
throw new RuntimeException("groupName is null");
}
String[] ss = groups.split(",");
if (ss != null && ss.length > 0) {
groupList = new ArrayList<>();
for (String s : ss) {
groupList.add(s.trim());
}
Collections.sort(groupList);
} else {
throw new RuntimeException("groupName is invalid");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy