注册中心
注册中心的实现有很多种方式, 如下图所示。
这是注册中心定义的接口, 分别给不同的角色使用。 例如协调者: 注册,取消注册,关闭。 事务管理器和资源管理器: 订阅,取消订阅,查找
public interface RegistryService<T> {
// 注册
void register(InetSocketAddress address) throws Exception;
// 取消注册
void unregister(InetSocketAddress address) throws Exception;
// 订阅
void subscribe(String cluster, T listener) throws Exception;
// 取消订阅
void unsubscribe(String cluster, T listener) throws Exception;
// 查找
List<InetSocketAddress> lookup(String key) throws Exception;
// 关闭
void close() throws Exception;
// 获得服务组名称
default String getServiceGroup(String key) {
key = PREFIX_SERVICE_ROOT + CONFIG_SPLIT_CHAR + PREFIX_SERVICE_MAPPING + key;
if (!SERVICE_GROUP_NAME.contains(key)) {
ConfigurationCache.addConfigListener(key);
SERVICE_GROUP_NAME.add(key);
}
return ConfigurationFactory.getInstance().getConfig(key);
}
Set<String> SERVICE_GROUP_NAME = new HashSet<>();
}
注册中心有什么意义呢?
注册中心主要就是为了让TM,RM动态感知协调者的变更。 在向协调者发送请求的时候,会从注册中心里的协调者列表用负载均衡算法选择一个协调者,然后发送请求。 从这里可以看出协调者必须是对等的, 每个请求发给哪一台协调者都是一样的。
例如seata中的各个角色和注册中心的交互图:
zookeeper注册中心加载流程
在创建 GlobalTransactionScanner 时,会确认事物分组名称,例如: my_test_tx_group
注册中心的加载
接下来看获取注册中心的协调者(TC)列表
- 首先从本地缓存中获取, 不存在从注册中心获取
- 通过配置中心获取集群名称: service.vgroupMapping.my_test_tx_group = default
- 获取协调者列表, zk的节点数据: /registry/zk/default = [192.168.1.103:8091]
- 将协调者列表加入本地缓存
负载均衡
负载均衡算法在1.3.0 还没有最少使用算法和一致性hash算法, 这里是1.4.0 的负载均衡图
负载均衡算法,用来在多台协调者中选择一台机器,将消息发送给协调者。 例如TC1,TC2,TC3 通过负载均衡算法选择了TC2, 将全局事物开启请求发送给TC2。
当只有一台机器的时候,是不会采用任何负载均衡算法的
随机
从当前机器总数5, 通过 ThreadLocalRandom 随机一个下标, 例如 随机到 3, 选择3的下标,就是第四台机器
最少使用
1.4.0 的负载均衡算法
最少使用的算法由RpcStatus实现,RemotingClient 内部有通过SPI机制加载的列表: List<RpcHook>,
发消息前对RpcStatus计数+1, 收到响应后对计数-1
发消息变更 RpcStatus 的流程
首先遍历Invoker, 然后通过RpcStatus拿到对应的调用次数。
首先过滤到一组最少的调用次数,如果只有一个,就只用这一个, 如果有多个最少的,就随机一个。
例如有五台机器的调用次数如下: (4,8,9,29,4), 筛选到新数组(0,5) , 新数组的内容是老数组的下标, 然后新数组随机一个值: 下标1, 取得里面的值:5. 那么就取原数组的第5个下标。 最终调用第五台机器。
例如有五台机器调用次数如下: (5,8,19,6,2), 只有2是最少的, 所以调用第五台机器
一致性哈希
1.4.0 的负载均衡算法
在 [0,Integer,MAX_VALUE) 范围看成一个环, 如果环上用实际节点,很容易会不均匀。 例如 0,MAX_VALUE-100 属于节点A, 其他的属于节点B。 那么这种负载均衡算法就没有意义了, 99.99%都是选择节点A。
为了解决节点分布不均匀的问题, 引入了虚拟节点。 例如原先的节点A的hash是MAX_VALUE-1000, 虚拟节点就是计算 节点A+1,节点A+2,节点A+n 重新计算的hash值, 会有更大概率分布在整个环上。 根据调用的全局事物ID 计算一个hash值, 找到环上的下一个虚拟几点, 最后找到实际节点进行调用。
一致性hash听起来还是很高大上的,实现方式一个是虚拟节点的hash计算, 另一个是用 SortedMap.tailMap 找到下一个hash值
轮询
先从 [0,Integer.MAX_VALUE) 取值, 然后用当前值 % 机器总数
例如轮询到 18, 总共 5 太机器 , 18%5 = 3, 选择3的下标,就是第四台机器