问题描述
(1)有一个提供唯一ID的项目
(2)项目根据项目名称和表名称,使用REDIS做为全局锁(REDIS单线程特性,每次取一批号)
(3)项目额外提供一个工具包,给使用者调用获取下一个ID
下面的代码就是“工具包给使用者调用获取下一个ID”,求各位大神指点下
(1)idProviderApi是常规feign http方法,去ID服务获取一批号
(2)getNextId就获取下一个ID
(3)分为主MAP和备份MAP,都是存储一批号,主的用于直接提供ID,备份MAP用于当主的没有号的时候提供给主MAP用,然后把备份MAP清空
(4)有定时器定时检测备份MAP是否有数据,如无,去ID项目获取
自己测试下,一个节点N个线程,暂时没出现获取ID是相同的,请各位大神指点下
相关代码
package com.jiangdaxian.project.biz;
import com.jiangdaxian.project.api.IdProviderApi;
import com.jiangdaxian.project.dto.IdProviderDto;
import com.jiangdaxian.project.request.IdProviderRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Component
public class GetIdProviderInfo {
private static final String SPLIT = "_________";
//主存储数组
Map map = new HashMap();
//备用存储数组,当主存储第一次有数据时候,备用数组同时加KEY
Map backMap = new HashMap();
//每个KEY的锁
Map lockMap = new HashMap();
@Autowired
private IdProviderApi idProviderApi;
/**
* 获取下一个ID
* @param projectName,项目名称
* @param tableName,表名称
* @return
* @throws Exception
*/
public Long getNextId(String projectName,String tableName) throws Exception {
String key = getKey(projectName,tableName);
if(map.containsKey(key)==false){
//针对初始化
synchronized (key.intern()){
if(map.containsKey(key)==false) {
getNextIdProvider(projectName, tableName,true);
lockMap.put(getKey(projectName,tableName),new ReentrantLock());
getNextIdProvider(projectName, tableName,false);
}
}
}
IdProviderObj obj = map.get(key);
Lock lock = lockMap.get(key);
try {
//独占锁,10秒内拿不到直接出错
boolean result = lock.tryLock(10, TimeUnit.SECONDS);
if (result == false) {
throw new Exception("取号超时");
}
return getId(obj,projectName, tableName);
}catch(Exception e){
e.printStackTrace();
throw e;
}finally {
lock.unlock();
}
}
private Long getId(IdProviderObj obj,String projectName,String tableName) throws Exception {
long nextId = obj.getNowId().longValue();
obj.getNowId().increment();
if (nextId > (obj.getIdProviderDto().getIdEnd())) {
//重新取号,如果备份MAP有,用备份MAP的顶上,否则直接获取
IdProviderObj getObj= backMap.get(getKey(projectName,tableName));
if(getObj!=null && getObj.getIdProviderDto()!=null){
//如果备份有,移动到主的MAP上,备份的清空
map.put(getKey(projectName,tableName),getObj);
backMap.put(getKey(projectName,tableName),null);
System.out.println("obj " + getKey(projectName,tableName) + ",getId时候,从backMap获取");
return getId(getObj,projectName, tableName);
}else{
//如果访问太大,导致备份的来不及定时补充信息的话,直接去ID服务拿
IdProviderObj idProviderObj = getNextIdProvider(projectName, tableName,true);
System.out.println("obj " + getKey(projectName,tableName) + ",getId时候,从远程获取");
return getId(idProviderObj,projectName,tableName);
}
} else {
//正常取号
return nextId;
}
}
/**
*
* @param projectName
* @param tableName
* @param isSetBackNull,保存在哪里,TRUE,保存在主MAP;FALSE,保存在备份MAP
* @return
* @throws Exception
*/
private IdProviderObj getNextIdProvider(String projectName, String tableName,boolean isSetBackNull) throws Exception {
IdProviderObj idProviderObj = new IdProviderObj();
try {
IdProviderRequest idProviderRequest = new IdProviderRequest();
idProviderRequest.setTableName(tableName);
idProviderRequest.setProjectName(projectName);
IdProviderDto dto = idProviderApi.get(idProviderRequest);
if(dto!=null && dto.getId()!=null) {
idProviderObj.setIdProviderDto(dto);
LongAdder la = new LongAdder();
la.add(dto.getIdStart());
idProviderObj.setNowId(la);
if(isSetBackNull==true) {
map.put(getKey(projectName,tableName), idProviderObj);
backMap.put(getKey(projectName, tableName), null);
}else{
backMap.put(getKey(projectName, tableName), idProviderObj);
}
return idProviderObj;
}else{
throw new Exception();
}
}catch(Exception e){
e.printStackTrace();
throw e;
}
}
private class IdProviderObj{
private IdProviderDto idProviderDto = null;
private LongAdder nowId;
public IdProviderDto getIdProviderDto() {
return idProviderDto;
}
public void setIdProviderDto(IdProviderDto idProviderDto) {
this.idProviderDto = idProviderDto;
}
public LongAdder getNowId() {
return nowId;
}
public void setNowId(LongAdder nowId) {
this.nowId = nowId;
}
}
private class NameObj{
private String projectName;
private String tableName;
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
}
private String getKey(String projectName,String tableName){
return projectName + SPLIT+tableName;
}
private NameObj getNameObj(String str){
NameObj nameObj = new NameObj();
String []arr = str.split(SPLIT);
nameObj.setProjectName(arr[0]);
nameObj.setTableName(arr[1]);
return nameObj;
}
@Scheduled(cron = "0/10 * * * * *")
public void addIdProviderDto(){
backMap.entrySet().parallelStream().forEach(obj->{
if(obj.getValue()==null){
System.out.println("obj " + obj.getKey() + ",没有数据,正在定时补充数据");
NameObj nameObj = getNameObj(obj.getKey());
try {
getNextIdProvider(nameObj.getProjectName(),nameObj.getTableName(),false);
} catch (Exception e) {
e.printStackTrace();
}
}else{
System.out.println("obj " + obj.getKey() + ",有数据,无需处理");
}
});
}
}