Eurekaは分散型サービスガバナンスアプリケーションであり、その特徴的な機能は、サーバーとサービスの両方として構成したアドレスに登録できることです。次に、この記事では、ユーレカの登録プロセスについて説明しましょう。
1。ユーレカのサーバー
Eurekaのサーバー側のコアクラスはEurekabootStrapであり、ServletContextListenerのリスナーを実装しています。したがって、ユーレカはサーブレット容器に基づいて実装されていると結論付けることができます。重要なコードは次のとおりです。
パブリッククラスのeurekabootStrapは、servletcontextlistener {//...oMit関連コード/***他のeurekaピアと同期したり、レジストリを公開したりするなど、eurekaを初期化します。 * * @see * javax.servlet.servletcontextlistener#contextInitialized(javax.servlet.servletcontextevent) */ @Override public void contextInitialized(servletContextExteventイベント){try {initeurekaenvironment(); initeurekaservercontext(); servletcontext sc = event.getServletContext(); sc.setattribute(eurekaservercontext.class.getName()、serverContext); } catch(throwable e){logger.error( "bootstrap eureka server:"、e);新しいruntimeexception( "bootstrap eureka server:"、e)を投げます。 }} //関連コードを省略してください......}ServletContextの初期化が完了すると、Eureka環境が初期化され、EurekaServerContextが初期化されることがわかります。次に、initeurekaservercontextメソッドをご覧ください。
/***サーバーコンテキスト用のinitフック。カスタムロジックのオーバーライド。 */ Protected void initeurekaservercontext()スロー例外{// ...... applicationInfomanager applicationInfomanager = null; if(eurekaclient == null){eurekainstanceconfig instanceconfig = iscloud(configurationmanager.getDeploymentContext())? new CloudInstanceConfig():new MyDataCenterinStanceConfig(); ApplicationInfomanager = new ApplicationInfomanager(instanceConfig、new eurekaconfigbasedInstanceInfoprovider(instanceconfig).get()); eurekaclientconfig eurekaclientconfig = new defaulteurekaclientconfig(); eurekaclient = new DiscoveryClient(applicationInfomanager、eurekaclientConfig); } else {applicationInfomanager = eurekaclient.getApplicationInfomanager(); } PeerawareinStanceregistryレジストリ。 if(isaws(applicationinfomanager.getInfo())){registry = new awsinstanceregistry(eurekaserverconfig、eurekaclient.geteurekaclientconfig()、servercodecs、eurekaclient); awsbinder = new awsbinderdelegate(eurekaserverconfig、eurekaclient.geteurekaclientconfig()、registry、application infomanager); awsbinder.start(); } else {registry = new Peerawareinstanceregistryimpl(eurekaserverconfig、eurekaclient.geteurekaclientConfig()、servercodecs、eurekaclient); } //...コードの一部の部分}この方法では、Eurekaサービスに関連する多くのオブジェクトが作成されます。ここでは、2つのコアオブジェクト、すなわちeurekaclientとpeerawareinstanceregistryをリストします。後でクライアントの部分について説明します。 PeerawareinStanceregistryが使用しているものを見てみましょう。ここでは、このクラスについてクラス図を書きます。
クラス図によれば、Peerawareinstanceregistryのトップレベルのインターフェイスはリースマンガーおよびルックアップサービスであることが明確にわかります。ここでは、LookupServiceが最も基本的な発見例の動作を定義し、リースマンアンジャーはクライアント登録、更新、およびキャンセル操作の処理を定義します。したがって、この記事では、LeaseManagerの関連するインターフェイスの実装に焦点を当てましょう。振り返ってみると、Peerawareinstanceregistryを見ています。実際、このクラスは、複数のノードの下で関連情報をコピーするために使用されます。たとえば、ノードが更新およびオフラインのために登録する場合、関連するコピー(通知)がこのクラスを介して各ノードにコピーされます。クライアントの登録をどのように処理するかを見てみましょう。
/** * {@link instanceInfo}とレプリカ *の情報をすべてのピアエレカノードに登録します。これが他のレプリカノードからのレプリケーションイベント *の場合、複製されません。 * * @param info * {@link instanceinfo}登録および複製される。 * @param isReplication *これが他のレプリカノードからのレプリケーションイベントである場合、 * falseそれ以外の場合。 */ @Override public void Register(final instanceinfo info、final boolean isReplication){int leaseduration = lease.default_duration_in_secs; if(info.getLeaseInfo()!= null && info.getLeaseInfo()。getDurationInsecs()> 0){leadeuration = info.getLeaseInfo()。getSurationInsecs(); } super.register(info、leaduration、isreplication); ReplicateTopeers(action.register、info.getAppname()、info.getId()、info、null、isReplication); }親クラスのレジスタメソッドを呼び出してから、対応する動作をReplicateTopersを介して他のノードに複製することがわかります。ここでは、特定の複製については説明しません。登録方法に焦点を当てましょう。親クラスにレジスタ()メソッドがあります。
/***特定の期間の新しいインスタンスを登録します。 * * @see com.netflix.eureka.lease.leasemanager#レジスタ(java.lang.object、int、boolean) */ public voidレジスタ(instanceinfo登録、int leased、boolean isreplication){try {read.lock(); map <string、lease <instanceinfo >> gmap = registry.get(restionant.getAppname()); Register.Increment(isReplication); if(gmap == null){final concurrenthashmap <string、lease <instanceinfo >> gnewmap = new concurrenthashmap <string、lease <instanceinfo >>(); gmap = registry.putifabsent(restorlant.getAppname()、gnewmap); if(gmap == null){gmap = gnewmap; }} lease <intanceinfo>既存のlease = gmap.get(restorant.getid()); //上書きせずに最後のダーティタイムスタンプを保持します。既にリースがある場合、(既存のlease!= null &&(expstingLease.getholder()!= null)){long expstinglastdirtytimestamp = expstinceLease.getholder()。getLastDirtyTimestamp(); long registrationlastdirtytimestamp = Registered.getLastDirtyTimestamp(); logger.debug( "既存のリースが見つかった(既存= {}、redumentdirtytimestamp、registrationlastdirtytimestamp); //これはa> =の代わりにa>です。登録lastdirtytimestamp){logger.warn( "既存のリースがあり、既存のリースのダーティタイムスタンプは登録されている{}よりも「 +」、existlastdirtytimestamp、登録lastdirtytimestam登録= expectLease.getholder();これはリース<登録者、既存ソースコードを使用して、プロセスを簡単に整理しましょう。
1)最初に、AppNameに基づいてサービスインスタンスオブジェクトの列を取得します。 nullの場合は、新しいマップを作成し、現在の登録アプリケーション情報をこのマップに追加します。ここにリースオブジェクトがあります。このクラスでは、登録時間、サービススタートアップ時間、最終更新時間など、ジェネリックTの時間属性について説明します。その実装に注意することができます。
/ * * Copyright 2012 Netflix、Inc。 * * Apacheライセンス、バージョン2.0(「ライセンス」)に基づいてライセンスされています。 *ライセンスに準拠している場合を除き、このファイルを使用することはできません。 *ライセンスのコピーを取得することができます * * http://www.apache.org/licenses/license-2.0 * *該当する法律で要求されていない場合、または書面で合意しない限り、ライセンスに基づいて配布されるソフトウェア *は、保証または条件なしで「現状」に分配されます。 *特定の言語ガバナンス許可のライセンスとライセンスに基づく制限を参照してください。 */パッケージcom.netflix.eureka.lease;インポートcom.netflix.eureka.registry.abstractinstanceregistry;/*** {@link t}の時間ベースの可用性について説明します。目的は、AWS環境では珍しいことではない非graceful的な *シャットダウンの結果として、{@link abstractinstanceregistry}のインスタンスの蓄積を避けることです。 * *リースが更新せずに経過すると、最終的には、即時の立ち退きのために関連する{@link t}を継続的に有効にします - これは * {@link t}と{@link leasemanager}の間に通信がないことを除いて、明示的なキャンセルに似ています。 * * @Author Karthik Ranganathan、Greg Kim */Public Class Lease <T> {enum Action {Register、Cancel、Renew}; public static final int default_duration_in_secs = 90;プライベートTホルダー。プライベートロングevictiontimestamp;プライベートロング登録timestamp;プライベートLong ServiceUptimestamp; //有効期限タスクがこの速い私的な揮発性の長いlastDateTimestampを見るように揮発性にします。プライベート長い期間;パブリックリース(T r、int durationinsecs){Holder = r; RegistrationTimestamp = System.CurrentTimemillis(); lastUpDateTimestamp = RegistrationTimestamp;持続時間=(DurationInsecs * 1000); } /** *リースを更新し、登録中に *関連{@link t}によって指定された場合、更新期間を使用します。 */ public void Renew(){lastUpDateTimestamp = System.CurrentTimemillis() + duration; } /***立ち退き時間を更新することにより、リースをキャンセルします。 */ public void cancel(){if(evictiontimestamp <= 0){evictiontimestamp = system.currenttimemillis(); }} /***サービスを上にマークします。これは、最初の時間と呼ばれる最初の時間のみに影響を及ぼします *後続の呼び出しは無視されます。 */ public void serviceup(){if(serviceuptimestamp == 0){serviceuptimestamp = system.currenttimemillis(); }} /*** leavesサービスをタイムスタンプに設定します。 */ public void setServiceUptimestamp(long ServiceUptimestamp){this.ServiceUptimestamp = serviceUptimestamp; } /***特定の{@link com.netflix.appinfo.instanceinfo}のリースが有効であるかどうかを確認します。 */ public boolean isexpired(){return isexpired(0L); } /***特定の{@link com.netflix.appinfo.instanceinfo}のリースが有効であるかどうかを確認します。 * *「間違った」ことを行い、lastupDateTimestampを +lastupdateTimestampに設定することにより、 *は実際には2 *の期間になります。これはマイナーなバグであり、既存の使用に対する広範な影響の可能性があるため、非grace延の影響のために、既存の使用に幅広い影響を受けているため、これは修正されません。 Boolean isexpired(long additalalleasems){return(evictiontimestamp> 0 || system.currenttimemillis()>(lastupdatetimestamp + duration + additionalleasems);} / ** *は、リースが登録されていたときに登録されていたときに、エポックが登録されていたときに、エポックが登録されていたときにエポックが登録されていたときに、エポックがエポック以来数百万を取得します。 GetRegistrationTimeStamp(){returnationTimeStamp LastDateTimestampは、リースが解放されたときに数百万秒を取得しますエポックは、リースのサービスが上昇したときです。2)現在登録されているIDによると、マップで入手できる場合は、次のことを行います。
2.1)現在既存のノードのタッチタイムと登録ノードのタッチタイムによると、前の時間が後者の時間より遅い場合、現在登録されているインスタンスは既存のインスタンスの対象となります。
2.2)それ以外の場合は、予想される更新数とそのしきい値を更新します
3)現在の登録ノードをマップに保存し、登録プロセスが基本的に終了しました
2。ユーレカクライアント
サーバーServletContextが初期化されると、DiscoveryClientが作成されます。ユーレカに精通している友人は、これらの2つの属性に精通している必要があります:FetchregistryとRegisterWitheureka。 SpringCloudでIntegrated Eureka Independentモードで実行する場合、これらの2つの値が偽でない場合、スタートアップはエラーを報告します。なぜエラーを報告するのですか?実際、答えはDiscoveryClientのコンストラクターにあります。
@Inject discoveryClient(ApplicationInfomanager ApplicationInfomanager、eurekaclientConfig config、abstractdiscoveryclientoptionalargs args、provider <backupregistristryprovider){//.......oMitコードの部分データの登録もクエリもないように構成されています。 ");スケジューラ= null; heartbeatexecutor = null; cacherefreshexecutor = null; eurekatransport = null; instanceregionchecker = new instanceregionchecker(new PropertyBasedAztoreGionMapper(config)、clientConfig.getErgion()); //これは、discoverymanager.getInstance()を使用して既存のコードを許可するための少しハッキングです。 discoverymanager.getInstance()。seteurekaclientconfig(config); inittimestampms = system.currenttimemillis(); logger.info( "初期インスタンスCount:{}"、inittimestampms、this.getApplications()。size())を使用して、Timestampで初期化されたDiscoveryクライアント{}戻る; //ネットワークタスクをセットアップする必要はなく、私たちは完了しました} try {//ハートビートおよびcacherefreshスケジューラ= executors.newscheduledthreadpool(2、new SthreadFactoryBuilder().setNameBuilt( "DiscoveryClient -%d").setDaemon(true).buil()。 heartbeatexecutor = new SthreadPoolexecutor(1、clientConfig.getheartBeatexecutorthreadpoolsize()、0、timeUnit.seconds、new synchronousqueue <runnable>()、new SthreadFactoryBuilder()、setnameformat( "Discovery-Clientexutor- d"。 //直接ハンドオフcacherefreshexecutor = new SthreadPoolexecutor(1、ClientConfig.getCachereFreshExecutorThreadPoolsize()、0、TimeUnit.seconds、new synchronousqueue <runnable>()、new SthreadFactoryBuilder()。 。建てる() ); //直接ハンドオフeurekatransport = new eurekatransport()を使用します。 ScheduleServerEndpointTask(eurekatransport、args); //.....いくつかのコードinitscheduledtasks(); // ....}ソースコードに基づいて、次の結論を描画できます。
1)両方がwitheurekaとfelthregistryの両方がfalseである場合、直接戻ります。
2)ハートビートを送信してキャッシュをリフレッシュするスレッドプールを作成する
3)作成されたタイミングされたタスクを初期化します
次に、initscheduledtasks()メソッドの次のコードを見てみましょう。
// HeartBeat Timer Scheduler.schedule(new TimedSupervisortask( "Heartbeat"、heartbeat "、scheduler、heartbeatexecutor、renyalinterintervalinsecs、timeunit.seconds、expbackoffbound、new heartbeatthread()、renewalinterintervalinsecs、timeunit.seconds);
これは、数秒で時限実行をトリガーし、更新用のハートビートを実行するスレッドです。 HeartBeatThreadスレッドは次のように実行されます。
/***指定された間隔でリースを更新するハートビートタスク。 */ private class heartbeatthread runnable {public void run(){if(renew()){lastsuccessfuleheartbeattimestamp = system.currenttimemillis(); }}}実行方法は、更新方法を実行するのが非常に簡単であり、時間が正常に記録されるかどうかを確認できます。更新方法:
/ ** *適切なレストを作成することにより、eurekaサービスで更新 */ boolean regning(){eurekahttpresponse <intancanceinfo> httpresponse; try {httpresponse = eurekatransport.registrationclient.sendheartbeat(instanceinfo.getAppname()、instanceinfo.getid()、instanceinfo、null); logger.debug( "{} - heartbeat status:{}"、prefix + apppathidentifier、httpresponse.getStatuscode()); if(httpresponse.getStatusCode()== 404){reregister_counter.increment(); logger.info( "{} - apps/{}"、registing + apppathidentifier、instanceinfo.getAppname()); long timestamp = instanceinfo.setisdirtywithtime(); boolean success = register(); if(success){instanceinfo.unsetisdirty(Timestamp); }成功を返す; } httpresponse.getStatusCode()== 200; } catch(throwable e){logger.error( "{} - heartbeatを送信できませんでした!"、prefix + apppathidentifier、e); falseを返します。 }}ハートビートがここに送信される場合、返品が404の場合、登録操作が実行されます。リターン値httpresponseに基づいて、これらすべての操作はHTTP要求に基づいていると結論付けることができることに注意してください。それは本当ですか?登録方法を引き続き見てみましょう。
/***適切な休憩を行うことにより、ユーレカサービスに登録します。 */ boolean register()throws throwable {logger.info(prefix + apppathidentifier + ":登録サービス..."); eurekahttpresponse <void> httpresponse; try {httpresponse = eurekatransport.registrationclient.register(instanceinfo); } catch(Exception e){logger.warn( "{} - 登録失敗{}"、prefix + apppathidentifier、e.getmessage()、e); eを投げる; } if(logger.isinfoenabled()){logger.info( "{} - 登録ステータス:{}"、prefix + apppathidentifier、httpresponse.getStatuscode()); } httpresponse.getStatusCode()== 204を返します。 }ここでは、eurekatransportの登録方法が次のように呼ばれます。
プライベート静的最終クラスeurekatransport {private cloderesolver bootstrapresolver; Private TransportClientFactory TransportClientFactory; private eurekahttpclient RegistrationClient; private eurekahttpclientFactory registrationClientFactory; private eurekahttpclient queryclient; private eurekahttpclientFactory QueryClientFactory; void shutdown(){if(registrationClientFactory!= null){registrationClientFactory.shutdown(); } if(queryClientFactory!= null){QueryClientFactory.ShutDown(); } if(registrationClient!= null){restionalclient.shutdown(); } if(queryclient!= null){queryclient.shutdown(); } if(TransportClientFactory!= null){TransportClientFactory.ShutDown(); }}}ここでは、EurekaのクライアントがHTTP要求を使用してサービスを登録していることがわかります。つまり、DiscoveryClientを作成すると、サーバーにインスタンスを登録します。
3。サーバーが提供する休憩サービス
クライアントの登録要求を処理するためにサーバーが提供するコードをすでに見ています。クライアントはHTTPプロトコルを介して登録するため、サーバーにはこのHTTP要求を処理するアドレスが必要です。実際、Eureka ServerはJAX-RS標準を使用して、サービスを公開するためにRESTメソッドを提供します。このアプリケーションリソースのAddInstanceメソッドを見ることができます。
/** * * {@link com.netflix.discovery.shared.application}の特定のインスタンスに関する情報を登録します。 * * @param info * {@link instanceinfo}インスタンスの情報。 * @param isReplication *これが他のノードから複製されているかどうかを含むヘッダーパラメーター。 */@post @consumes({"application/json"、 "application/xml"})public Response addinstance(instanceinfo info、@headerparam(peereurekanode.header_replication)string isReplication){logger.debug( "Registing Instance {}(}(Replication = {}) // InstanceInfoが必要なすべての必要なフィールドを含むことを確認するif(isblank(info.getid())){return response.status(400).entity( "missing instanceid")。build(); } else if(isblank(info.gethostname())){return response.status(400).entity( "Missing HostName")。build(); } else if(isblank(info.getipaddr())){return response.status(400).entity( "欠落IPアドレス")。build(); } else if(isblank(info.getAppname()))){return response.status(400).entity( "Missing AppName")。build(); } else if(!appname.equals(info.getAppname())){return response.status(400).entity( "but + appname +"を期待するが、 " + info.getAppname())。build(); } else if(info.getDataCenterInfo()== null){return response.status(400).entity( "Missing DataCenterInfo")。build(); } else if(info.getDataCenterInfo()。getName()== null){return response.status(400).entity( "Missing DataCenterInfo")。build(); } else if(info.getDataCenterInfo()。getName()== null){return response.status(400).entity( "欠損dataCenterinfo name")。build(); } //不足しているデータDataCenterInfo dataCenterInfo = info.getDatacenterInfo(); if(dataCenterInfo instance of initeidentifier){string dataCenterInfoid =((((((((((((」」」dataCenterInfo).getId(); if(isblank(dataCenterInfoid)){boolean experimental = "true" .equalsignorecase(serverConfig.getExperimental( "restional.validation.datacenterinfoid")); if(experimental){string entity = "type" + dataCenterInfo.getClass() + "の有効なIDを含める必要があります。 RESPONSE.STATUS(400).Entity(Entity).Build(); } else if(dataCenterInfo instance of amazoninfo){amazoninfo amazoninfo =(amazoninfo)dataCenterInfo; string effectionid = amazoninfo.get(amazoninfo.metadatakey.instanceId); if(effectionid == null){amazoninfo.getMetadata()。put(amazoninfo.metadatakey.instanceId.getName()、info.getId()); }} else {logger.warn( "適切なIDなしでタイプ{}のdataCenterInfoの登録"、dataCenterInfo.getClass()); }}}} registry.register(info、 "true" .equals(isReplication)); RESPONSE.STATUS(204).build(); // 204後方に互換性がある}上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。