Préface
Zuul est un composant open source fourni par Netflix, déterminé à fournir des services de routage, de surveillance, de résilience, de sécurité et d'autres services dynamiques sur la plate-forme cloud. Il existe également de nombreuses entreprises qui l'utilisent comme une partie importante de la passerelle. Cette année, le groupe d'architecture de l'entreprise a décidé de développer un produit de passerelle, d'intégrer le routage dynamique, des autorisations dynamiques, un quota de limite actuel et d'autres fonctions, offrant une gestion des appels de réseau externe unifiée pour les projets dans d'autres départements, et enfin la formation d'un produit (ALI a en fait des produits de passerelle mature à cet égard, mais il ne convient pas à des configurations personnalisées) et n'a pas de permis intégrés et de bombe à la baisse actuelle).
Cet article présente principalement le contenu pertinent sur le spring cloud zuul Unified Excepte Manipting and Fallback. Il est partagé pour votre référence et votre apprentissage. Je ne dirai pas beaucoup ci-dessous, jetons un coup d'œil à l'introduction détaillée ensemble.
1. Manipulation des exceptions unifiées dans le filtre
En fait, dans la version Edgware SR2 de SpringCloud, il existe une gestion unifiée des erreurs dans Zuulfilter, mais dans le développement réel, je pense que chaque équipe a ses propres spécifications de traitement pour les méthodes de réponse des erreurs. Alors, comment faire la manipulation des exceptions personnalisées?
Nous pouvons d'abord nous référer au SenderRorFilter fourni par SpringCloud:
/ * * Copyright 2013-2015 L'auteur ou les auteurs originaux. * * Licencié sous la licence Apache, version 2.0 (la "licence"); * Vous ne pouvez pas utiliser ce fichier sauf conforme à la licence. * Vous pouvez obtenir une copie de la licence à * * http://www.apache.org/licenses/license-2.0 * * sauf si la loi applicable ou convenu par écrit, le logiciel * distribué sous la licence est distribué sur une base "en tant que", * sans garantie ou conditions de toute nature, exprimée ou impliquée. * Voir la licence pour la langue spécifique régissant les autorisations et les * limitations sous la licence. * / package org.springframework.cloud.netflix.zuul.filters.post; import javax.servlet.requestdispatcher; import javax.servlet.http.httpservletRequest; importation org.apache.commons.logging.logging.Logging. org.apache.commons.logging.logfactory; import org.springframework.beans.factory.annotation.value; import org.springframework.cloud.netflix.zuul.util.reflectionutiles; import org.springframework.util.stringutils; import com.netflix.zuul.zuulfilter; import com.netflix.zuul.context.requestContext; import com.netflix.zuul.exception.zuulexception; import static static com.netflix.zuul.exception.zuulexception; import static static com.netflix.zuul.exception.zuulexception; import static static com.netflix.zuul.exception.zuulexception; Importer Static Static com.netflix. org.springframework.cloud.netflix.zuul.filters.support.filterconstants.error_type; import static org.springframework.cloud.netflix.zuul.filters.support.filterconstants.send_error_filter_Order; / ** * error {@link zuulferror} to-oorder; / ** * erre / error (par défaut) si {@link requestContext # getThrowable ()} n'est pas nul. * * @author spencer gibb * /// todo: déplacer le package d'erreur dans la classe EdgWarePublic SenderrorFilter étend ZuulFilter {private static final log = logfactory.getLog (SenderrorFilter.class); String final statique protégé Send_error_filter_ran = "senderrorfilter.ran"; @Value ("$ {error.path: / error}") String privé errorpath; @Override public String filterType () {return error_type; } @Override public int filterOrder () {return send_error_filter_order; } @Override public boolean aitfilter () {requestContext ctx = requestContext.getCurrentContext (); // transmettez à ErrorPath s'il n'a pas été transmis à nouveau à retourner ctx.getThrowable ()! = null &&! ctx.getBoolean (send_error_filter_ran, false); } @Override public objet run () {try {requestContext ctx = requestContext.getCurrentContext (); Zuulexception exception = findzuulexception (ctx.getThrowable ()); HttpServLetRequest request = ctx.getRequest (); request.setAttribute ("javax.servlet.error.status_code", exception.nstatuscode); Log.Warn ("Erreur pendant le filtrage", exception); request.setAttribute ("javax.servlet.error.exception", exception); if (stringUtils.hastext (exception.errorcause)) {request.setAttribute ("javax.servlet.error.mesage", exception.errorcause); } RequestDispatcher Dispatcher = request.getRequestDispatcher (this.errorpath); if (Dispatcher! = null) {ctx.set (send_error_filter_ran, true); if (! ctx.getResponse (). isCommited ()) {ctx.setResponSestAruscode (exception.nstaturScode); Dispatcher.Forward (demande, ctx.getResponse ()); }}} catch (exception ex) {refletUtils.rethrowRuntimeException (ex); } return null; } Zuulexception FindzuUulexception (Throwable Throwable) {if (throwable.getcause () de zuulruntimeException) {// Il s'agissait d'un échec initié par l'un des filtres locaux return (zuulexception) throwable.getcause (). Getcause (); } if (throwable.getcause () instanceof zuulexception) {// Exception zuul enveloppée return (zuulexception) throwable.getcause (); } if (instance thrognable de Zuulexception) {// Exception lancée par Zuul Lifecycle Return (Zuulexception) Throwable; } // Fallback, ne devrait jamais obtenir ici Retourne New Zuulexception (Throwable, httpservletResponse.sc_internal_server_error, null); } public void SetErrorPath (String errorPath) {this.errorpath = errorPath; }}Ici, nous pouvons trouver plusieurs points clés:
1) Dans le code ci-dessus, nous pouvons constater que le filtre a mis les informations d'erreur pertinentes dans la demande:
request.setAttribute ("javax.servlet.error.status_code", exception.nstatuscode);
request.setAttribute ("javax.servlet.error.exception", exception);
request.setAttribute ("javax.servlet.error.mesage", exception.errorcause);
2) Une fois l'erreur traitée, elle sera transmise à l'adresse xxx / erreur pour le traitement
Ensuite, nous pouvons faire une expérience. Nous créons un filtre qui lance des exceptions dans le module de projet de service de passerelle:
Package com.hzgj.lyrk.springcloud.gateway.server.filter; import com.netflix.zuul.zuulfilter; importage Lombok.extern.slf4j.slf4j; import org.springframework.sterreteotype.comPonent; @ composant @ slf4jpublic classe @Override public String filterType () {return "post"; } @Override public int filterOrder () {return 9; } @Override public boolean aitfilter () {return true; } @Override public objet run () {log.info ("Exécuter le test d'erreur ..."); lancer un nouveau runtimeException (); // retourne null; }}Ensuite, nous définissons un contrôleur pour gérer les erreurs:
package com.hzgj.lyrk.springcloud.gateway.server.filter; import org.springframework.http.httpstatu; import org.springframework.http.annotation.getmaping; org.springframework.web.bind.annotation.restController; import javax.servlet.http.httpservletRequest; @RestControllerPublic class ErrorHandler {@getMapping (valeur = "/ error") public réponse <rorrEBean> Errear (httpservillequest request) {String message = request.getAttribute ("javax.servlet.error.mesage"). toString (); Errorbean errorbean = new ErrorBean (); errorbean.setMessage (message); errorbean.setReason ("Erreur du programme"); return new ResponseNtity <> (errorbean, httpstatus.bad_gateway); } classe statique privée errorbean {message de chaîne privée; Raison de chaîne privée; public String getMessage () {return message; } public void setMessage (Message de chaîne) {this.Message = message; } public String getReason () {Retour Raison; } public void setReason (String Reason) {this.reason = raisonnement; }}}Après avoir commencé le projet, essayons d'y accéder via la passerelle:
2. Questions sur le repli Zuul
1. Concernant le problème de délai d'expiration de Zuul:
Il existe de nombreuses solutions à ce problème en ligne, mais je souhaite également publier le code source. Veuillez prêter attention à cette classe AbstractribbonCommand, qui intègre Hystrix et Ribbon dans cette classe.
/ * * Copyright 2013-2016 L'auteur ou les auteurs originaux. * * Licencié sous la licence Apache, version 2.0 (la "licence"); * Vous ne pouvez pas utiliser ce fichier sauf conforme à la licence. * Vous pouvez obtenir une copie de la licence à * * http://www.apache.org/licenses/license-2.0 * * sauf si la loi applicable ou convenu par écrit, le logiciel * distribué sous la licence est distribué sur une base "en tant que", * sans garantie ou conditions de toute nature, exprimée ou impliquée. * Voir la licence pour la langue spécifique régissant les autorisations et les * limitations sous la licence. * * / package org.springframework.cloud.netflix.zuul.filter org.springframework.cloud.netflix.ribbon.ribbonHttpResponse; import org.springframework.cloud.netflix.ribbon.support.abstractloadBalancingClient; import org.springframework.cloud.netflix.ribbon.support.contextAwareRerest; Importer; import; org.springframework.cloud.netflix.zuul.filters.zuulproperties; import org.springframework.cloud.netflix.zuul.filter org.springframework.cloud.netflix.zuul.filter com.netflix.client.abstractloadBalancerAwAreclient; import com.netflix.client.clientRequest; import com.netflix.client.config.defaultClientConfigImp; import com.netflix.client.config.iclientconfig; import com.netflix.client.config.iclientconfigy; com.netflix.client.http.HttpResponse;import com.netflix.config.DynamicIntProperty;import com.netflix.config.DynamicPropertyFactory;import com.netflix.hystrix.HystrixCommand;import com.netflix.hystrix.HystrixCommandGroupKey;import com.netflix.hystrix.hystrixcommandkey; import com.netflix.hystrix.hystrixcommandproperties; import com.netflix.hystrix.hystrixcommandproperties.executionisolationstrategy; import com.netflix.hystrix.hystrixThreadpooly; com.netflix.zuul.constants.zuulConstants; import com.netflix.zuul.context.requestContext; / ** * @Author Spencer Gibb * / public Abstract Class AbstractRBONCAmand <LBC étend AbstractLoDBerBerawareclient <RQ, RS>, RQ Extends ClientRequest, RS prolonge HTPPERSE> ExtendS HystRixCommand <SimitéHttpResponse> implémente RubBonCommand {private static final logger = logfactory.getLog (abstractRiBbonCommand.class); Client LBC final protégé; Contexte de rubanCommandContext protégé; protégé Zuulfallbackprovider Zuulfallbackprovider; Config IclientConfig protégé; Public AbstractRiBbonCommand (Client LBC, Context RibBonCommandContext, ZuulProperties ZuulProperties) {this ("par défaut", client, contexte, zuulProperties); } public AbstractRiBbonCommand (String CommandKey, Client LBC, Contexte RibBonCommandContext, ZuulProperties ZuulProperties) {this (CommandKey, Client, Context, ZuulProperties, Null); } public AbstractRiBbonCommand (String CommandKey, Client LBC, RibBonCommandContext Context, ZuulProperties ZuulProperties, ZuulFallbackProvider FallbackProvider) {this (CommandKey, Client, Context, ZuulProperties, FallbackProvider, NULL); } public AbstractRiBbonCommand (String CommandKey, Client LBC, RibBonCommandContext Context, ZuulProperties ZuulProperties, ZuulFallbackProvider FallbackProvider, iClientConfig Config) {this (GetSetter (Commandkey, ZuulProperties, Config), Client, Context, FallbackProvider, Config); } AbstractraitBbonCommand protégé (setter setter, client LBC, contexte RibBonCommandContext, ZuulFallbackProvider FallbackProvider, iclientConfig config) {super (seter); this.client = client; this.context = context; this.zuulFallbackProvider = FallbackProvider; this.config = config; } protégé statique HystrixCommandProperties.Setter CreateSetter (IclientConfig Config, String CommandKey, ZuulProperties ZuulProperties) {int HystrixTimeout = GetHystrixTimeout (config, CommandKey); return HystrixCommandProperties.Setter (). WithexEcutionIsolationsTrategy (ZuulProperties.GetRibBonisolationsTrategy ()). WithexEcutionTimeUtinMilliseconds (HystrixTimeout); } Protected static int gethystrixTimeout (IclientConfig config, String CommandKey) {int ribBontimeOut = getRIBBontimeout (config, CommandKey); DynamicProperTyFactory DynamicPropertyFactory = DynamicPropertyFactory.getInstance (); int defaulThystrixTimeout = dynamicpropertyfactory.getIntProperty ("Hystrix.command.default.execution.isolation.thread.timeUtinMilliseconds", 0) .get (); int commandHystrixTimeout = dynamicpropertyfactory.getIntProperty ("Hystrix.command." + CommandKey + ".execution.isolation.thread.timeUtinMilliseconds", 0) .get (); int HystrixTimeout; if (CommandHystrixTimeout> 0) {HystrixTimeout = CommandHystrixTimeout; } else if (DefaulThystrixTimeout> 0) {HystrixTimeout = DefaulThystrixTimeout; } else {HystrixTimeout = RibBonTimeOut; } if (HystrixTimeout <BiBBonTimeOut) {Logger.Warn ("Le délai de temps Hystrix de" + HystrixTimeout + "MS pour la commande" + CommandKey + "est défini inférieur à la combinaison de la lecture du ruban et du délai de connexion," + RibBontimeout + "MS."); } return HystrixTimeout; } protégé static int getRIBBontimeout (IclientConfig config, String CommandKey) {int rubbontimeout; if (config == null) {rubbontimeout = rubbonClientConfiguration.default_read_timeout + rubbonClientConfiguration.default_connect_timeout; } else {int rubBbonReadTimeout = getTimeout (config, commandKey, "readtimeout", iclientconfigkey.keys.readtimeout, rubbonClientConfiguration.default_read_timeout); int rubbonConnectTimeout = getTimeout (config, commandKey, "connectTimeout", iclientconfigkey.keys.connectTimeout, rubbonClientConfiguration.default_connect_timeout); int maxAutoreTrries = getTimeout (config, commandKey, "maxautoretries", iclientconfigkey.keys.maxautoretries, defaultClientConfigImpl.default_max_auto_retries); int maxautoretriesNextServer = getTimeout (config, commandkey, "maxautoretriesNextServer", iclientconfigkey.keys.maxautoretriesnextserver, defaultClientConfigImpl.default_max_auto_retties_next_server); RibBonTimeOut = (RubBonReadTimeout + RibBonConnectTimeout) * (MaxAutoretries + 1) * (MaxAutoretriesNextServer + 1); } return rubbontimeout; } private static int getTimeout (iclientConfig config, String CommandKey, String Property, iClientConfigKey <Integer> configKey, int defaultValue) {DynamicPropertyFactory DynamicPropertyFactory = DynamicPropertyFactory.getInstance (); return dynamicpropertyfactory.getIntProperty (CommandKey + "." + config.getNamespace () + "." + propriété, config.get (configKey, defaultValue)). get (); } @Deprecated // TODO supprimé dans 2.0.x Protected Static Setter GetSetter (Final String CommandKey, ZuulProperties ZuulProperties) {return GetSetter (CommandKey, ZuulProperties, Null); } Protected Static Setter GetSetter (Final String CommandKey, ZuulProperties ZuulProperties, IclientConfig Config) {// @formatter: off Setter CommandSetter = seter.WithGroupKe .AndCommandKey (HystrixCommandkey.factory.askey (CommandKey)); HystrixCommandProperties Final.Setter Setter = CreateSetter (config, CommandKey, ZuulProperties); if (zuulProperties.getRibBonisolationsTrategy () == ExecutionIsolationsTrategy.semaphore) {Final String name = zuulConstants.zuul_eureka + CommandKey + ".semaphore.maxsemaphores"; // Nous voulons faire défaut à la sémaphore-isolation puisque ce wraps // 2 autres commandes qui sont déjà du thread isolé DynamicIntProperty value = dynamicPropertyFactory.getInStance () .getIntProperty (Name, zuulProperties.getSemaphore (). GetMaxSemaphores ()); seter.withexEcutionIsolationsMaphoreMaxConCurrentRequests (Value.get ()); } else if (zuulproperties.getThreadpool (). IsusEseParateThreadpools ()) {final String ThreadPoolKey = zuulProperties.getThreadPool (). GetThreadPoolKeyPreFix () + CommandKey; CommandSetter.andThreadPoolKey (HystrixThreadpoolKey.factory.askey (ThreadPoolKey)); } return CommandSetter.andCommandPropertiesDefaults (setter); // @formatter: on} @Override Protected ClientHttpResponse run () lève l'exception {final requestContext context = requestContext.getCurrentContext (); RQ Request = CreateRequest (); Réponse RS; booléen reryableClient = this.client instance of AbstractloadBalancingClient && ((abstractloadBalancingClient) this.client) .islientiryableable ((contextawarerequest) request); if (reryableClient) {réponse = this.client.execute (request, config); } else {réponse = this.client.executewithloadBalancer (request, config); } context.set ("rubbonResponse", réponse); // Fermez explicitement la HTTPResponse si la commande Hystrix a chronométré à // libérer la connexion HTTP sous-jacente maintenue par la réponse. // if (this.isResponSetimedout ()) {if (réponse! = null) {réponse.close (); }} return new rubbonHttpResponse (réponse); } @Override Protected ClientHttpResponse Getfallback () {if (zuulfallbackProvider! = Null) {return getFallBackResponse (); } return super.getFallback (); } ClientHTTPRESPONSE protégé GetFallbackResponse () {if (ZuulFallbackProvider Instance de FallbackProvider) {Trowable Cause = GetFaideDExEcutionException (); cause = cause == null? getExEcutionException (): Cause; if (cause == null) {zuulfallbackprovider.fallbackResponse (); } else {return ((FallbackProvider) zuulfallbackprovider) .FallbackResponse (cause); }} return zuulfallbackprovider.fallbackResponse (); } public lbc getClient () {return client; } public rubbonCommandContext getContext () {return context; } Résumé protégé RQ CreateRequest () lève une exception;}Veuillez noter: Méthode GetribBontimeout et la méthode GethystrixTimeout, où la valeur de ces deux méthodes CommandKey est le nom de l'itinéraire. Par exemple, si nous visitons: http: // localhost: 8088 / commander-server / xxx pour accéder au service de serveur de commande, alors CommandKey est Order-Server
Selon le code source, nous définissons d'abord les paramètres de délai d'expiration de Gateway-Server:
#Global Ribbon Paramètres Ruban: ConnectTimeout: 3000 ReadTimeout: 3000Hystrix: Commande: Par défaut: Exécution: Isolement: Thread: TimeoutinMilliseconds: 3000zuul: Hôte: ConnectTimeoutMillis: 10000
Bien sûr, vous pouvez également définir séparément les paramètres de délai d'expiration du ruban pour le serveur d'ordre: order-server.ribbon.xxxx = xxx. Afin de démontrer l'effet de secours dans Zuul, j'ai défini le délai d'Hystrix un peu plus court ici. Bien sûr, il est préférable de ne pas définir le délai d'expiration par défaut d'hystrix pour être plus court que le délai d'expiration du ruban. Cette situation a été avertie de nous dans le code source.
Ensuite, nous ajoutons la méthode suivante sous Order-Server:
@GetMapping ("/ sleep / {sleeptime}") public String sleep (@Pathvariable long sleep-temps) lève InterruptedException {timeunit.seconds.sleep (SleepTime); retourner le "succès"; }2. Méthode de secours de Zuul
Nous pouvons implémenter l'interface ZuulFallbackProvider et implémenter le code:
package com.hzgj.lyrk.springcloud.gateway.server.filter; import com.google.common.collect.immutablemap; import com.google.gson.gsonbuilder; import org.springframework.cloud.netflix.zuul.filters.rote.zuulfallbackprovider; org.springframework.http.httpheaders; import org.springframework.http.httpstatus; import org.springframework.http.mediaType; import org.springframework.http.client.clienthttpruswe java.io.bytearrayinputStream; import java.io.ioexception; import java.io.inputStream; import java.time.localateTetime; import java.time.localtime; @componentpublic classbackhandler implémente zuulfallbackprovider {@Override Setting to thisRote GetRoute () {// représente les routes {@Override To ThisRoute () {// représentent les routes qui sont des routes. retour "*"; } @Override public ClientHttpResponse FallBackResponse () {return new ClientHttpResponse () {@Override public httpstatu getStaturcod () lève ioException {return httpstatus.ok; } @Override public int getrawstatuscode () lève ioException {return 200; } @Override public String getStatusTExt () lève ioException {return "ok"; } @Override public void close () {} @Override public inputStream getBody () lève ioException {String result = new gsonBuilder (). Create (). Tojson (ImmutableMap.of ("Errorcode", 500, "Content", "request failli", "time", localDateTeTeTiTe.Now ())); Renvoie un nouveau bytearrayInputStream (result.getBytes ()); } @Override public httpheaders getheaders () {httpheaders en-têtes = new httpheaders (); henders.setContentType (mediaType.Application_json); En-têtes de retour; }}; }}À l'heure actuelle, nous visitons: http: // localhost: 8088 / commander-server / sleep / 6 et obtenez les résultats suivants:
Lorsque nous visitons: http: // localhost: 8088 / order-server / sleep / 1, nous obtenons le résultat suivant:
Résumer
Ce qui précède est l'intégralité du contenu de cet article. J'espère que le contenu de cet article a une certaine valeur de référence pour l'étude ou le travail de chacun. Si vous avez des questions, vous pouvez laisser un message pour communiquer. Merci pour votre soutien à wulin.com.