Nacos のアーキテクチャと原則 - アドレス指定メカニズム


ここに画像の説明を挿入


前提

Nacos はスタンドアロン展開とクラスター展開をサポートします

  • スタンドアロン モードの場合、Nacos はそれ自体と通信するだけです。
  • クラスター モードの場合、クラスター内の各 Nacos メンバーは相互に通信する必要があります。

したがって、クラスター内の Nacos メンバー ノードの情報をどのように管理するかという疑問が生じます。これが Nacos の内部アドレス指定メカニズムです。


デザイン

スタンドアロンモードでもクラスターモードでも、基本的な違いはNacosメンバーノードの数が単一か複数かです。

  • ノードが増加したか減少したかなど、ノードの変化を認識できる必要があります。

  • 現在の最新のメンバーリスト情報は何ですか?

  • 会員名簿情報の管理方法

  • より優れた新しいメンバー リスト管理モードを迅速にサポートする方法など。


メンバー検索

上記の要件を考慮して、 MemberLookupインターフェイスは抽象化されています。

ここに画像の説明を挿入

package com.alibaba.nacos.core.cluster;

import com.alibaba.nacos.api.exception.NacosException;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;

/**
 * Member node addressing mode.
 *
 * @author <a href="mailto:[email protected]">liaochuntao</a>
 */
public interface MemberLookup {
    
    
    
    /**
     * start.
     *
     * @throws NacosException NacosException
     */
    void start() throws NacosException;
    
    /**
     * is using address server.
     *
     * @return using address server or not.
     */
    boolean useAddressServer();
    
    /**
     * Inject the ServerMemberManager property.
     *
     * @param memberManager {@link ServerMemberManager}
     */
    void injectMemberManager(ServerMemberManager memberManager);
    
    /**
     * The addressing pattern finds cluster nodes.
     *
     * @param members {@link Collection}
     */
    void afterLookup(Collection<Member> members);
    
    /**
     * Addressing mode closed.
     *
     * @throws NacosException NacosException
     */
    void destroy() throws NacosException;
    
    /**
     * Some data information about the addressing pattern.
     *
     * @return {@link Map}
     */
    default Map<String, Object> info() {
    
    
        return Collections.emptyMap();
    }
    
}

  • ServerMemberManagerノードが認識しているすべてのメンバー ノード リスト情報を保存し、メンバー ノードの追加、削除、変更、およびクエリ操作を提供し、メンバー ノードのアドレス指定方法の動的な切り替えを容易にするために MemberLookup リストを維持します。

  • MemberLookupインターフェイスは非常にシンプルで、コア インターフェイスは と の 2 つだけでinjectMemberManager、前者はストレージとクエリ機能の使用を容易にするためにに注入するafterLookupために使用され、後者はイベント インターフェイスです。現在の最新のノードのメンバーノードリスト情報はこの関数を通じて通知され、特定のノード管理方法は特定の実装に隠蔽されます。ServerMemberManagerMemberLookupServerMemberManagerafterLookupMemberLookupServerMemberManagerMemberLookup


内部実装

ここに画像の説明を挿入

スタンドアロンのアドレス指定 StandaloneMemberLookup

スタンドアロン モードのアドレッシング モードは非常に単純で、実際には、独自の IP:PORT の組み合わせ情報を見つけてノード情報としてフォーマットし、afterLookup を呼び出し、その情報を ServerMemberManager に保存します。

package com.alibaba.nacos.core.cluster.lookup;

import com.alibaba.nacos.core.cluster.AbstractMemberLookup;
import com.alibaba.nacos.core.cluster.MemberUtil;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.sys.utils.InetUtils;

import java.util.Collections;

/**
 * Member node addressing mode in stand-alone mode.
 *
 * @author <a href="mailto:[email protected]">liaochuntao</a>
 */
public class StandaloneMemberLookup extends AbstractMemberLookup {
    
    
    
    @Override
    public void doStart() {
    
    
        String url = InetUtils.getSelfIP() + ":" + EnvUtil.getPort();
        afterLookup(MemberUtil.readServerConf(Collections.singletonList(url)));
    }
    
