TX-LCN分布式事务框架源码解析(服务端-5-TxLcnInitializer初始化之TMAutoCluster)

最后一个TxLcnInitializer。

TMAutoCluster从名称上看是自动集群,当我们启动一个新的服务端时,不用额外的配置也不需要重新启动客户端。所有的客户端都会感知到新的服务端并且与之链接。

整体的逻辑是这样的,如果启动一个服务端A,启动时这个A会把自己的信息存入redis的hash中,hash为tm.instances,hk为host:TransactionPort,hv为HttpPort。

例:

如果服务端A host 为192.168.120.10 设置的server.port=7970,则redis 中的tm.instances 一个值为KEY 为192.168.120.10:7970,VALUE8070。然后客户端都连接这个服务端A。

过段时间后,又需要启动一个服务端B,启动B时会先去(A服务也会这样)redis上获取tm.instances上所有的值(这里只有服务端A),排除掉自己的信息后(如果有),根据redis存储的信息通过restTemplate以自身的地址信息为参数去调用服务端A,服务端A收到服务端B的请求会给与自己相连的所有的channel发送信息要求所有的客户端连接服务端B,客户端接收到服务端A的消息后会新启动netty客户端去连接服务端B。这就是自动集群的所有流程。

下面我们通过代码看下具体的实现步骤

public void init() throws Exception {

        // 1. 通知 TC 建立连接
        List tmList = fastStorage.findTMProperties().stream()
                .filter(tmProperties ->
                        !tmProperties.getHost().equals(txManagerConfig.getHost()) || !tmProperties.getTransactionPort().equals(txManagerConfig.getPort()))
                .collect(Collectors.toList());
        for (TMProperties properties : tmList) {
            NotifyConnectParams notifyConnectParams = new NotifyConnectParams();
            notifyConnectParams.setHost(txManagerConfig.getHost());
            notifyConnectParams.setPort(txManagerConfig.getPort());
            //构造url
            String url = String.format(MANAGER_REFRESH_URL, properties.getHost(), properties.getHttpPort());
            try {
                //调用其他服务
                ResponseEntity res = restTemplate.postForEntity(url, notifyConnectParams, Boolean.class);
                if (res.getStatusCode().equals(HttpStatus.OK) || res.getStatusCode().is5xxServerError()) {
                    log.info("manager auto refresh res->{}", res);
                    break;
                } else {
                    fastStorage.removeTMProperties(properties.getHost(), properties.getTransactionPort());
                }
            } catch (Exception e) {
                log.error("manager auto refresh error: {}", e.getMessage());
                //check exception then remove.
                if (e instanceof ResourceAccessException) {
                    ResourceAccessException resourceAccessException = (ResourceAccessException) e;
                    if (resourceAccessException.getCause() != null && resourceAccessException.getCause() instanceof ConnectException) {
                        //can't access .
                        fastStorage.removeTMProperties(properties.getHost(), properties.getTransactionPort());
                    }
                }
            }
        }

        // 2. 保存TM 到快速存储
        if (StringUtils.hasText(txManagerConfig.getHost())) {
            TMProperties tmProperties = new TMProperties();
            tmProperties.setHttpPort(ApplicationInformation.serverPort(serverProperties));
            tmProperties.setHost(txManagerConfig.getHost());
            tmProperties.setTransactionPort(txManagerConfig.getPort());
            fastStorage.saveTMProperties(tmProperties);
        }
    }

主要做了两件事

1、获取redis的值,排除自己的信息后用restTemplate去调用所有redis存储的信息地址。

2、把自己的信息保存到redis

调用代码如下

public class TxManagerController {

    @Autowired
    private ManagerService managerService;

    @PostMapping("/refresh")
    public boolean refresh(@RequestBody NotifyConnectParams notifyConnectParams) throws RpcException {
        return managerService.refresh(notifyConnectParams);
    }
}
public boolean refresh(NotifyConnectParams notifyConnectParams) throws RpcException {
        List keys = rpcClient.loadAllRemoteKey();
        if (keys != null && keys.size() > 0) {
            for (String key : keys) {
                rpcClient.send(key, MessageCreator.newTxManager(notifyConnectParams));
            }
        }
        return true;
    }
public List loadAllRemoteKey() {
        List allKeys = new ArrayList<>();
        for (Channel channel : channels) {
            allKeys.add(channel.remoteAddress().toString());
        }
        return allKeys;
    }

可以看到调用是对每一个连接的channel都会去通知

保存TM信息

public void saveTMProperties(TMProperties tmProperties) {
        Objects.requireNonNull(tmProperties);
        stringRedisTemplate.opsForHash().put(REDIS_TM_LIST,
                tmProperties.getHost() + ":" + tmProperties.getTransactionPort(), String.valueOf(tmProperties.getHttpPort()));
    }

可以看到采用的hash结构

你可能感兴趣的:(分布式事物,TX-LCN)