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<TMProperties> 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<Boolean> 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<String> keys = rpcClient.loadAllRemoteKey();
        if (keys != null && keys.size() > 0) {
            for (String key : keys) {
                rpcClient.send(key, MessageCreator.newTxManager(notifyConnectParams));
            }
        }
        return true;
    }
public List<String> loadAllRemoteKey() {
        List<String> 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結構

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章