Spring CloudAlibabaの読書ノート_4:サービスの登録と発見

サービスの登録と発見

マイクロサービスアーキテクチャでは、ビジネスは複数のマイクロサービスに分割され、各サービスは相互に通信して全体的な機能を完了します。単一障害点を回避するために、マイクロサービスはクラスターベースの高可用性デプロイメントを採用し、サービスコンシューマーは複数のサービスプロバイダーで構成されるクラスターを呼び出す必要があります。サービスプロバイダークラスターでノードのダウンタイムまたはオフラインが発生した場合、サービスコンシューマーのローカル構成漢方薬は、ノードの関連する構成情報を削除します。このとき、サービスアドレス管理、サービス登録、サービス動的認識を主に担当するサービス登録センターを導入する必要があります

アリババナコス

Nacosは、マイクロサービスでの統合構成、サービス登録、および検出の問題を解決するために使用されます。開発者が動的なサービス検出、サービス構成、サービスメタデータ、およびトラフィック管理を迅速に実現するのに役立つ、シンプルで使いやすい機能セットのセットを提供します。その主な機能は次のとおりです。[nacos-1.1.4による説明]
サービス登録の場合、提供される外部サービスインターフェイスアドレスはnacos / v1 / ns / instance、対応するソースコード:com.alibaba.nacos.naming.controllers.InstanceControllerです。

  • サービス登録:register()
@CanDistro
@PostMapping
public String register(HttpServletRequest request) throws Exception {
    
    

    String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
    String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);

    serviceManager.registerInstance(namespaceId, serviceName, parseInstance(request));
    return "ok";
}

ポジショニング:registerInstance()メソッドは、主に次の操作を実行します。

  • 空のサービスを作成し、serviceMapを初期化します[基本的に:ConcurrentHashMap ]
  • サービスオブジェクトを取得しますから、ルート虎namespaceIdでserviceNameをserviceMap
  • addInstance()呼び出して、サービスインスタンスを追加します
public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
    
    
    createEmptyService(namespaceId, serviceName, instance.isEphemeral());
    Service service = getService(namespaceId, serviceName);
    if (service == null) {
    
    
        throw new NacosException(NacosException.INVALID_PARAM,
                                 "service not found, namespace: " + namespaceId + ", service: " + serviceName);
    }
    addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
}

ポジショニング:createEmptyService()、つまり:createServiceIfAbsent()メソッド。主に以下を実装します。

  • namespaceIdserviceNameを介してキャッシュからサービスインスタンスを取得しますインスタンスが空の場合、インスタンスが作成され、キャッシュに保存されます
public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local, Cluster cluster) throws NacosException {
    
    
    Service service = getService(namespaceId, serviceName);
    if (service == null) {
    
    

        Loggers.SRV_LOG.info("creating empty service {}:{}", namespaceId, serviceName);
        service = new Service();
        service.setName(serviceName);
        service.setNamespaceId(namespaceId);
        service.setGroupName(NamingUtils.getGroupName(serviceName));
        // now validate the service. if failed, exception will be thrown
        service.setLastModifiedMillis(System.currentTimeMillis());
        service.recalculateChecksum();
        if (cluster != null) {
    
    
            cluster.setService(service);
            service.getClusterMap().put(cluster.getName(), cluster);
        }
        service.validate();

        putServiceAndInit(service);
        if (!local) {
    
    
            addOrReplaceService(service);
        }
    }
}

以下に注目しましょう:putServiceAndInit()メソッド。これは以下を実装します。

  • putServiceメソッドを介してサービスをメモリにキャッシュします
  • service.init()を介してハートビート検出メカニズム確立します
  • 整合性サービス .listenはデータ整合性監視を実装します
private void putServiceAndInit(Service service) throws NacosException {
    
    
    // 将服务缓存到内存,将service保存到serviceMap中
    putService(service);
    // 建立服务下包含实例心跳检测机制,每5s发送一次心跳包,不断检测当前服务下所有实例的状态,若请求超时,表明服务不健康,触发服务变更事件
    service.init();
    // 实现数据一致性监听
    consistencyService.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), true), service);
    consistencyService.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), false), service);
    Loggers.SRV_LOG.info("[NEW-SERVICE] {}", service.toJSON());
}
  • サービスアドレス管理クエリ:list()、リクエストパラメータの解析、doSrvIPXTを介したサービスリストデータの返送
@GetMapping("/list")
public JSONObject list(HttpServletRequest request) throws Exception {
    
    

    String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
                                           Constants.DEFAULT_NAMESPACE_ID);
    String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
    String agent = WebUtils.getUserAgent(request);
    String clusters = WebUtils.optional(request, "clusters", StringUtils.EMPTY);
    String clientIP = WebUtils.optional(request, "clientIP", StringUtils.EMPTY);
    Integer udpPort = Integer.parseInt(WebUtils.optional(request, "udpPort", "0"));
    String env = WebUtils.optional(request, "env", StringUtils.EMPTY);
    boolean isCheck = Boolean.parseBoolean(WebUtils.optional(request, "isCheck", "false"));
    String app = WebUtils.optional(request, "app", StringUtils.EMPTY);
    String tenant = WebUtils.optional(request, "tid", StringUtils.EMPTY);
    boolean healthyOnly = Boolean.parseBoolean(WebUtils.optional(request, "healthyOnly", "false"));
    // 主要实现方法
    return doSrvIPXT(namespaceId, serviceName, agent, clusters, clientIP, udpPort, env, isCheck, app, tenant,
                     healthyOnly);
}

