Eureka ist eine dezentrale Anwendung für Service -Governance -Anwendung, und ihre charakteristische Funktion ist, dass sie mit der Adresse registriert werden kann, die Sie sowohl als Server als auch als Service konfiguriert haben. Lassen Sie uns in diesem Artikel den Registrierungsprozess von Eureka erörtern.
1. Server von Eureka
Die Kernklasse der Serverseite von Eureka ist EurekabootStrap, der einen Hörer von ServletContextListener implementiert. Daher können wir zu dem Schluss kommen, dass Eureka basierend auf Servlet -Containern implementiert wird. Der Schlüsselcode lautet wie folgt:
öffentliche Klasse EurekabootStrap implementiert servletContextListener {//...OMIT Related Code/*** Initialisiert Eureka, einschließlich der Synchronisierung mit anderen Eureka -Kollegen und Veröffentlichung der Registrierung. * * @see * javax.servlet.servletContextListener#contextInitialized (javax.servlet.servletContextevent) */ @Override public void contextInitialized (servletContexTevent) {try {IniteureKaenvironment (); IniteurekaserverContext (); ServletContext sc = event.getServletContext (); sc.SetAttribute (eurekaservercontext.class.getName (), servercontext); } catch (throwable e) {logger.Error ("kann Eureka -Server nicht bootstrap:", e); Neue RunTimeException werfen ("kann Eureka -Server nicht starten:", E); }} // verwandten Code aus dem verwandten Code auslassen ......}Wir können sehen, dass die Eureka -Umgebung, wenn die ServletContext -Initialisierung abgeschlossen ist, initialisiert wird und der Eurekaservercontext initialisiert wird. Dann werfen wir einen Blick auf die Initeurekaservercontext -Methode:
/*** Init Hook für den Serverkontext. Überschreiben Sie für benutzerdefinierte Logik. */ Protected void IniteurekaserverContext () löst Ausnahme aus {// ...... applicationInfomanager applicationInfomanager = null; if (eurekaclient == null) {eurekainstanceConfig InstanceConfig = iscloud (configurationManager.getDeploymentContext ())? neuer CloudInstanceConfig (): Neue mydataCenterIninStanceConfig (); applicationInfomanager = new ApplicationInfomanager (InstanceConfig, New EurekaconfigBasedInstanceInfoprovider (InstanceConfig) .get ()); Eurekaclientconfig EurekaClientConfig = new DefaultEureKaclientConfig (); eurekaclient = new DiscoveryClient (ApplicationInfomanager, EurekaclientConfig); } else {applicationInfomanager = eurekaclient.getApplicationInfomanager (); } PeerawareInStanceregistry Registry; if (isaws (applicationInfomanager.getInfo ())) {Registry = new Awsinstanceregistry (eurekaserverconfig, eurekaclient.geteureKaclientConfig (), Servercodecs, Eurekaclient); awsbinder = new awsbinderDelegate (eurekaserverconfig, eurekaclient.geteurekaclientconfig (), Registrierung, applicationInfomanager); awsbinder.start (); } else {registry = new peerawareInStanceregistryImpl (eurekaserverconfig, eurekaclient.geteureKaclientConfig (), servercodecs, eurekaclient); } //...Omit Teil des Code}Bei dieser Methode werden viele Objekte im Zusammenhang mit dem Eureka -Dienst erstellt. Hier liste ich zwei Kernobjekte auf, nämlich Eurekaclient und PeerawareInStanceregistry. Wir werden später über den Kundenteil sprechen. Schauen wir uns an, wofür die PeerawareInStanceregistry verwendet wird. Hier schreibe ich ein Klassendiagramm über diese Klasse:
Laut dem Klassendiagramm können wir deutlich feststellen, dass die oberste Schnittstelle von Peerawareinstanmeregistry Leasemanager und LookupService ist, wobei LookupService das Verhalten der grundlegendsten Entdeckungsbeispiele definiert, während Leasemanager die Verarbeitung von Kundenregistrierung, Erneuerungs- und Stornierungsbetrieb definiert. Konzentrieren wir uns in diesem Artikel also auf die Implementierung der zugehörigen Schnittstellen von Leasemanager. Rückblickend schauen wir uns die PeerawareInstanceRegistry an. Tatsächlich wird diese Klasse verwendet, um relevante Informationen unter mehreren Knoten zu kopieren. Wenn beispielsweise ein Knoten für Erneuerung und Offline registriert wird, wird die relevante Kopie (Benachrichtigung) über diese Klasse an jeden Knoten kopiert. Mal sehen, wie es die Kundenregistrierung umgeht:
/** * registriert die Informationen zum {@link InstanceInfo} und Replicas * Diese Informationen an alle Peer -Eureka -Knoten. Wenn dies Replikationsereignis * von anderen Replikatknoten ist, wird es nicht repliziert. * * @param info * Die zu registrierte und replizierte {@linkinstanceInfo}. * @param isreplication * true Wenn dies ein Replikationsereignis von anderen Replik -Knoten ist, * falsch falsch. */ @Override public void Register (endgültige Instanzinfo, endgültige boolean isReplication) {int mieteruration = lease.default_duration_in_secs; if (info.getleaseInfo ()! } Super.register (Info, Mietversuch, Isreplikation); Replicatetopeers (action.register, info.getAppname (), info.getId (), Info, Null, Isreplikation); }Wir können sehen, dass es die Registermethode der übergeordneten Klasse aufruft und dann das entsprechende Verhalten durch Replikatetoper an andere Knoten repliziert. Die spezifische Replikation wird hier nicht besprochen. Konzentrieren wir uns auf die Registrierungsmethode. Wir finden die Methode Register () in der übergeordneten Klasse:
/*** registriert eine neue Instanz mit einer bestimmten Dauer. * * @see com.netflix.eureka.lease.Leasemanager#Register (java.lang.object, int, boolean) */ public void Register (InstanceInfo -Registrierung, intiertes Mietversuch, boolean isReplication) {try {read.lock (); Karte <String, Lease <InstanceInfo >> gmap = Registry.get (Registrant.getAppname ()); Register.increment (ISReplikation); if (gmap == null) {endgültige concurrentHashMap <String, Lease <InstanceInfo >> Gnewmap = new ConcurrentHasMap <String, Lease <instanceInfo >> (); GMAP = Registry.putiFabSent (Registrant.getAppname (), Gnewmap); if (gmap == null) {GMAP = GNEWMAP; }} Lease <instanceInfo> vorhandeneLease = gmap.get (Registrant.getId ()); // behalten Sie den letzten schmutzigen Zeitstempel, ohne ihn zu überschreiben, wenn es bereits einen Mietvertrag gibt, wenn (vorhanden! Long RegistrationLastDirtyTimestamp = registriert.getLastDirtimestamp (); logger.debug ("vorhandener Leasing gefunden (vorhanden = {}, bereitgestellt = {}", vorhandeneLastDirtyTimestamp, RegistrierungLastDirtytimestamp); // Dies ist ein> anstelle eines> =, weil die Zeitstempel gleich sind, wenn die Zeitstempel gleich sind, und nehmen immer noch das Remote -Sendung // InstanceInfo anstelle des Server -Lokales. logger.warn ("Es gibt einen vorhandenen Mietvertrag und der schmutzige Zeitstempel des Mietvertrags {} ist größer" + "als derjenige, der registriert ist {}", existieren Der Mietvertrag existiert nicht und es handelt sich um eine neue Registrierung, die synchronisiert ist (Lock) {if (this.ecwednumbernewSPermin> 0) {// Da der Kunde ihn stornieren will, reduzieren Sie den Schwellenwert // (1 // 30 Sekunden lang) = (int) (this.expectNumberneweNmin Lease.SetSeViceUpTimStam (vorhandene Sicht).Lassen Sie uns über den Quellcode kurz den Prozess aussortieren:
1) Erstens einige Spalten von Service -Instanzobjekten basierend auf dem AppName erhalten. Wenn es null ist, erstellen Sie eine neue Karte und fügen Sie die aktuellen registrierten Anwendungsinformationen dieser Karte hinzu. Hier gibt es ein Mietobjekt. Diese Klasse beschreibt die Zeitattribute von Generika wie Registrierungszeit, Startzeit für Dienstleistungen, endgültige Update -Zeit usw. Sie können auf die Implementierung achten:
/ * * Copyright 2012 Netflix, Inc. * * Lizenziert unter der Apache -Lizenz, Version 2.0 (die "Lizenz"); * Sie dürfen diese Datei nur in Übereinstimmung mit der Lizenz verwenden. * Sie können eine Kopie der Lizenz unter * * http://www.apache.org/licenses/license-2.0 * * * *, sofern nach geltendem Recht oder schriftlich gefordert oder schriftlich gefordert wird, Software *, die im Rahmen der Lizenz verteilt ist, auf "As "basis * ohne Gewährleistungen oder Bedingungen jeglicher Art verteilt oder implementiert. * Siehe die Lizenz für die spezifischen Sprachregierungsberechtigungen und * Einschränkungen im Rahmen der Lizenz. */Paket com.netflix.eureka.lease; import com.netflix.eureka.registry.abstractinStanceregistry;/*** beschreibt eine zeitbasierte Verfügbarkeit eines {@link t}. Ziel ist es, die Ansammlung von Instanzen in {@link contractInstanceregistry} als Ergebnis von unanständigen Herunterfahren zu vermeiden, die in AWS -Umgebungen nicht ungewöhnlich sind. * * Wenn ein Mietvertrag ohne Erneuerungen verstrichen, wird er letztendlich kontinuierlich ablaufen * * @Author Karthik Ranganathan, Greg Kim */Public Class Lease <T> {enum action {Register, abbrechen, erneuern}; public static final int default_duration_in_secs = 90; privater T -Halter; privat langer Räumungstimestamp; privat langer Registrierungstempel; privat langer Serviceuptimestamp; // Machen Sie es volatil, damit die Ablaufaufgabe diesen schnelleren privaten, volatilen langen LastUpDatetimestamp erzeugt. private lange Dauer; public miet (t r, int durationInsecs) {Holder = r; RegistrationTimestAMP = System.currentTimemillis (); lastUpDATETIMESTAMP = RegistrationTimestAMP; Dauer = (DurationInsecs * 1000); } /** * erneuern Sie den Mietvertrag, verwenden Sie die Erneuerungsdauer, wenn sie während der Registrierung durch den zugehörigen {@link t} angegeben wurde. Andernfalls lautet die Standarddauer * {@link #default_duration_in_secs}. */ public void renew () {lastUpDateTimestamp = System.currentTimemillis () + Dauer; } /*** Storniert den Mietvertrag durch Aktualisierung der Räumungszeit. */ public void corn () {if (evictionTimestamp <= 0) {evictionTimestamp = system.currentTimemillis (); }} /*** markieren Sie den Dienst als Up. Dies wirkt sich nur um das erste Mal bezeichnet. * Nachfolgende Anrufe werden ignoriert. */ public void serviceUp () {if (serviceUntimestamp == 0) {serviceuptimestamp = system.currentTimemillis (); }} /*** Legen Sie den Blätterdienst nach oben. */ public void setServiceUntimestamp (langes Serviceuptimestamp) {this.serviceUpTimestamp = serviceuptimestamp; } /*** prüft, ob der Mietvertrag eines gegebenen {@link com.netflix.appinfo.instanceInfo} abgelaufen ist oder nicht. */ public boolean isexpired () {return isExpired (0l); } /*** prüft, ob der Mietvertrag eines gegebenen {@link com.netflix.appinfo.instanceInfo} abgelaufen ist oder nicht. * * Beachten Sie, dass aufgrund von erneuerung () das "falsche" Ding und das Setzen von LastUpDateTimestamp auf +Dauer mehr als * Was es sein sollte, tatsächlich 2 * Dauer sein wird. Dies ist ein kleiner Fehler und sollte nur beeinflussen. iSexpired (long Additionalleasems) {return (evictionTimestamp> 0 || system.currentTimillis ()> (lastUpDateTimestamp + Dauer + Additionalleasems);} / ** * erhält die MillionSeconds, da EPOCH, als das Pacht registriert wurde. Registrierungstempel; da wurde der Mietvertrag über die Mietvertrag, die die Mietvertrag entfacht hat, entfaltet GetServiceUntimestamp () {return Serviceuptimestamp;2) Wenn Sie sie nach der aktuell registrierten ID auf der Karte erhalten, machen Sie Folgendes:
2.1) gemäß der Berührungszeit des derzeit vorhandenen Knotens und der Berührungszeit des registrierten Knotens unterliegt die derzeit registrierte Instanz der vorhandenen Instanz.
2.2) Aktualisieren Sie ansonsten die erwartete Erneuerungsnummer pro Minute und die Schwelle
3) Speichern Sie den aktuellen Registrierungsknoten in der Karte, und unser Registrierungsprozess ist im Grunde zu Ende
2. Eureka -Kunde
Wenn der Server -ServletContext initialisiert wird, wird ein DiscoveryClient erstellt. Freunde, die mit Eureka vertraut sind, müssen mit diesen beiden Attributen vertraut sein: Fetchregistry und Register Witheureka. Wenn diese beiden Werte nicht falsch sind, meldet das Startup einen Fehler, wenn diese beiden Werte nicht falsch sind. Warum meldet es einen Fehler? Tatsächlich liegt die Antwort im Konstruktor von DiscoveryClient:
@Inject DiscoveryClient (ApplicationInfomanager applicationInfomanager, EurekaClientConfig -Konfiguration, AbstractDiscoveryClientoptionalArgs Args, Anbieter <Backupregistry> BackupregistryProvider) {// ....... weder registrieren noch abfragen nach Daten. "); Scheduler = null; Heartbeatexecutor = null; cachereFreshExecutor = null; eUREKATRANSPORT = NULL; instanceregionChecker = new InstanceregionChecker (new PropertyBasedAntoregionMapper (config), clientconfig.getRegion ()); // Dies ist ein bisschen Hack, um vorhandenen Code mit DiscoveryManager.getInstance () // mit di'd DiscoveryClient DiscoveryManager.getInstance () zu arbeiten. SetDiscoveryClient (this); DiscoveryManager.getInstance (). SeteureKaclientConfig (config); Inittimestampms = System.currentTimemillis (); logger.info ("Discovery -Client in Timestamp {} mit anfänglichen Instanzen zählen: {}", Inittimestampms, this.getApplications (). size ()); zurückkehren; // Es ist nicht erforderlich, eine Netzwerkaufgabe einzurichten, und wir sind fertig} try {// Standardgröße von 2 - 1 jeweils für Herzschlag und CachereFresh Scheduler = Executors.NewScheduledThreadpool (2, new threadFactoryBuilder () .setnameformat ("Discoveryclient -%d"). Heartbeatexecutor = neuer Threadpoolexecutor (1, ClientConfig.GetheartbeeTexecutHeadpoolsize (), 0, TimeUnit.sekunden, neue Synchronousqueue <Runnable> (), new threadFactoryBuilder () .SetNameFormat ("Discoveryclientclient-HeartbeatbeatExecutor-iguTorybuilder (). // Verwenden Sie Direct Handoff CacheRefresExecutor = New ThreadPoolexecutor (1, Clientconfig.getCacheReFeRexecutortHeadpoolsize (), 0, TimeUnit.seconds, New Synchronousqueue <Runnable> (), new threadFactoryBuilder (). .bauen() ); // Direct Handoff Eurekatransport = new eurekatransport () verwenden; ScheduleServerendPointTask (Eurekatransport, Args); //....Omit einige Code initScheduledTasks (); // ....}Basierend auf dem Quellcode können wir die folgenden Schlussfolgerungen ziehen:
1) Wenn beides RegisterWitheureka und Sollte -Fetchregistry falsch sind, kehren Sie direkt zurück.
2) Erstellen Sie einen Fadenpool, der Herzschläge sendet und Caches aktualisiert
3) Initialisieren Sie die erstellten zeitgesteuerten Aufgaben
Schauen wir uns dann den folgenden Code in der Methode InitScheduledTasks () an:
// Heartbeat Timer Scheduler.Schedule (New TimedSupervisortask ("Heartbeat", Scheduler, Heartbeeatexecutor, Erneuerungsintervalinsecs, Timeunit.Seconds, Expbackoffbound, New HeartbeatThread ()), Erneuerungsintervalinsecs, Timeunit.Seconds);Hier ist ein Thread, der in Sekundenschnelle eine zeitgesteuerte Ausführung auslöst und einen Sende -Herzschlag gemäß dem Wert der Erneuerungsintervalinsecs ausführt. Der Heartbeatthrad -Thread wird wie folgt ausgeführt:
/*** Die Herzschlagaufgabe, die den Mietvertrag in den angegebenen Intervallen erneuert. */ private class HeartbeatThread implementiert runnable {public void run () {if (renew ()) {lastSuccessfulheartbeeTimestamp = System.currentTimemillis (); }}}Wir können sehen, dass die Run -Methode sehr einfach die Renew -Methode ausführen kann und wenn die Zeit erfolgreich aufgezeichnet wird. Erneuerungsmethode:
/ ** * Erneuerung mit dem Eureka -Dienst, indem Sie den entsprechenden REST -Anruf erstellen */ boolean renew () {eurekahttpesponse <instanceInfo> httPesponse; try {httpresponse = eurekatransport.registrationClient.sendheartbeat (instanceInfo.getAppname (), instanceInfo.getId (), InstanceInfo, null); logger.debug ("{} - Heartbeat Status: {}", Präfix + AppPathIdentifier, httPesponse.getStatusCode ()); if (httpresponse.getStatusCode () == 404) {reregister_counter.increment (); logger.info ("{} - Wiederregistrierung von Apps/{}", Präfix + AppPathIdentifier, InstanceInfo.getAppname ()); long Timestamp = InstanceInfo.setIsDirtyWithTime (); boolescher Erfolg = Register (); if (Erfolg) {InstanceInfo.unsetisDirty (Zeitstempel); } Return Success; } return httPresponse.getStatusCode () == 200; } catch (throwable e) {logger.Error ("{} - konnte keinen Herzschlag senden!", Präfix + AppPathidentifier, e); false zurückgeben; }}Wenn der Herzschlag hierher gesendet wird, wird der Registrierungsvorgang durch die Rückgabe 404 durchgeführt. Beachten Sie, dass wir basierend auf dem Rückgabewert HTTPResponse schließen können, dass alle diese Operationen auf HTTP -Anforderungen basieren. Ist das wahr? Schauen wir uns weiterhin die Registermethode an:
/*** Registrieren Sie sich beim Eureka -Dienst, indem Sie den entsprechenden REST -Anruf tätigen. */ boolean register () löscht Throwable {logger.info (Präfix + AppPathIdentifier + ": Registrierung des Dienstes ..."); Eurekahttpesponse <void> httPesponse; try {httPresponse = eurekatranport.registrationClient.register (InstanceInfo); } catch (Ausnahme e) {logger.warn ("{} - Registrierung fehlgeschlagen {}", Präfix + AppPathIdentifier, e.getMessage (), e); werfen e; } if (logger.issinfoenabled ()) {logger.info ("{} - Registrierungsstatus: {}", Präfix + AppPathIdentifier, httPesponse.getStatusCode ()); } return httPresponse.getStatusCode () == 204; }Hier heißt die Registrierungsmethode in Eurekatransport:
private statische endgültige Klasse Eurekatransport {private closableresolver bootstrapresolver; private transportclientFactory transportclientFactory; private eurekahttpclient Registrierungspezialer; private eurekahttpclientFactory RegistrationClientFactory; private Eurekahttpclient QueryClient; private eurekahttpclientFactory queryClientFactory; void Shutdown () {if (RegistrationClientFactory! } if (queryClientFactory! = null) {queryClientFactory.shutdown (); } if (RegistrationClient! = null) {RegistrationClient.shutdown (); } if (queryClient! = null) {queryClient.shutdown (); } if (transportClientFactory! = null) {TransportClientFactory.ShutDown (); }}}Hier können wir sehen, dass der Client von Eureka die HTTP -Anfrage verwendet, um den Dienst zu registrieren. Dies bedeutet, dass wir beim Erstellen von DiscoveryClient die Instanz mit dem Server registrieren.
3. REST -Service vom Server bereitgestellt
Wir haben bereits den vom Server bereitgestellten Code gesehen, um Client -Registrierungsanforderungen zu bearbeiten. Da die Clients über das HTTP -Protokoll registriert, muss der Server über eine Adresse verfügen, um diese HTTP -Anforderung zu behandeln. Tatsächlich verwendet der Eureka-Server den JAX-RS-Standard, um die REST-Methode bereitzustellen, um den Dienst freizulegen. Wir können einen Blick auf die AddinStance -Methode dieser ApplicationResource werfen:
/** * Registriert Informationen zu einer bestimmten Instanz für eine * {@link com.netflix.discovery.shared.application}. * * @param info * {@link InstanceInfo} Informationen der Instanz. * @param isreplication * Ein Header -Parameter mit Informationen, ob dies * von anderen Knoten repliziert wird. */ @POST @Consumes({"application/json", "application/xml"}) public Response addInstance(InstanceInfo info, @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) { logger.debug("Registering instance {} (replication={})", info.getId(), isReplication); // validieren Sie, dass das InstanzInfo alle erforderlichen Felder enthält, wenn (isBlank (info.getId ()) {return response.status (400) .Entity ("fehlende InstanzID"). Build (); } else if (isBlank (info.gethostname ())) {return response.status (400) .Entity ("fehlender Hostname"). Build (); } else if (isBlank (info.getipaddr ()) {return response.status (400) .Entity ("fehlende IP -Adresse"). Build (); } else if (isBlank (info.getAppname ()))) {return response.status (400) .Entity ("fehlende AppName"). Build (); } else if (! appname.equals (info.getAppname ()))) {return response.status (400) .Entity ("nicht übereinstimmter AppName, erwartet" + appname + ", aber" + info.getappname ()). Build (); } else if (info.getDataCenterInfo () == null) {return response.status (400) .Entity ("fehlende DataCenterInfo"). Build (); } else if (info.getDatacenterInfo (). getName () == null) {return response.status (400) .Entity ("fehlende DataCenterInfo"). Build (); } else if (info.getDatacenterInfo (). getName () == null) {return response } // Fälle handhaben, in denen Clients möglicherweise mit schlechten Rechenzenterinfo mit fehlenden Daten datacenterInfo DataCenterInfo = info.getDatacenterInfo () registriert werden. if (DataCenterInfo -Instanz von Uniqueidentifier) {String DataCenterInfoid = ((UniqueIdentifier) DataCenterInfo) .getId (); if (isBlank (DataCenterInfoid)) {boolean experimental = "true" .equalSignoreCase (serverconfig.getExperimental ("Registrierung.Validation.DataCenterInfoid")); if (experimental) {String entity = "DataCenterInfo vom Typ" + DataCenterInfo.getClass () + "Muss eine gültige ID enthalten"; return response.status (400) .Entity (Entity) .build (); } else if (DataCenterInfo Instanz von AmazonInfo) {Amazoninfo AmazonInfo = (Amazoninfo) DataCenterInfo; String effect effektiv = Amazoninfo.get (Amazoninfo.Metadatakey.instanceId); if (effektiv == null) {Amazoninfo.getmetadata (). }} else {logger.warn ("Registrieren von DataCenterInfo vom Typ {} ohne eine entsprechende ID", DataCenterInfo.getClass ()); }}}} Registry.register (info, "true" .Equals (isReplication)); return response.status (204) .build (); // 204 um rückwärts kompatibel zu sein}Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, es wird für das Lernen aller hilfreich sein und ich hoffe, jeder wird Wulin.com mehr unterstützen.