上篇讲了TMRpcServer的init方法,按照执行顺序接下来的TxLcnInitializer类是EnsureIdGenEngine
这是一个id生成器类,用来生成机器id号
下面看init方法
public void init() throws Exception {
managerConfig.applyMachineId(managerService.machineIdSync());
IdGenInit.applyDefaultIdGen(managerConfig.getSeqLen(), managerConfig.getMachineId());
Transactions.setApplicationIdWhenRunning(modIdProvider.modId());
}
做了三件事
1、为managerConfig设置机器id,机器id的生成由manageService#machineIdSync方法生成。无论是服务端或是客户端在启动时候都会生成自己的机器ID,且不能重复。用于唯一标识应用
2、初始化一个id生成器
3、为分布式事物工具类设置当前的运行表示id。
我们一部分一部分看
manageService#machineIdSync
public long machineIdSync() throws TxManagerException {
//最大机器id
long machineMaxSize = ~(-1L << (64 - 1 - managerConfig.getSeqLen())) - 1;
//超时时间
long timeout = managerConfig.getHeartTime() + 2000;
long id;
try {
//通过redis操作获取机器id
id = fastStorage.acquireMachineId(machineMaxSize, timeout);
} catch (FastStorageException e) {
throw new TxManagerException(e);
}
log.info("Acquired machine id {}, max machine id is: {}", id, machineMaxSize);
return id;
}
public long acquireMachineId(long machineMaxSize, long timeout) throws FastStorageException {
try {
//获取锁
acquireGlobalXLock();
stringRedisTemplate.opsForValue().setIfAbsent(REDIS_MACHINE_ID_MAP_PREFIX + "cur_id", "-1");
for (int i = 0; i < machineMaxSize; i++) {
long curId = Objects.requireNonNull(
stringRedisTemplate.opsForValue().increment(REDIS_MACHINE_ID_MAP_PREFIX + "cur_id", 1));
if (curId > machineMaxSize) {
stringRedisTemplate.opsForValue().set(REDIS_MACHINE_ID_MAP_PREFIX + "cur_id", "0");
curId = 0;
}
if (Optional
.ofNullable(stringRedisTemplate.hasKey(REDIS_MACHINE_ID_MAP_PREFIX + curId))
.orElse(true)) {
continue;
}
stringRedisTemplate.opsForValue().set(REDIS_MACHINE_ID_MAP_PREFIX + curId, "", timeout, TimeUnit.MILLISECONDS);
return curId;
}
throw new FastStorageException("non can used machine id", FastStorageException.EX_CODE_NON_MACHINE_ID);
} finally {
//释放锁
releaseGlobalXLock();
}
}
上面就是获取机器id的的步骤
1、先去设置尝试tm.machine.id.gen:cur_id 为-1,如果已经存在tm.machine.id.gen:cur_id为key 的值则不设置,不存在才能设置成功
2、把已经存在的tm.machine.id.gen:cur_id的值自增1
3、判断自增1后的值是否大于最大机器值machineMaxSize,如果大于则设置tm.machine.id.gen:cur_id为0,当前机器id为0
4、判断tm.machine.id.gen:+curid 的key是否存在,为true说明已经有机器用了次id号,需要再次循环加1获取直到不存在
5、设置当前tm.machine.id.gen:+curid的超时时间。返回机器id
这里用了两个redis key 一个是tm.machine.id.gen:cur_id用于存储所有的已经生成的机器ID,会一直存在redis没有过期时间。
另一个是tm.machine.id.gen:+curid 比如tm.machine.id.gen:7 只会存在很短的时间,这样是为了解决在并发场景下,curid大于machineMaxSize导致curid是同一个的情况。
通过以上5个步骤生成的机器id是唯一的且自增。
id生成器
IdGenInit代码如下
public abstract class IdGenInit {
/**
* 原版雪花算法,生成一个long类型的ID
*
* @param machineLen 机器位长度
* @param machineId 机器ID
*/
public static void applySnowFlakeIdGen(int machineLen, long machineId) {
SnowFlakeGenerator.Factory factory = new SnowFlakeGenerator.Factory(machineLen, 0);
SnowFlakeGenerator snowFlakeGenerator = factory.create(0, machineId);
RandomUtils.init(() -> String.valueOf(snowFlakeGenerator.nextId()));
}
/**
* 默认ID生成。和雪花组成一样,加长了机器位,生成了更长的ID
*
* @param seqLen 序列位长度
* @param machineId 机器ID
*/
public static void applyDefaultIdGen(int seqLen, long machineId) {
RandomUtils.init(new DefaultIdGen(seqLen, machineId));
}
}
这里有两个id生成算法一个是雪花算法,另一个是默认id生成策略。关于这些这里不做细讲,有兴趣的可以自己去追下源码
设置modid
modid生成方式前面已经讲了,即spring.application.name:port