    @Override
    public boolean useAddressServer() {
    
    
        return false;
    }
}


ファイルのアドレス指定 FileConfigMemberLookup

ファイル アドレッシング モードは、Nacos クラスタ モードのデフォルトのアドレッシング実装です

ファイル アドレッシング モードは非常に単純で、実際、各 Nacos ノードは、cluster.conf というファイルを維持する必要があります。

192.168.16.101:8847
192.168.16.102
192.168.16.103

デフォルトでは、このファイルは各メンバー ノードの IP 情報を入力するだけでよく、ポートは Nacos のデフォルト ポート 8848 を自動的に選択します。特別なニーズのために Nacos のポート情報を変更した場合は、完全な IP 情報を追加する必要があります。ネットワーク 完全なルート アドレス情報 (IP:PORT)。

Nacos ノードが起動すると、ファイルの内容を読み取り、ファイル内の IP を解析してノードのリストを作成し、afterLookup を呼び出して ServerMemberManager に保存します。


/**
 * Cluster.conf file managed cluster member node addressing pattern.
 *
 * @author <a href="mailto:[email protected]">liaochuntao</a>
 */
public class FileConfigMemberLookup extends AbstractMemberLookup {
    
    
    
    private static final String DEFAULT_SEARCH_SEQ = "cluster.conf";
    
    private FileWatcher watcher = new FileWatcher() {
    
    
        @Override
        public void onChange(FileChangeEvent event) {
    
    
            readClusterConfFromDisk();
        }
        
        @Override
        public boolean interest(String context) {
    
    
            return StringUtils.contains(context, DEFAULT_SEARCH_SEQ);
        }
    };
    
    @Override
    public void doStart() throws NacosException {
    
    
        readClusterConfFromDisk();
        
        // Use the inotify mechanism to monitor file changes and automatically
        // trigger the reading of cluster.conf
        try {
    
    
            WatchFileCenter.registerWatcher(EnvUtil.getConfPath(), watcher);
        } catch (Throwable e) {
    
    
            Loggers.CLUSTER.error("An exception occurred in the launch file monitor : {}", e.getMessage());
        }
    }
    
    @Override
    public boolean useAddressServer() {
    
    
        return false;
    }
    
    @Override
    public void destroy() throws NacosException {
    
    
        WatchFileCenter.deregisterWatcher(EnvUtil.getConfPath(), watcher);
    }
    
    private void readClusterConfFromDisk() {
    
    
        Collection<Member> tmpMembers = new ArrayList<>();
        try {
    
    
            List<String> tmp = EnvUtil.readClusterConf();
            tmpMembers = MemberUtil.readServerConf(tmp);
        } catch (Throwable e) {
    
    
            Loggers.CLUSTER
                    .error("nacos-XXXX [serverlist] failed to get serverlist from disk!, error : {}", e.getMessage());
        }
        
        afterLookup(tmpMembers);
    }
}

クラスターが拡大および縮小していることが判明した場合は、各 Nacos ノードの下にあるcluster.conf ファイルを変更する必要があります。その後、Nacos 内のファイル変更監視センターが自動的にファイルの変更を検出し、ファイルの内容を再読み込みし、ロードします。 IPリスト情報を取得し、新しく追加したノードを更新します(FileWatcher)

ただし、このデフォルトのアドレッシング モードには、運用コストとメンテナンス コストが比較的高いという欠点があります。ご想像のとおり、新しい Nacos ノードを追加するときは、各 Nacos ノードの下にあるcluster.conf ファイルを手動で変更する必要があります。作業するか、もう少し高度な場合は、ansible などの自動デプロイメント ツールを使用していずれかのノードのファイルに障害が発生すると、クラスタ間のメンバー ノードのリストでデータの不整合が発生するため、別の新しいアドレッシング モード - アドレス サーバー アドレッシング モードが拡張されました。


アドレスサーバー検索AddressServerMemberLookup

