Dubbo 3.0サーバー露出の全プロセスの詳細な分析

Dubbo 3.0サーバー露出の全プロセスの詳細な分析

背景

クラウドネイティブ時代の到来に伴い、Dubbo 3.0 の重要な目標はクラウドネイティブを完全に採用することです。このため、クラウド ネイティブへの適応性を高めるために、Dubbo 3.0 では、元のインターフェイス レベルのサービス検出メカニズムをアプリケーション レベルのサービス検出メカニズムに進化させました。

アプリケーションレベルのサービス検出メカニズムに基づいて、Dubbo 3.0 はフレームワークによって発生する追加のリソース消費を大幅に削減し、リソース使用率を大幅に向上させることができます。これは主に次の点に反映されます。

単一マシンの常駐メモリが75%減少

数百万のクラスターインスタンスをサポートできるクラスター登録センターの全体的なデータ量は 90% 以上減少しました。
Dubbo サーバーの公開プロセスに関する技術記事は多数ありますが、それらはすべて Dubbo インターフェース レベルのサービス検出メカニズムに基づいて解釈されています。 Dubbo 3.0 のアプリケーション レベルのサービス検出メカニズムでは、サーバー公開プロセスが以前とは大きく変わりました。この記事では、Dubbo 3.0 のソースコードを理解することで、サーバーの公開プロセス全体を分析したいと考えています。

アプリケーションレベルのサービス検出とは何ですか?

簡単に言えば、Dubbo はすべてのインターフェース情報を登録センターに登録していましたが、アプリケーション インスタンスには通常複数のインターフェースがあるため、登録されるデータの量が非常に多く、冗長になっています。アプリケーション レベルのサービス検出のメカニズムでは、同じアプリケーション インスタンスが登録センターに 1 つのデータのみを登録します。このメカニズムは主に以下の問題を解決します。

Spring Cloudなどの主流のマイクロサービスモデルに合わせる
Kubernetes ネイティブ サービスをサポートします。 Kubernetes で維持およびスケジュールされるサービスはすべて、インターフェース レベルではなく、アプリケーション インスタンス レベルに基づいています。登録センターのデータ保存容量を削減し、住所変更プッシュの圧力を軽減します。アプリケーション dubbo-application が 3 つのインスタンス (instance1、instance2、instance3) を展開し、異なるタイムアウトが設定された 3 つのインターフェース (sayHello、echo、getVersion) を提供するとします。インターフェース レベルとアプリケーション レベルのサービス検出メカニズムでは、登録センターに登録されるデータは完全に異なります。次の図に示すように:

インターフェースレベルのサービス検出メカニズムのレジストリ内のデータ

  1. "sayHello" : [ { "application" : "dubbo-application" "name" : "instance1" "ip" : "127.0.0.1" "metadata" :{ "timeout" : 1000 }}, { "application" : "dubbo-application" "name" : "instance2" "ip" : "127.0.0.2" "metadata" :{ "timeout" : 2000 }}, { "application" : "dubbo-application" "name" : "instance3" "ip" : "127.0.0.3" "metadata" :{ "timeout" : 3000 }},], "echo" : [ { "application" : "dubbo-application" , "name" : "instance1" , "ip" : "127.0.0.1" , "metadata" :{ "timeout" : 1000 }}, { "application" : "dubbo-application" , "name" : "instance2" , "ip" : "127.0.0.2" , "metadata" :{ "timeout" : 2000 }}, { "application" : "dubbo-application" , "name" : "instance3" , "ip" : "127.0.0.3" , "metadata" :{ "timeout" : 3000 }},], "getVersion" : [ { "application" : "dubbo-application" , "name" : "instance1" , "ip" : "127.0.0.1" "メタデータ" :{ "タイムアウト" 1000 }}、{ "アプリケーション" "dubbo-application" "名前" "インスタンス2" "ip" "127.0.0.2" "メタデータ" :{ "タイムアウト" 2000 }}、{ "アプリケーション" "dubbo-application" "名前" "インスタンス3" "ip" "127.0.0.3" "メタデータ" :{ "タイムアウト" 3000 }}]

アプリケーションレベルのサービス検出メカニズムのレジストリ内のデータ

  1. 「dubbo-application」 : [ { "name" : "instance1" "ip" : "127.0.0.1" "metadata" :{ "timeout" : 1000 }}、 { "name" : "instance2" "ip" : "127.0.0.2" "metadata" :{ "timeout" : 2000 }}、 { "name" : "instance3" "ip" : "127.0.0.3" "metadata" :{ "timeout" : 3000 }}]