主にnamespaceIdとserviceNameに基づいてServiceインスタンスを取得し、インスタンスsrvIPに基づいてすべてのサービスプロバイダーのインスタンス情報を取得するdoSrvIPXT()メソッドに注目しましょう

public JSONObject doSrvIPXT(String namespaceId, String serviceName, String agent, String clusters, String clientIP,
                                int udpPort,
                                String env, boolean isCheck, String app, String tid, boolean healthyOnly) throws Exception {
    
    
	ClientInfo clientInfo = new ClientInfo(agent);
	JSONObject result = new JSONObject();
	Service service = serviceManager.getService(namespaceId, serviceName);
	List<Instance> srvedIPs;
    // 获取指定服务下的所有实例IP
	srvedIPs = service.srvIPs(Arrays.asList(StringUtils.split(clusters, ",")));

	Map<Boolean, List<Instance>> ipMap = new HashMap<>(2);
	ipMap.put(Boolean.TRUE, new ArrayList<>());
	ipMap.put(Boolean.FALSE, new ArrayList<>());

	for (Instance ip : srvedIPs) {
    
    
		ipMap.get(ip.isHealthy()).add(ip);
	}
 
	for (Map.Entry<Boolean, List<Instance>> entry : ipMap.entrySet()) {
    
    
		List<Instance> ips = entry.getValue();

		if (healthyOnly && !entry.getKey()) {
    
    
			continue;
		}

		for (Instance instance : ips) {
    
    
			// remove disabled instance:
			if (!instance.isEnabled()) {
    
    
				continue;
			}
			JSONObject ipObj = new JSONObject();
			ipObj.put("ip", instance.getIp());
			ipObj.put("port", instance.getPort());
			// deprecated since nacos 1.0.0:
			ipObj.put("valid", entry.getKey());
			ipObj.put("healthy", entry.getKey());
			ipObj.put("marked", instance.isMarked());
			ipObj.put("instanceId", instance.getInstanceId());
			ipObj.put("metadata", instance.getMetadata());
			ipObj.put("enabled", instance.isEnabled());
			ipObj.put("weight", instance.getWeight());
			ipObj.put("clusterName", instance.getClusterName());
			if (clientInfo.type == ClientInfo.ClientType.JAVA &&
				clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
    
    
				ipObj.put("serviceName", instance.getServiceName());
			} else {
    
    
				ipObj.put("serviceName", NamingUtils.getServiceName(instance.getServiceName()));
			}

			ipObj.put("ephemeral", instance.isEphemeral());
			hosts.add(ipObj);

		}
	}
	result.put("hosts", hosts);
	if (clientInfo.type == ClientInfo.ClientType.JAVA &&
		clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
    
    
		result.put("dom", serviceName);
	} else {
    
    
		result.put("dom", NamingUtils.getServiceName(serviceName));
	}
	result.put("name", serviceName);
	result.put("cacheMillis", cacheMillis);
	result.put("lastRefTime", System.currentTimeMillis());
	result.put("checksum", service.getChecksum());
	result.put("useSpecifiedURL", false);
	result.put("clusters", clusters);
	result.put("env", env);
	result.put("metadata", service.getMetadata());
	return result;
}
  • サービスの動的認識
    • クライアントがイベントサブスクリプションを開始した後、NacosクライアントにHostReactorクラスがあります。これは、サービスの動的更新を実装するために使用されます。これには、サーバー側のサービスアドレスリストを取得するために10秒ごとにプルリクエストを送信するUpdateTaskサイトが含まれています。
    • Naocs Serverの場合、ハートビート検出はサーバーとサービスプロバイダーのインスタンスの間で維持されます。サービスプロバイダーのインスタンスが異常になると、サービスコンシューマーであるNacosクライアントに通知されます。
    • リクエストを受信した後、Nacosクライアントサービスコンシューマーは、HostReactorで提供されるprocessServiceJSONを使用して情報を解析し、サーバーサービスのアドレスリストをタイムリーに更新します。


Nacos実装アーキテクチャ図

ここに画像の説明を挿入
アーキテクチャ図から、次のことがわかります。

  • プロバイダーAPP:サービスプロバイダー
  • コンシューマーアプリ:サービスコンシューマー
  • ネームサーバー:VritualIPまたはDNSによって実装されたNacos高可用性クラスターサービスルーティング
  • Nacosサーバー:機能アクセスエントリとしてOpen APIを含むNacosサービスプロバイダー。ConfigServiceとNamingServiceは、Nacosが提供する構成および命名サービスモジュールです。ConsistencyProtocolは、Nacosクラスターノード間のデータ同期を実現するために使用される一貫性プロトコルです。
  • Nacosコンソール:コンソールアプリケーション

レジストリの機能は主に以下に反映されます。

  • サービスインスタンスは、起動時にサービスレジストリに登録され、シャットダウン時に登録解除されます。
  • サービスコンシューマーは、サービスレジストリにクエリを実行して、使用可能なインスタンスを取得します。
  • サービスレジストリは、サービスインスタンスのヘルスチェックAPIを呼び出して、リクエストを処理できるかどうかを確認する必要があります。特定のサービス登録と検出のケースコードについては、Githubを参照してください
    ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/Nerver_77/article/details/108258357