アドレスサーバーアドレッシングモードは、Nacos が公式に推奨するクラスタメンバノードの情報管理の一種で、cluster.conf ファイルの内容情報を簡易 Web サーバーで管理することで、運用保守担当者のみが必要とするモードです。これを管理するには、各 Nacos メンバー ノードは、この Web ノードから最新のクラスター メンバー ノード リスト情報を定期的に要求するだけで済みます。

ここに画像の説明を挿入

public class AddressServerMemberLookup extends AbstractMemberLookup {
    
    
    
    private final GenericType<String> genericType = new GenericType<String>() {
    
     };
    
    public String domainName;
    
    public String addressPort;
    
    public String addressUrl;
    
    public String envIdUrl;
    
    public String addressServerUrl;
    
    private volatile boolean isAddressServerHealth = true;
    
    private int addressServerFailCount = 0;
    
    private int maxFailCount = 12;
    
    private final NacosRestTemplate restTemplate = HttpClientBeanHolder.getNacosRestTemplate(Loggers.CORE);
    
    private volatile boolean shutdown = false;
    
    private static final String HEALTH_CHECK_FAIL_COUNT_PROPERTY = "maxHealthCheckFailCount";
    
    private static final String DEFAULT_HEALTH_CHECK_FAIL_COUNT = "12";
    
    private static final String DEFAULT_SERVER_DOMAIN = "jmenv.tbsite.net";
    
    private static final String DEFAULT_SERVER_POINT = "8080";
    
    private static final int DEFAULT_SERVER_RETRY_TIME = 5;
    
    private static final long DEFAULT_SYNC_TASK_DELAY_MS = 5_000L;
    
    private static final String ADDRESS_SERVER_DOMAIN_ENV = "address_server_domain";
    
    private static final String ADDRESS_SERVER_DOMAIN_PROPERTY = "address.server.domain";
    
    private static final String ADDRESS_SERVER_PORT_ENV = "address_server_port";
    
    private static final String ADDRESS_SERVER_PORT_PROPERTY = "address.server.port";
    
    private static final String ADDRESS_SERVER_URL_ENV = "address_server_url";
    
    private static final String ADDRESS_SERVER_URL_PROPERTY = "address.server.url";
    
    private static final String ADDRESS_SERVER_RETRY_PROPERTY = "nacos.core.address-server.retry";
    
    @Override
    public void doStart() throws NacosException {
    
    
        this.maxFailCount = Integer.parseInt(EnvUtil.getProperty(HEALTH_CHECK_FAIL_COUNT_PROPERTY, DEFAULT_HEALTH_CHECK_FAIL_COUNT));
        initAddressSys();
        run();
    }
    
    @Override
    public boolean useAddressServer() {
    
    
        return true;
    }
    
    private void initAddressSys() {
    
    
        String envDomainName = System.getenv(ADDRESS_SERVER_DOMAIN_ENV);
        if (StringUtils.isBlank(envDomainName)) {
    
    
            domainName = EnvUtil.getProperty(ADDRESS_SERVER_DOMAIN_PROPERTY, DEFAULT_SERVER_DOMAIN);
        } else {
    
    
            domainName = envDomainName;
        }
        String envAddressPort = System.getenv(ADDRESS_SERVER_PORT_ENV);
        if (StringUtils.isBlank(envAddressPort)) {
    
    
            addressPort = EnvUtil.getProperty(ADDRESS_SERVER_PORT_PROPERTY, DEFAULT_SERVER_POINT);
        } else {
    
    
            addressPort = envAddressPort;
        }
        String envAddressUrl = System.getenv(ADDRESS_SERVER_URL_ENV);
        if (StringUtils.isBlank(envAddressUrl)) {
    
    
            addressUrl = EnvUtil.getProperty(ADDRESS_SERVER_URL_PROPERTY, EnvUtil.getContextPath() + "/" + "serverlist");
        } else {
    
    
            addressUrl = envAddressUrl;
        }
        addressServerUrl = "http://" + domainName + ":" + addressPort + addressUrl;
        envIdUrl = "http://" + domainName + ":" + addressPort + "/env";
        
        Loggers.CORE.info("ServerListService address-server port:" + addressPort);
        Loggers.CORE.info("ADDRESS_SERVER_URL:" + addressServerUrl);
    }
    
