核心类及代码如下:
private static IdGeneratorFactoryClient client = IdGeneratorFactoryClient.getInstance(null);
public class IdGeneratorFactoryClient extends AbstractIdGeneratorFactory {
//引用客户端项目中配置文件
private static final String DEFAULT_PROP = "tinyid_client.properties";
//请求服务端的url
private static String serverUrl = "http://{0}/tinyid/id/nextSegmentIdSimple?token={1}&bizType=";
//初始化信息
public static IdGeneratorFactoryClient getInstance(String location) {
if (idGeneratorFactoryClient == null) {
synchronized (IdGeneratorFactoryClient.class) {
if (idGeneratorFactoryClient == null) {
if (location == null || "".equals(location)) {
init(DEFAULT_PROP);
} else {
init(location);
}
}
}
}
return idGeneratorFactoryClient;
}
//封装数据
private static void init(String location) {
idGeneratorFactoryClient = new IdGeneratorFactoryClient();
Properties properties = PropertiesLoader.loadProperties(location);
String tinyIdToken = properties.getProperty("tinyid.token");
String tinyIdServer = properties.getProperty("tinyid.server");
String readTimeout = properties.getProperty("tinyid.readTimeout");
String connectTimeout = properties.getProperty("tinyid.connectTimeout");
}
}
核心类及代码如下:
public class TinyId {
public static Long nextId(String bizType) {
if (bizType == null) {
throw new IllegalArgumentException("type is null");
}
//最终目的就是初始化SegmentId对象,获取id相关信息
IdGenerator idGenerator = client.getIdGenerator(bizType);
....
....
}
}
public abstract class AbstractIdGeneratorFactory implements IdGeneratorFactory {
private static ConcurrentHashMap<String, IdGenerator> generators = new ConcurrentHashMap<>();
//优先查询缓存中是否存在Id信息
@Override
public IdGenerator getIdGenerator(String bizType) {
if (generators.containsKey(bizType)) {
return generators.get(bizType);
}
synchronized (this) {
if (generators.containsKey(bizType)) {
return generators.get(bizType);
}
//不存在则发起Http请求获取
IdGenerator idGenerator = createIdGenerator(bizType);
generators.put(bizType, idGenerator);
return idGenerator;
}
}
/**
* 根据bizType创建id生成器
*
* @param bizType
* @return
*/
protected abstract IdGenerator createIdGenerator(String bizType);
}
public class IdGeneratorFactoryClient extends AbstractIdGeneratorFactory {
@Override
protected IdGenerator createIdGenerator(String bizType) {
return new CachedIdGenerator(bizType, new HttpSegmentIdServiceImpl());
}
}
public class CachedIdGenerator implements IdGenerator {
public CachedIdGenerator(String bizType, SegmentIdService segmentIdService) {
this.bizType = bizType;
this.segmentIdService = segmentIdService;
loadCurrent();
}
//初始化数据,在每次启动项目时都会进行
public synchronized void loadCurrent() {
if (current == null || !current.useful()) {
if (next == null) {
//从服务端获取id信息
SegmentId segmentId = querySegmentId();
this.current = segmentId;
} else {
current = next;
next = null;
}
}
}
private SegmentId querySegmentId() {
String message = null;
try {
//调用HttpSegmentIdServiceImpl的getNextSegmentId方法,从服务端获取id信息
SegmentId segmentId = segmentIdService.getNextSegmentId(bizType);
if (segmentId != null) {
return segmentId;
}
} catch (Exception e) {
message = e.getMessage();
}
throw new TinyIdSysException("error query segmentId: " + message);
}
}
public class HttpSegmentIdServiceImpl implements SegmentIdService {
private static final Logger logger = Logger.getLogger(HttpSegmentIdServiceImpl.class.getName());
@Override
public SegmentId getNextSegmentId(String bizType) {
String url = chooseService(bizType);
/*
post请求服务端,获取数据,服务端返回各个如下:
"currentId,loadingid,maxId,delta,remainder",比如:"1,20,100,1,1"
currentId:当前ID
loadingid:当该值小于当前ID时会请求一次服务请求,更新Id信息,当前Id+步长*0.2
maxId:当前能够获取的最大Id
delta:每次id增量
remainder:余数量
*/
String response = TinyIdHttpUtils.post(url, TinyIdClientConfig.getInstance().getReadTimeout(),
TinyIdClientConfig.getInstance().getConnectTimeout());
logger.info("tinyId client getNextSegmentId end, response:" + response);
if (response == null || "".equals(response.trim())) {
return null;
}
SegmentId segmentId = new SegmentId();
String[] arr = response.split(",");
segmentId.setCurrentId(new AtomicLong(Long.parseLong(arr[0])));
segmentId.setLoadingId(Long.parseLong(arr[1]));
segmentId.setMaxId(Long.parseLong(arr[2]));
segmentId.setDelta(Integer.parseInt(arr[3]));
segmentId.setRemainder(Integer.parseInt(arr[4]));
return segmentId;
}
}
public class TinyId {
public static Long nextId(String bizType) {
if (bizType == null) {
throw new IllegalArgumentException("type is null");
}
...
...
//获取id
return idGenerator.nextId();
}
}
public class CachedIdGenerator implements IdGenerator {
@Override
public Long nextId() {
while (true) {
if (current == null) {
loadCurrent();
continue;
}
//获取下一次Id
Result result = current.nextId();
//大于最大Id,重新进行初始化
if (result.getCode() == ResultCode.OVER) {
loadCurrent();
} else {
if (result.getCode() == ResultCode.LOADING) {
//更新下一次数据Id信息,loadingId,只是让数据库提前更新Id信息,保证系统的健壮性
loadNext();
}
//无论是否大于loadingId都会返回当前Id
return result.getId();
}
}
}
}
//更新数据Id信息,loadingId,只是让数据库提前更新Id信息,保证系统的健壮性
public void loadNext() {
if (next == null && !isLoadingNext) {
synchronized (lock) {
if (next == null && !isLoadingNext) {
isLoadingNext = true;
executorService.submit(new Runnable() {
@Override
public void run() {
try {
// 无论获取下个segmentId成功与否,都要将isLoadingNext赋值为false
next = querySegmentId();
} finally {
isLoadingNext = false;
}
}
});
}
}
}
}
public class SegmentId {
public Result nextId() {
init();
//本地获取Id值,返回的Id为Id的增量
long id = currentId.addAndGet(delta);
//大于最大Id
if (id > maxId) {
return new Result(ResultCode.OVER, id);
}
//大于loadingId:当前ID+步长*0.2
if (id >= loadingId) {
return new Result(ResultCode.LOADING, id);
}
//正常
return new Result(ResultCode.NORMAL, id);
}
}
具体流程如下:
1、实例化client对象—>读取配置文件,请求服务端的封装URL信息。
2、初始化ID信息—>判断缓存是否存在,存在则直接返回,—>不存在则请求服务端获取ID信息,存入本地。
3、获取Id—>判断是否进行了初始化,没有则进行 ,—>获取Id时,判断是否大于最大id,判断是否大于loadingId,如果大于最大Id则重新初始化,—>如果loadingId则请求服务端更新Id信息,但是还是会返回当前Id,只是让数据库提前更新Id信息,保证系统的健壮性。
客户端加载Id时,先判断是否存在本地缓存,如何没有则从服务端获取,并且存入本地。 客户端进行一次http会返回5个属性:currentId,loadingid,maxId,delta,remainder 当客户端存在缓存时,通过currentId.addAndGet(delta); 如何获取最新的Id值,大于loadingId(http返回的当前+步长*0.2)时,客户端会重新发起http请求,目的时让数据库更新信息,保证系统的健壮性。
客户端封装的URL,就是该请求:nextSegmentIdSimple
@RequestMapping("nextSegmentIdSimple")
public String nextSegmentIdSimple(String bizType, String token) {
logger.info("nextSegmentIdSimple ==== " + bizType);
//对token进行验证
if (!tinyIdTokenService.canVisit(bizType, token)) {
return "";
}
String response = "";
try {
SegmentId segmentId = segmentIdService.getNextSegmentId(bizType);
response = segmentId.getCurrentId() + "," + segmentId.getLoadingId() + "," + segmentId.getMaxId()
+ "," + segmentId.getDelta() + "," + segmentId.getRemainder();
} catch (Exception e) {
logger.error("nextSegmentIdSimple error", e);
}
return response;
}
请求数据库,获取当前的ID信息,及更新ID信息
public class DbSegmentIdServiceImpl implements SegmentIdService {
public SegmentId getNextSegmentId(String bizType) {
// 获取nextTinyId的时候,有可能存在version冲突,需要重试
for (int i = 0; i < Constants.RETRY; i++) {
//获取ID信息
TinyIdInfo tinyIdInfo = tinyIdInfoDAO.queryByBizType(bizType);
if (tinyIdInfo == null) {
throw new TinyIdSysException("can not find biztype:" + bizType);
}
Long newMaxId = tinyIdInfo.getMaxId() + tinyIdInfo.getStep();
Long oldMaxId = tinyIdInfo.getMaxId();
//更新ID最大值
int row = tinyIdInfoDAO.updateMaxId(tinyIdInfo.getId(), newMaxId, oldMaxId, tinyIdInfo.getVersion(),
tinyIdInfo.getBizType());
if (row == 1) {
tinyIdInfo.setMaxId(newMaxId);
//封装数据返回到客户端
SegmentId segmentId = convert(tinyIdInfo);
logger.info("getNextSegmentId success tinyIdInfo:{} current:{}", tinyIdInfo, segmentId);
return segmentId;
} else {
logger.info("getNextSegmentId conflict tinyIdInfo:{}", tinyIdInfo);
}
}
throw new TinyIdSysException("get next segmentId conflict");
}
//封装数据
public SegmentId convert(TinyIdInfo idInfo) {
SegmentId segmentId = new SegmentId();
segmentId.setCurrentId(new AtomicLong(idInfo.getMaxId() - idInfo.getStep()));
segmentId.setMaxId(idInfo.getMaxId());
segmentId.setRemainder(idInfo.getRemainder() == null ? 0 : idInfo.getRemainder());
segmentId.setDelta(idInfo.getDelta() == null ? 1 : idInfo.getDelta());
//设置LoadingId值,默认为达到步长的20%,客户端获取的当前Id>LoadingId时就会再次发起服务端请求
segmentId.setLoadingId(segmentId.getCurrentId().get() + idInfo.getStep() * Constants.LOADING_PERCENT / 100);
return segmentId;
}
}
具体时间可以自定义,并且也可以取消Token信息验证
public class TinyIdTokenServiceImpl implements TinyIdTokenService {
/**
* 1分钟刷新一次token
*/
@Scheduled(cron = "0 0/1 * * * ?")
public void refresh() {
logger.info("refresh token begin");
init();
}
@PostConstruct
private synchronized void init() {
logger.info("tinyId token init begin");
List<TinyIdToken> list = queryAll();
Map<String, Set<String>> map = converToMap(list);
token2bizTypes = map;
logger.info("tinyId token init success, token size:{}", list == null ? 0 : list.size());
}
}