Szene
Der Betrieb von Microservices veröffentlichen in der Regel ein neues Codepaket, tötet die laufende Anwendung ab, ersetzt das neue Paket und starten Sie es.
Eureka wird als Registrierungszentrum in der Spring Cloud verwendet, die die Verzögerung der Dienstlistendaten ermöglicht. Auch wenn sich die Anwendung nicht mehr in der Serviceliste befindet, fordert der Client diese Adresse weiterhin für einen bestimmten Zeitraum an. Dann wird die Anfrage angezeigt, was zu einem Fehler führt.
Wir werden die Aktualisierungszeit der Serviceliste optimieren, um die Aktualität der Informationen zur Serviceliste zu verbessern. Aber egal was passiert, es ist unmöglich, einen Zeitraum zu vermeiden, in dem Daten inkonsistent sind.
Eine Möglichkeit, wie wir dachten, ist, den Mechanismus wiederzuholen. Wenn eine Maschine A neu gestartet wird, kann B im selben Cluster Dienste normal anbieten. Wenn es einen Wiederholungsmechanismus gibt, können Sie B im obigen Szenario wiederholen, ohne die richtige Antwort zu beeinflussen.
arbeiten
Die folgenden Operationen sind erforderlich:
Ribbon: Readtimeout: 10000 ConnectTimeout: 10000 MaxAutoretries: 0 MaxAutoretriesnextServer: 1 OktoretryonAlaloperations: Falsch
Einführung des Spring-Retry-Pakets
<Depopentcy> <gruppe> org.springframework.retry </Groupid> <artifactId> Spring-Retry </artifactid> </abhängig>
Wenn Sie Zuul als Beispiel einnehmen, müssen Sie auch konfigurieren und erneut aktivieren:
Zuul.retryable = true
Auf ein Problem gestoßen sind
Alles war jedoch nicht so glatt. Der Test -Wiederholungsmechanismus wirkte sich in Wirkung, forderte jedoch keine andere gesunde Maschine an, wie ich es mir vorgestellt hatte. Also war ich gezwungen, zum Open Source -Code zu gehen und stellte schließlich fest, dass es sich um einen Quellcode -Fehler handelte, aber es wurde behoben und die Version wurde aktualisiert.
Codeanalyse
Die verwendete Version ist
Spring-Cloud-Netflix-Core: 1.3.6.Release
Spring-Retry: 1.2.1.Release
Spring Cloud -Abhängigkeitsversion:
<DepepecyManagement> <Depelencies> <Depopenty> <gruppe> org.springframework
Da das Wiederholen aktiviert ist, wird die Methode für RetryableribbonloadBalancingHttpclient.execute ausgeführt, wenn die Anwendung angefordert wird:
public ribbonapachehttpesponse execute (endgültige RibbonapacheHttpRequest -Anforderung, endgültige iClientConfig configOverride) löst die Ausnahme aus {endgültig RequestConfig.builder Builder = RequestConfig.custom (); IClientConfig config = configOverride! = Null? configOverride: this.config; Builder.setConnectTimeout (config.get (CommonClientConFigkey.ConnectTimeout, this.connectTimeout)); builder.setsockettimeout (config.get (CommonClientConFigkey.Readtimeout, this.ReadTimeout)); builder.setRectSenabled (config.get (CommonClientConFigkey.Followredirects, this.FollowRedirects)); endgültige RequestConfig RequestConfig = builder.build (); lastbalancedRetryPolicy retypolicy = loadBalancedRetryPolicyFactory.create (this.getClientname (), this); Retrycallback retrycallback = new retrycallback () {@Override public bonapacheHttpesponse dowithrsery (retryContext context) löst Ausnahme aus {// Auf dem neuesten Stand Die Richtlinie wählt den Server aus und setzt ihn im Kontext // die Servere extrahiert und aktualisiert die Anfrage, die RibbonapacheHttprequest = Anfrage erfolgt. if (contextinstanceOf loadBalancedRetryContext) {ServiceInstance Service = ((loadBalancedRetryContext) .GetServiceInstance (); if (service! newRequest.geturi (). getQuery (), newRequest.geturi (). getfragment ())); }} newRequest = GETECUREquest (Anfrage, configOverride); Httpurirequest httpurirequest = newRequest.torequest (RequestConfig); endgültig httPresponse httPresponse = retryableribbonloadBalancingHttpclient.this.delegate.execute (httpurirequest); if (retrypolicy.retryablestatusCode (httpesponse.getStatusLine (). getStatusCode ()) {if (CloSeableHtttPesponse.class.issinstance (httPespect)) {(ClospeableAsHttPesponse) httpespesponse) .closes (). } neue retyryablestatuscodeexception werfen (retryableribbonLoadBalancingHttpclient.this.clientname, httPesponse.getStatusLine (). getStatusCode ()); } return New RibbonapacheHttPesponse (httPesponse, httpurirequest.geturi ()); }}; Gibt dies aus. ExecuteWitRetry (Anfrage, Wiederholung, Retyprycallback); }Wir haben festgestellt, dass wir zuerst einen RetryCallback neu haben und dann dies ausführen. ExecuteWithretry (Anfrage, Retypolicy, RetryCallback);
Wir sehen deutlich, dass der Code von RetryCallback.dowithretry der tatsächliche angeforderte Code ist, was bedeutet, dass dies.
Protected <t, E erweitert Throwable> t doExecute (retrycallback <t, e> retrycallback, RecoveryCallback <T> RecoveryCallback, Returnystate -Status). Backoffpolicy BackOffpolicy = this.backoffpolicy; // Erlauben Sie der Wiederholung der Richtlinie, sich selbst zu initialisieren ... RetyryContext Context = Open (retypolicy, Zustand); if (this.logger.istaceEnabled ()) {this.logger.trace ("retryContext abgerufen:" + context); } // Stellen Sie sicher, dass der Kontext weltweit für Kunden verfügbar ist, die // IT ... RetrySynchronizationManager.register (Kontext); Throwable lastException = null; boolean erschöpft = falsch; Versuchen Sie es mit {// Clients die Möglichkeit, den Kontext zu verbessern ... boolean running = doOpenInterceptors (RetryCallback, Kontext); if (! rennen) {neue terminedRetryException ("Wiederholung abnormal von Interceptor vor dem ersten Versuch"); } // den Backoff -Kontext abrufen oder starten ... BackOffContext BackOffContext = null; Object Resource = context.getAttribute ("BackOffContext"); if (Ressourceninstanz von BackOffContext) {BackOffContext = (BackOffContext) Ressource; } if (BackOffContext == null) {BackOffContext = BackOffPolicy.Start (Kontext); if (BackOffContext! }} / * * Wir erlauben, dass die gesamte Schleife übersprungen wird, wenn die Richtlinie oder der Kontext bereits den ersten Versuch verbieten. Dies wird im Fall von externem Wiederholung verwendet, um eine * Wiederherstellung in Handleretryexhasted ohne die Rückrufverarbeitung zu ermöglichen (die eine Ausnahme ausgeben würde). */ while (canretry (retypolicy, context) &&! context.isexhaustedonly ()) {try {if (this.Logger.isdebugenabled ()) {this.logger.debug ("retyry: count =" + context.getrycount (); } // Setzen Sie die letzte Ausnahme zurück. Wenn wir also erfolgreich sind // Die engen Interceptors werden nicht glauben, dass wir versagt haben ... lastException = null; return retrycallback.dowithretry (Kontext); } catch (throwable e) {lastException = e; try {register toWowable (retypolicy, Zustand, Kontext, e); } catch (Ausnahme ex) {neue terminedRetryException werfen ("konnte nicht throwable registrieren", ex); } endlich {doonErrorInterceptors (retryCallback, Kontext, e); } if (canretry (retypolicy, context) &&! context.isexhaustedonly ()) {try {BackOffPolicy.backoff (BackOffContext); } catch (BackOffInterruptedException ex) {lastException = e; // Zurück -Off wurde durch einen anderen Thread verhindert - fehlen Sie die Wiederholung, wenn (this.logger.isdebugenabled ()) {this.logger .debug ("RETORT REGRY WIRKRY WIRKTEMENDE: count =" + context.getRyCount ()); } werfen ex; }} if (this.logger.isdebugenabled ()) {this.logger.debug ("Überprüfung auf Rethrow: count =" + context.getRyCount ()); } if (sollteRethrow (retypolicy, context, Status)) {if (this.logger.isdebugenabled ()) {this.logger.debug ("Rethrow in Wiederholung für Richtlinien: count =" + context.getRyCount ()); } RetyryTemplate werfen. }} / * * Ein statiger Versuch, der wiederholt kann, kann die Ausnahme früher überdenken, * aber wenn wir in einem staatlichen Wiederholung so weit kommen, gibt es einen Grund dafür, * wie ein Leistungsschalter oder ein Rollback -Klassifizierer. */ if (state! = null && context.hasAttribute (global_state)) {break; }} if (state == null && this.logger.isdebugenabled ()) {this.logger.debug ("Wiederholung des letzten Versuchs fehlgeschlagen: count =" + context.getRyCount ()); } erschöpft = true; return HandleretryExausted (RecoveryCallback, Kontext, Zustand); } catch (throwable e) {RetyryTemplate. } endlich {close (retypolicy, Kontext, Zustand, lastException == null || erschöpft); DocloseInterceptors (RetryCallback, Kontext, LastException); RetrySynchronizationManager.Clear (); }}Implementieren Sie den Wiederholungsmechanismus in einer Weile Schleife. Wenn eine Ausnahme auftritt, wenn ein Retyprycallback.dowithretry (Kontext) ausgeführt wird, wird eine Ausnahme eingerichtet. Verwenden Sie dann die Wiederholung, um zu bestimmen, ob sie erneut werden sollen. Achten Sie besonders auf Registrierdrowsable (Retypolicy, Zustand, Kontext, e); Verfahren. Es entscheidet nicht nur, ob es wiederholt werden soll, sondern im Rahmen des Wiederholung wird eine neue Maschine ausgewählt und in den Kontext gesteckt, und dann wird sie beim Retyprycallback eingebracht.
Aber warum hat meine Konfiguration das Telefon nicht geändert? Der Debugging -Code ergab, dass Registerrowable (Wiederholung, Zustand, Kontext, e); Die ausgewählte Maschine ist in Ordnung, es ist eine neue und gesunde Maschine, aber beim Ausführen des RetRyCallback.dowithretry (Kontext -Code) wird er weiterhin angefordert.
Schauen wir uns also den Code von RetryCallback genauer an. Dowithretry (Kontext):
Wir fanden diese Codezeile:
newRequest = GetEcureRequest (Anfrage, configOverride); Protected RibbonapacheHttprequest GetEcurerequest (RibbonapachehttpRequest Request, IclientConfig configOverride) {if (isspecure (configOverride)) {endgültig uri Secureuri = HarricomponentsBuilder.fromuri (Anregung.Geturi () .Scheme (). Rückgabeanforderung. } Rückgabeanforderung; }Der NewRequest wurde im vorherigen Beispiel mit dem Kontext erstellt. Die Anfrage sind die Daten, die letzte Mal angefordert wurden. Solange Sie diesen Code ausführen, werden Sie feststellen, dass der NewRequest von der Anfrage immer überschrieben wird. Als wir das sahen, fanden wir heraus, dass es sich um einen Quellcode -Fehler handelte.
Problemadresse: https://github.com/spring-cloud/spring-cloud-netflix/issues/2667
Zusammenfassen
Dies ist ein sehr gewöhnlicher Prozess der Überprüfung von Problemen. Als ich feststellte, dass die Konfiguration meine Erwartungen nicht erfüllte, habe ich zuerst die Bedeutung der Konfiguration überprüft und sie ohne Erfolg mehrmals ausprobiert. Nach dem Debugug des Haltepunkts stellte ich fest, dass der Haltepunkt abnormal war. Da für die Szene eine Maschine gesunde und eine Maschine offline sein müssen, simulierte ich sie hunderte Male, bevor ich diese Codezeile schließlich gefunden habe. Sogar ein Open -Source -Projekt ist ein hervorragendes Projekt und wird unweigerlich Fehler haben, nicht abergläubisch oder blind. Andererseits ist die Fähigkeit, Quellcode zu lesen, auch eine wichtige Fähigkeit, Probleme zu lösen. Zum Beispiel suche ich nach Quellcode -Eingängen und es dauert viel Zeit, um den Code zu finden.
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.