    @SuppressWarnings("PMD.UndefineMagicConstantRule")
    private void run() throws NacosException {
    
    
        // With the address server, you need to perform a synchronous member node pull at startup
        // Repeat three times, successfully jump out
        boolean success = false;
        Throwable ex = null;
        int maxRetry = EnvUtil.getProperty(ADDRESS_SERVER_RETRY_PROPERTY, Integer.class, DEFAULT_SERVER_RETRY_TIME);
        for (int i = 0; i < maxRetry; i++) {
    
    
            try {
    
    
                syncFromAddressUrl();
                success = true;
                break;
            } catch (Throwable e) {
    
    
                ex = e;
                Loggers.CLUSTER.error("[serverlist] exception, error : {}", ExceptionUtil.getAllExceptionMsg(ex));
            }
        }
        if (!success) {
    
    
            throw new NacosException(NacosException.SERVER_ERROR, ex);
        }
        
        GlobalExecutor.scheduleByCommon(new AddressServerSyncTask(), DEFAULT_SYNC_TASK_DELAY_MS);
    }
    
    @Override
    public void destroy() throws NacosException {
    
    
        shutdown = true;
    }
    
    @Override
    public Map<String, Object> info() {
    
    
        Map<String, Object> info = new HashMap<>(4);
        info.put("addressServerHealth", isAddressServerHealth);
        info.put("addressServerUrl", addressServerUrl);
        info.put("envIdUrl", envIdUrl);
        info.put("addressServerFailCount", addressServerFailCount);
        return info;
    }
    
    private void syncFromAddressUrl() throws Exception {
    
    
        RestResult<String> result = restTemplate
                .get(addressServerUrl, Header.EMPTY, Query.EMPTY, genericType.getType());
        if (result.ok()) {
    
    
            isAddressServerHealth = true;
            Reader reader = new StringReader(result.getData());
            try {
    
    
                afterLookup(MemberUtil.readServerConf(EnvUtil.analyzeClusterConf(reader)));
            } catch (Throwable e) {
    
    
                Loggers.CLUSTER.error("[serverlist] exception for analyzeClusterConf, error : {}",
                        ExceptionUtil.getAllExceptionMsg(e));
            }
            addressServerFailCount = 0;
        } else {
    
    
            addressServerFailCount++;
            if (addressServerFailCount >= maxFailCount) {
    
    
                isAddressServerHealth = false;
            }
            Loggers.CLUSTER.error("[serverlist] failed to get serverlist, error code {}", result.getCode());
        }
    }
    
    class AddressServerSyncTask implements Runnable {
    
    
        
        @Override
        public void run() {
    
    
            if (shutdown) {
    
    
                return;
            }
            try {
    
    
                syncFromAddressUrl();
            } catch (Throwable ex) {
    
    
                addressServerFailCount++;
                if (addressServerFailCount >= maxFailCount) {
    
    
                    isAddressServerHealth = false;
                }
                Loggers.CLUSTER.error("[serverlist] exception, error : {}", ExceptionUtil.getAllExceptionMsg(ex));
            } finally {
    
    
                GlobalExecutor.scheduleByCommon(this, DEFAULT_SYNC_TASK_DELAY_MS);
            }
        }
    }
}

したがって、アドレス サーバー モードは、Nacos クラスター ノードの管理コストを大幅に簡素化すると同時に、アドレス サーバーは非常に単純な Web プログラムであり、そのプログラムの安定性は十分に保証されます。


今後の拡張ポイント

クラスタノードの自動拡張および縮小

現在、Nacos におけるクラスタノードの管理は依然として手動操作であるため、将来的には、アドレッシングモードに基づいてクラスタノードを自動管理する機能が実現されることが期待されています。ノード情報が 1 つだけあれば、一定期間内に元の Nacos クラスターに正常に参加できると同時に、生き残っていないノードを自分で発見し、自動的に削除することもできます。クラスター内の使用可能なノードのリストからそれらを選択します。この部分のロジック実装は、実際には Consul の Gossip プロトコルに似ています。

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/yangshangwei/article/details/131133842