比較すると、アプリケーション レベルのサービス検出メカニズムを使用すると、登録センターのデータ量が大幅に削減され、元のインターフェイス レベルのデータはメタデータ センターに保存されることがわかります。

サーバーはプロセス全体を公開します

アプリケーションレベルのサービス検出メカニズムの導入後、Dubbo 3.0 サーバー公開のプロセス全体が以前とは大きく異なります。サーバー プロセス全体を公開するコア コードは、次のように DubboBootstrap#doStart にあります。

  1. プライベート  void doStart() { // 1. Dubboサービスを公開する exportServices(); // コンシューマーインスタンスを登録するか、サービスをエクスポートした場合 if (isRegisterConsumerInstance() || hasExportedServices()) { // 2. メタデータ サービスを公開する exportMetadataService(); // 3. メタデータを定期的に更新してレポートする registerServiceInstance(); .... } ......}  

Zookeeper が Triple プロトコルの登録と公開のためのサービスとして使用されている場合、サーバー公開のプロセス全体のシーケンス図は次のようになります。

露出プロセス全体は非常に複雑で、次の 4 つの部分に分けられることがわかります。

injvm プロトコルのサービスを公開し、service-discovery-registry プロトコルを登録します。 Triple プロトコルのサービスを公開し、レジストリ プロトコルに登録します。 MetadataService サービスを公開します。以下では、これら 4 つの部分からサービス公開のプロセス全体を詳しく説明します。

1. injvmプロトコルサービスを公開する

injvm プロトコルのサービスはローカルに公開されます。主な理由は、アプリケーションにはサービス (公開されたサービス) と参照 (サービス参照) の両方が存在することが多く、参照によって参照されるサービスはアプリケーションで公開されるサービスであるためです。この使用シナリオをサポートするために、Dubbo はサービスをローカルで公開する injvm プロトコルを提供します。これにより、参照はネットワークを経由せずにローカルで直接サービスを呼び出すことができます。

全体的なタイミング図

この部分は以前のインターフェース レベルのサービス検出メカニズムと似ているため、関連するコア コードについてはここでは説明しません。

2. サービス検出レジストリプロトコルを登録する

サービス検出レジストリ プロトコルを登録する主な目的は、サービスに関連するメタデータを登録することです。デフォルトでは、メタデータは InMemoryWritableMetadataService を通じてローカル メモリとローカル ファイルに保存されます。

全体的なタイミング図

コア コードは、次のように ServiceConfig#exportRemote にあります。

サービス検出レジストリプロトコルのエントリを登録する

  1. private URL exportRemote(URL url, List<URL> registryURLs) { if (CollectionUtils.isNotEmpty(registryURLs)) { // レジストリが複数ある場合は、ループして各レジストリを登録します for (URL registryURL : registryURLs) { // サービス検出レジストリ プロトコルかどうかを判断します // サービス名マッピング パラメータの値を true に設定します if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) { url = url.addParameterIfAbsent(SERVICE_NAME_MAPPING_KEY, "true"); } ...... // サービス検出レジストリ プロトコル再利用サービス公開プロセスを登録します。doExportUrl(registryURL.putAttribute(EXPORT_KEY, url), true); } ...... URLを返します;}  

Invoker でのメタデータのパッケージ化

コア コードは、ServiceConfig#doExportUrl にあります。次のようになります。

  1. プライベート  void doExportUrl(URL url, boolean withMetaData) { Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url); // この時点での withMetaData の値は true です // 呼び出し元を DelegateProviderMetaDataInvoker にラップします if (withMetaData) {invoker = new DelegateProviderMetaDataInvoker(invoker, this); } エクスポーター<?> exporter = PROTOCOL.export(invoker); exporters.add(エクスポーター);}  

RegistryProtocol 経由で Invoker を Exporter に変換する

コア コードは、次のように ProtocolListenerWrapper#export にあります。

  1. public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { // この時点でのプロトコルは RegistryProtocol 型です if (UrlUtils.isRegistry(invoker.getUrl())) { return protocol.export(invoker); } ......}  

RegistryProtocol は Invoker を Exporter コアプロセスに変換します

コア コードは、次のように RegistryProtocol#export にあります。

  1. public <T> Exporter<T> export( final Invoker<T> originInvoker) throws RpcException { URL registryUrl = getRegistryUrl(originInvoker); URL プロバイダーUrl = getProviderUrl(originInvoker); ...... // Triple プロトコルのサービスを再度公開します final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl); // registryUrl には、サービス検出レジストリ プロトコルが含まれています // このプロトコルを使用して ServiceDiscoveryRegistry オブジェクトを作成します // 次に、RegistryServiceListener リスナーを結合します // 最後に、それを ListenerRegistryWrapper オブジェクトにラップします final Registry registry = getRegistry(registryUrl);最終 URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);ブール値 register = providerUrl.getParameter(REGISTER_KEY, true); if (register) { // service-discovery-registry プロトコルを登録します // RegistryServiceListener の onRegister イベントをトリガーします register(registry, registeredProviderUrl); } ...... // RegistryServiceListener の onRegister イベントをトリガーします。notifyExport(exporter);新しい DestroyableExporter<>(exporter) を返します。}  

トリプルプロトコルを公開するサービス

コア コードは、次のように RegistryProtocol#doLocalExport にあります。

  1. private <T> ExporterChangeableWrapper<T> doLocalExport( final Invoker<T> originInvoker, URL providerUrl) { String key = getCacheKey(originInvoker); // この時点でのプロトコルは、Triple プロトコルのプロキシ クラスです // injvm プロトコルを公開する PROTOCOL と同じです return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> { Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl); return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker); });}  

サービス検出レジストリプロトコルを登録する

コア コードは、次のように ServiceDiscoveryRegistry#register と ServiceDiscoveryRegistry#doRegister にあります。

1.サービスディスカバリレジストリ#登録

  1. 公共 ファイナル  void register(URL url) { // サーバー (プロバイダー) のみを登録する必要があります if (!shouldRegister(url)) { return; } // サービス検出レジストリ プロトコルを登録します。doRegister(url);}  

2. サービスディスカバリレジストリ#doRegister

  1. 公共  void doRegister(URL url) { url = addRegistryClusterKey(url); // メタデータを登録する if (writableMetadataService.exportURL(url)) { if (logger.isInfoEnabled()) { logger.info(format("URL[%s] が正常に登録されました。", url.toString())); } } else { if (logger.isWarnEnabled()) { logger.warn(format("URL[%s]が登録されました。", url.toString())); } }}  

登録メタデータ

コア コードは、次のように InMemoryWritableMetadataService#exportURL にあります。

  1. 公共  boolean exportURL(URL url) { // MetadataService の場合、メタデータは登録されません if (MetadataService.class.getName().equals(url.getServiceInterface())) { this.metadataServiceURL = url; true を返します。 } updateLock.readLock().lock();試してください { String[] clusters = getRegistryCluster(url).split(","); (String cluster : clusters) の場合 { MetadataInfo metadataInfo = metadataInfos.computeIfAbsent(cluster, k -> new MetadataInfo(ApplicationModel.getName())); // トリプル プロトコル サービスのインターフェイス関連データから ServiceInfo を生成します // ServiceInfo を MetadataInfo に登録します metadataInfo.addService(new ServiceInfo(url)); } メタデータセマフォをリリースします。 addURL(エクスポートされたServiceURLs、url)を返します。 } 最後に { updateLock.readLock().unlock(); }}  

onRegisterイベントを公開

コア コードは、次のように ListenerRegistryWrapper#register にあります。

  1. 公共  void register(URL url) { try { // registry は ServiceDiscoveryRegistry オブジェクトです // この時点で ServiceDiscoveryRegistry#registry メソッドが呼び出されています registry.register(url); } 最後に { if (CollectionUtils.isNotEmpty(listeners) && !UrlUtils.isConsumer(url)) { RuntimeException 例外 = null; for (RegistryServiceListener listener : listeners) { if (listener != null) { try { // service-discovery-registry プロトコルを登録した後に onRegister イベントを発行します listener.onRegister(url, registry); } キャッチ (RuntimeException t) { logger.error(t.getMessage(), t);例外 = t; } } } if (exception != null) { 例外をスローします; } } }}  

サービス登録イベントを公開する

コア コードは、次のように RegistryProtocol#notifyExport にあります。

  1. private <T> void notificationExport(ExporterChangeableWrapper<T> exporter) { List<RegistryProtocolListener> listeners = ExtensionLoader.getExtensionLoader(RegistryProtocolListener. class ) .getActivateExtension(exporter.getOriginInvoker().getUrl(), "registry.protocol.listener" ); if (CollectionUtils.isNotEmpty(listeners)) { for (RegistryProtocolListener listener : listeners) { // RegistryProtocolListener の onExport イベントを公開します listener.onExport(this, exporter); } }}  

サービス検出レジストリ プロトコルを登録する主な目的は、サービス インターフェイスに関連する情報をメモリに保存することであることがわかります。互換性とスムーズな移行を考慮して、コミュニティは実装時に ServiceConfig の公開プロセスを再利用するというアプローチを採用しました。

3. トリプルプロトコルサービスを公開し、レジストリプロトコルを登録する

トリプル プロトコル サービスの公開とレジストリ プロトコルの登録は、Dubbo 3.0 サービス公開のコア プロセスであり、次の 2 つの部分に分かれています。

トリプルプロトコルを公開するサービス

レジストリ プロトコルの登録 Triple プロトコル サービスを公開するプロセスは、Injvm プロトコル サービスを公開するプロセスと同じなので、詳細には説明しません。レジストリ プロトコルを登録するプロセスでは、アプリケーション インスタンスに関連する情報のみが登録されます。これは、前述のアプリケーション レベルのサービス検出メカニズムです。

全体的なタイミング図

InterfacecompatibleRegistryProtocol 経由で Invoker を Exporter に変換する

コア コードは、次のように ProtocolListenerWrapper#export にあります。

  1. public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { // この時点でのプロトコルは InterfacecompatibleRegistryProtocol タイプです (RegistryProtocol を継承します) // 注: service-discovery-registry プロトコルを登録する場合、プロトコルは RegistryProtocol タイプです if (UrlUtils.isRegistry(invoker.getUrl())) { return protocol.export(invoker); } ......}  

RegistryProtocol は Invoker を Exporter コアプロセスに変換します

コア コードは、次のように RegistryProtocol#export にあります。

  1. public <T> Exporter<T> export( final Invoker<T> originInvoker) throws RpcException { URL registryUrl = getRegistryUrl(originInvoker); URL プロバイダーUrl = getProviderUrl(originInvoker); ...... // Triple プロトコルのサービスを再度公開します final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl); // registryUrl にはレジストリ プロトコルが含まれます // このプロトコルを使用して ZookeeperRegistry オブジェクトを作成します // 次に RegistryServiceListener リスナーを結合します // 最後にそれを ListenerRegistryWrapper オブジェクトにラップします // 注: // 1. service-discovery-registry プロトコルは ServiceDiscoveryRegistry に対応します // 2. registry プロトコルは ZookeeperRegistry に対応します final Registry registry = getRegistry(registryUrl);最終 URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);ブール値 register = providerUrl.getParameter(REGISTER_KEY, true); if (register) { // レジストリ プロトコルを登録する // RegistryServiceListener の onRegister イベントをトリガーする register(registry, registeredProviderUrl); } ...... // RegistryProtocolListener の onExport イベントを公開します。notifyExport(exporter);新しい DestroyableExporter<>(exporter) を返します。}  

レジストリプロトコルを登録する

コア コードは、次のように FailbackRegistry#register と ServiceDiscoveryRegistry#doRegister (ZookeeperRegistry は FailbackRegistry を継承) にあります。

1. フェイルバックレジストリ#register

  1. 公共  void register(URL url) { if (!acceptable(url)) { ...... try { // レジストリプロトコルを登録する doRegister(url); } キャッチ (例外 e) { ...... } }}  

2. サービスディスカバリレジストリ#doRegister

  1. 公共  void doRegister(URL url) { try { // プロバイダーを Zookeeper に登録 // ディレクトリ: /dubbo/xxxService/providers/*** // データ: dubbo://192.168.31.167:20800/xxxService?anyhost=true& // application=application-name&async=false&deprecated=false&dubbo=2.0.2& // dynamic=true&file.cache=false&generic=false&interface=xxxService& // metadata-type=remote&methods=hello&pid=82470&release=& // service-name-mapping=true&side=provider×tamp=1629588251493 zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true)); } catch (Throwable e) { throw new RpcException("" + url + " を Zookeeper " + getUrl() + " に登録できませんでした。原因: " + e.getMessage(), e); }}  

購読アドレスの変更

コア コードは、次のように FailbackRegistry#subscribe と ZookeeperRegistry#doSubscribe にあります。

1. フェイルバックレジストリ#subscribe

  1. 公共  void subscribe(URL url, NotificationListener listener) { ...... try { // ZookeeperRegistry#doSubscribe を呼び出します。doSubscribe(url, listener); } 例外 e をキャッチします { ......}  

2. Zookeeperレジストリ#doSubscribe

  1. 公共  void doSubscribe(最終URL url、最終NotificationListener リスナー) { try { if (ANY_VALUE.equals(url.getServiceInterface())) { ...... } else { ...... for (String path : toCategoriesPath(url)) { ConcurrentMap<NotifyListener、ChildListener> listeners = zkListeners.computeIfAbsent(url、k -> new ConcurrentHashMap<>());子リスナー zkListener = listeners.computeIfAbsent(リスナー、k ->新しいRegistryChildListenerImpl(url、パス、k、ラッチ)); zkListener のinstanceof RegistryChildListenerImpl の場合、((RegistryChildListenerImpl) zkListener).setLatch(latch); } // コンフィギュレータ データを保存するための一時ノードを作成します // ディレクトリ:/dubbo/xxxService/configurators // データ: dubbo-admin で変更できるアプリケーション構成情報。デフォルト値は空です。 zkClient.create(パス、false); // コンフィギュレータの変更を監視するリスナーを追加します。List<String> children = zkClient.addChildListener(path, zkListener);子要素が null の場合、 urls.addAll(toUrlsWithEmpty(url, path, children)); } } ...... } } キャッチ (Throwable e) { ...... }  

公開されたトリプル プロトコル サービスとメタデータ間の接続を確立するためのコア コードは、次のように、ServiceConfig#exportUrl、MetadataUtils#publishServiceDefinition、InMemoryWritableMetadataService#publishServiceDefinition、RemoteMetadataServiceImpl#publishServiceDefinition、および MetadataReport#storeProviderMetadata にあります。

1. サービス構成#エクスポートUrl

  1. プライベート  void exportUrl(URL url, List<URL> registryURLs) { ...... if (!SCOPE_NONE.equalsIgnoreCase(scope)) { ...... if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) { url = exportRemote(url, registryURLs); // サービス インターフェイスに関連するイベントを公開し、データを更新します。MetadataUtils.publishServiceDefinition(url); } } ......}  

2. メタデータユーティリティ#publishServiceDefinition

  1. 公共 静的  void publishServiceDefinition(URL url) { // サービス インターフェイス関連のデータを InMemoryWritableMetadataService に保存します。WritableMetadataService.getDefaultExtension().publishServiceDefinition(url); // サービス インターフェイス関連のデータをリモート メタデータ センターに保存します if (REMOTE_METADATA_STORAGE_TYPE.equalsIgnoreCase(url.getParameter(METADATA_KEY))) { getRemoteMetadataService().publishServiceDefinition(url); }}  

3. InMemoryWritableMetadataService#publishServiceDefinition

  1. 公共  void publishServiceDefinition(URL url) { try { String interfaceName = url.getServiceInterface(); if (StringUtils.isNotEmpty(interfaceName) && !ProtocolUtils.isGeneric(url.getParameter(GENERIC_KEY))) { Class interfaceClass = Class.forName(interfaceName);サービス定義 serviceDefinition = ServiceDefinitionBuilder.build(interfaceClass); Gson を新しいGson() に変換します。文字列データ = gson.toJson(serviceDefinition); // サービス インターフェース関連のデータを保存します // データ形式: // { // "canonicalName": "xxxService", // "codeSource": "file:/Users/xxxx", // "methods": [{ // "name": "hello", // "parameterTypes": ["java.lang.String"], // "returnType": "java.lang.String", // "annotations": [] // }], // "types": [{ // "type": "java.lang.String" // }], // "annotations": [] // } serviceDefinitions.put(url.getServiceKey(), data);戻る; } そうでない場合、(CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY))) { ...... } ...... } catch (Throwable e) { ...... }}  

4. リモートメタデータサービス実装#publishServiceDefinition

  1. 公共  void publishServiceDefinition(URL url) { checkRemoteConfigured();文字列サイド = url.getSide(); if (PROVIDER_SIDE.equalsIgnoreCase(side)) { // サーバー (プロバイダー) のサービス インターフェース情報をメタデータ センターに公開します publishProvider(url); } else { ...... }}RemoteMetadataServiceImpl#publishProviderprivate void publishProvider(URL providerUrl) throws RpcException { ...... try { String interfaceName = providerUrl.getServiceInterface(); if (StringUtils.isNotEmpty(interfaceName)) { ...... for (Map.Entry<String, MetadataReport> entry : getMetadataReports().entrySet()) { // メタデータ センターへのアクセスに使用される MetadataReport サービスを取得します。MetadataReport metadataReport = entry.getValue(); // サービス インターフェイス情報をメタデータ センターに保存します。metadataReport.storeProviderMetadata(new MetadataIdentifier(providerUrl.getServiceInterface(), providerUrl.getVersion(), providerUrl.getGroup(), PROVIDER_SIDE, providerUrl.getApplication()), fullServiceDefinition); } 戻る; } ...... } (ClassNotFoundException e) をキャッチ { ...... }}  

5. 抽象メタデータレポート#storeProviderMetadata

  1. 公共  void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition){ if (syncReport) { storeProviderMetadataTask(providerMetadataIdentifier, serviceDefinition); } else { // メタデータ センターに非同期に保存します reportCacheExecutor.execute(() -> storeProviderMetadataTask(providerMetadataIdentifier, serviceDefinition)); }}private void storeProviderMetadataTask(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { try { ...... allMetadataReports.put(providerMetadataIdentifier, serviceDefinition);プロバイダーメタデータ識別子の削除に失敗しました。 Gson を新しい Gson() に変換します。 // データ形式: // { // "parameters": { // "side": "provider", // "interface": "xxxService", // "metadata-type": "remote", // "service-name-mapping": "true", // }, // "canonicalName": "xxxService", // "codeSource": "file:/Users/xxxx", // "methods": [{ // "name": "hello", // "parameterTypes": ["java.lang.String"], // "returnType": "java.lang.String", // "annotations": [] // }], // "types": [{ // "type": "java.lang.String" // }], // "annotations": [] // } String data = gson.toJson(serviceDefinition); // メタデータ センターに保存します。インスタンス内のメタデータ センターは ZookeeperMetadataReport です // ディレクトリ: メタデータ センターの Metadata-report 内の /dubbo/metadata/xxxService/provider/${application-name} ノード doStoreProviderMetadata(providerMetadataIdentifier, data); // ローカル ファイルに保存します // パス: xxxService:::provider:${application-name} saveProperties(providerMetadataIdentifier, data, true, !syncReport); } キャッチ (例外 e) { ...... }}  

Triple プロトコル サービスと MetadataReport サービスの関係を確立するためのコア コードは、次のように、ServiceConfig#exported、MetadataServiceNameMapping#map、および ZookeeperMetadataReport#registerServiceAppMapping にあります。

1. ServiceConfig#エクスポート

  1. 保護された  void exported() { exported = true ;リスト<URL> exportedURLs = this .getExportedUrls(); exportedURLs.forEach(url -> { // URL が service-name-mapping フィールドでマークされているかどうかを判断します // このフィールドでマークされたサービスは、公開されたサービスをメタデータ センターに関連付ける必要があります // コンシューマーは、メタデータ センターのメッセージの変更を通じて、プロバイダー側​​のメタデータの変更を認識できます if (url.getParameters().containsKey(SERVICE_NAME_MAPPING_KEY)) { ServiceNameMapping serviceNameMapping = ServiceNameMapping.getDefaultExtension(); // リレーションシップを確立します serviceNameMapping.map(url); } });エクスポートされた();}  

2. メタデータサービス名マッピング#map

  1. 公共  void map(URL url) { execute(() -> { String registryCluster = getRegistryCluster(url); // メタデータ センターへのアクセス パスである MetadataReport を取得します MetadataReport metadataReport = MetadataReportInstance.getMetadataReport(registryCluster); ...... int currentRetryTimes = 1; boolean success; String newConfigContent = getName(); do { // メタデータ センターに保存されているアプリケーションのバージョン情報を取得します ConfigItem configItem = metadataReport.getConfigItem(serviceInterface, DEFAULT_MAPPING_GROUP); String oldConfigContent = configItem.getContent(); if (StringUtils.isNotEmpty(oldConfigContent)) { boolean contains = StringUtils.isContains(oldConfigContent, getName()); if (contains) { break; } newConfigContent = oldConfigContent + COMMA_SEPARATOR + getName(); } // マッピング ノードを作成しますメタデータ センターで、公開されたサービス データをメタデータ センターに保存します。ここでのメタデータ センターは、zookeeper によって実装されています // ディレクトリ: /dubbo/mapping/xxxService // データ: configItem.content は ${application-name}、configItem.ticket はバージョン番号です success = metadataReport.registerServiceAppMapping(serviceInterface, DEFAULT_MAPPING_GROUP, newConfigContent, configItem.getTicket()); } while (!success && currentRetryTimes++ <= CAS_RETRY_TIMES); });}  

3. Zookeeperメタデータレポート#registerServiceAppMapping

  1. 公共 ブール型registerServiceAppMapping(文字列キー、文字列グループ、文字列コンテンツ、オブジェクトチケット) { try { if (ticket != null && !(ticket instanceof Stat)) { throw  新しいIllegalArgumentException( "zookeeper publishConfigCas には統計タイプのチケットが必要です" ); } 文字列 pathKey = buildPathKey(グループ、キー); // 1. /dubbo/mapping/xxxService ディレクトリを作成し、データを configItem として保存します。 // 2. バージョン番号を生成します。 zkClient.createOrUpdate(pathKey, content, false, ticket == null ? 0 : ((Stat) ticket).getVersion()); true を返します。 } catch (例外 e) { logger.warn("zookeeper publishConfigCas が失敗しました。", e); false を返します。 }}  

この時点で、トリプル プロトコル サービスを公開し、レジストリ プロトコルを登録するプロセスは完了です。主なことは、以前のインターフェース レベルのサービス検出メカニズムで登録センターに登録されたデータ (アプリケーション インスタンス データ + サービス インターフェース データ) を分割することです。登録レジストリプロトコル部分は、アプリケーションインスタンスデータを登録センターに登録します。エクスポーターが公開された後、MetadataUtils#publishServiceDefinition を呼び出すことによって、サービス インターフェイス データがメタデータ センターに登録されます。

4. MetadataService を公開する

MetadataService は主に、コンシューマー側にメタデータを取得するための API を提供します。公開プロセスは、トリプル プロトコルのサービス公開プロセスを再利用します。

全体的なタイミング図

MetadataServiceエントリを公開する

コアコードは DubboBootstrap#exportMetadataService にあります。次のようになります。

  1. プライベート  void exportMetadataService() { // MetadataServer を公開します。metadataServiceExporter.export();}  

MetadataService の公開

コア コードは、次のように ConfigurableMetadataServiceExporter#export にあります。

  1. } public ConfigurableMetadataServiceExporter export() { if (!isExported()) { // MetadataService の ServiceConfig を変更します ServiceConfig<MetadataService> serviceConfig = new ServiceConfig<>(); serviceConfig.setApplication(getApplicationConfig()); // MetadataService の ServiceConfig を設定します。serviceConfig.setRegistry(new RegistryConfig("N/A")); serviceConfig.setProtocol(メタデータプロトコルを生成()); serviceConfig.setInterface(MetadataService.class); serviceConfig.setDelay(0); serviceConfig.setRef(メタデータサービス); serviceConfig.setGroup(getApplicationConfig().getName()); serviceConfig.setVersion(metadataService.version()); serviceConfig.setMethods(メソッドConfigを生成) // 露出米データデータ服务过程露出多件协议服务服务// 使用Dubbo协议服务 

MetadataService を公開するプロセスは、前述の Triple protocol サービスを公開するプロセスを再利用するため、プロセス全体にいくつかの違いがあります。これらの違いは上記のコードでマークされているため、ここでは繰り返さないことにします。

ServiceInstance の登録

ServiceInstance を登録する目的は、メタデータを定期的に更新することです。アップデートがあった場合、コンシューマー側が認識できるように、MetadataReport を通じてバージョン番号が更新されます。

コア コードは、次のように DubboBootstrap#registerServiceInstance と DubboBootstrap#doRegisterServiceInstance にあります。

  1. プライベート  void registerServiceInstance() { .... // ServiceInstance を構築します // ServiceInstance には、次のフィールドが含まれます // 1. serviceName:${application-name} // 2. host: 192.168.31.167 // 3. port: 2080 // 4. metadata: メソッドなどのサービス インターフェース レベルに関するデータ // 同時に、ServiceInstance データ内のフィールドが補完され、次の 4 つの ServiceInstanceCustomizer インスタンスがそれぞれ呼び出されます // 1)ServiceInstanceMetadataCustomizer // 2)MetadataServiceURLParamsMetadataCustomizer // 3)ProtocolPortsMetadataCustomizer // 4)ServiceInstanceHostPortCustomizer ServiceInstance serviceInstance = createServiceInstance(serviceName);ブール値登録済み = true; try { // ServiceInstance を登録します doRegisterServiceInstance(serviceInstance); } catch (例外 e) { registered = false; logger.error("インスタンス登録エラー", e); } // 登録が成功した場合は、10 秒ごとにメタデータを定期的に更新します。 if(registered){ executorRepository.nextScheduledExecutor().scheduleAtFixedRate(() -> { ... try { // メタデータと ServiceInstance を更新します。 ServiceInstanceMetadataUtils.refreshMetadataAndInstance(serviceInstance); } catch (Exception e) { ... } finally { ... } }, 0, ConfigurationUtils.get(METADATA_PUBLISH_DELAY_KEY, DEFAULT_METADATA_PUBLISH_DELAY), TimeUnit.MILLISECONDS); }}  

DubboBootstrap#doRegisterServiceInstance

  1. プライベート  : : : : : : : : : : : : : : : serviceDiscovery.register(serviceInstanceForRegistry); }); }}  

上記の分析により、簡単に知ることができる

ServiceInstanceはメタデータに含まれています
メタデータは、ローカル メモリ領域を占有する InMemoryWritableMetadataService に保存されるメタデータです。
InMemoryWritableMetadataServiceはメタデータの更新に使用されます
ServiceInstanceはリモートメタデータレジストリに保存されるデータ構造です
RemoteMetadataServiceImpl は、メタデータレポートを呼び出して、ServiceInstance データをリモート メタデータ登録センターに更新します。

要約する

Dubbo 3.0サーバーの公開プロセス全体を分析すると、アプリケーションレベルのサービス検出メカニズムの実装ははるかに複雑ですが、ユーザーがスムーズに移行できるように、Dubbo 3.0は2.7.xバージョンと互換性があり、設計では多くの箇所が可能な限り再利用されていることがわかります。

Dubbo 3.0 が最近リリースしたベンチマーク データから判断すると、Dubbo 3.0 のパフォーマンスとリソース使用率は確かに大幅に向上しています。 Dubbo 3.0 がクラウド ネイティブを採用するまでには、まだ長い道のりが残っています。コミュニティは、Dubbo 3.0 のコア プロセスを整理し、最適化しています。後続のプランでは、複数インスタンスのアプリケーションの展開がサポートされます。 Dubbo のクラウド ネイティブ パスを目の当たりにすることに興味のある学生が、コミュニティへの貢献に積極的に参加できることを願っています。

<<:  Zookeeper における Kafka のデータ構造を完全に説明する図

>>:  クラウドウェブホスティングのメリット

推薦する

クラウドコンピューティング技術の出現は、IT運用・保守システムに大きな変化をもたらす可能性がある

クラウド コンピューティングは、新しいコンピューティング方法とビジネス モデルです。仮想化、分散処理...

多面的なアプローチ - ウェブマスターはさまざまな側面からウェブサイトのユーザーエクスペリエンスを向上させる必要があります

コンテンツの追加段階が終わると、ウェブマスターはウェブサイトの最適化に取り掛かります。しかし、ウェブ...

「消費者還元」制度の相次ぐ崩壊:ねずみ講の瀬戸際

最近、「消費者還元」をめぐって多くの問題が起きています。上海では、佳地豪電子商務有限公司(以下、佳地...

host1plus-VPS 特別オファー 25% オフ/年額 15.3 米ドル/768M メモリ/60g ハードディスク

host1plus.com が突然、全員に特別割引コードを提供しました。host1plus の南アフ...

Xiaomi はどのようにして広告をうまく行うべきでしょうか?

月収10万元の起業の夢を実現するミニプログラム起業支援プランXiaomi の第 2 四半期の財務報告...

インスタント メッセージング ソフトウェアの「10 年間の進化」

【ゼロからのスタート】 2003 年には、QQ や NetEase Bubble などのインスタント...

企業はWeiboをどのように活用して精密マーケティングを実現できるか:需要に基づいてコンテンツを決定する

ソーシャル化された電子商取引は、Facebook 上で非常に有望なビジネス モデルであることが徐々に...

Kubernetes Podの排除に関する詳細な説明

Kubernetes Pod が削除されるとはどういう意味ですか?これらは通常、リソース不足のために...

テンセント、美団、バイトダンスが外食産業をターゲットにしているのか?

さまざまなインターネットビジネスをめぐる大企業間の「絞め殺し戦争」に慣れてしまった外の世界は、それほ...

2010 南アフリカワールドカップのスケジュールと 2010 南アフリカワールドカップのテレビライブスケジュール

2010 年ワールドカップがもうすぐ始まります。ファンの皆様が試合を観戦しやすいように、2010 年...

最近のWeChatの調整から、WeChatにどれだけのマーケティングの血が流れているかについて簡単に議論する

少し前に、自分のウェブサイトの公式WeChat公開プラットフォームにログインし、情報ページを確認する...

Web デザインの評価: 準備はできていますか?ウェブページの第一画面に大きな画像が表示される時代です!

@度Celsius 一枚の写真は千の言葉に値します。写真を読むインターネット時代では、写真はもはやデ...

地方病院ウェブサイトの運用方法に関するいくつかの論点

地方病院のウェブサイトの運営モードは、地理、文化などの要因の影響を受け、ネットワークの面でも比較的単...

Docker Swarm: コンテナオーケストレーションを大幅に簡素化

翻訳者 |チェン・ジュンレビュー |チョンロウコンテナ化とクラウド コンピューティングの文脈では、分...