Ribbon est un projet open source publié par Netflix. Sa fonction principale est de fournir des algorithmes d'équilibrage de la charge du logiciel côté client pour connecter ensemble les services de niveau intermédiaire de Netflix. Le composant client du ruban fournit une série d'éléments de configuration complets tels que le délai de connexion, la réessayer, etc. Le ruban vous aidera automatiquement à connecter ces machines en fonction de certaines règles (telles que des sondages simples, une connexion instantanée, etc.). Nous avons également un moyen très simple d'implémenter des algorithmes d'équilibrage de charge personnalisés à l'aide du ruban.
En ce qui concerne l'équilibrage de charge, vous pensez généralement à l'équilibrage de la charge sur le serveur. Les produits couramment utilisés incluent les services de matériel ou de cloud LBS, Nginx, etc., qui sont tous des produits familiers.
Spring Cloud fournit un ruban, qui permet à l'appelant de service d'avoir des capacités d'équilibrage de charge. Grâce à une intégration étroite avec Eureka, il n'est pas nécessaire de configurer des services d'équilibrage de charge dans le cluster de services, ce qui simplifie considérablement l'architecture dans le cluster de service.
Je ne veux pas écrire plus de présentations virtuelles en détail, de toute façon, je peux voir les présentations pertinentes partout.
Ouvrez simplement le code et voyez comment le ruban est implémenté via le code.
Configuration
Explication détaillée:
1. Configuration de rubanAutoConfiguration génère une instance RibBonLoDBananClient.
Emplacement du code:
Spring-Cloud-netflix-core-1.3.5.release.jar
org.springframework.cloud.netflix.ribbon
Rubbonautoconfiguration.class
@ Configuration @ conditionalOnClass ({iclient.class, restTEmplate.class, asyncrestTemplate.class, rubbon.class}) @ rubbonClients @ autoconfigure "org.springframework.cloud.netflix.eureka.eurekaclientAutoConfiguration") @ autoconfigureBefore ({loadbalanceratoconfiguration.class, asyncloadBalerAutoConfiguration.class}) @ operConfigurationProperties (rubbonAgerloadProperties.Class) RibbonAutoConfiguration {// omettre @Bean @conditionalonMissingBean (LoadBalancerClient.class) Public LoadBalancer LoadBalancerClient () {return new RibBonLoDalancerClient (SpringClientFactory ()); } // omettre}Examinons d'abord les éléments conditionnels de la configuration. La configuration du rubanAutoConfiguration doit être exécutée avant la configuration de la configuration LoadBalanceraToCon, car l'instance RibBonLoadBalancerClient sera utilisée dans la configuration de la configuration LoadBalanceraToConfiguration.
RibbonloadBalancerClient hérite de l'interface LoadBalancerClient, est un client d'équilibrage de charge et l'appelant de la politique d'équilibrage de charge.
2.LoadBalancerInterceptorConfig Génération de configuration:
1). Instance de chargement BalancerInterceptor
Inclure:
RibbonloadBalancerLIENT Instance de Classe d'implémentation LoadBalancerClient
LoadBalancerRequestFactory: Instance
2).
Emplacement du code:
Spring-Cloud-Commons-1.2.4.release.jar
org.springframework.cloud.client.loadBalancer
Loadbalanceratoconfiguration.class
@ Configuration @ conditionalOnClass (RestTemplate.class) @conditionalonbean (loadbalancerclient.class) @enableConfigurationProperties (LoadBalancerRetStProperties.class) Public Class LoadBalAutoConfiguration {// omit @bean loadBalancerRequestFactory (LoadBalancerClient LoadBalancerClient) {return new LoadBalancerRequestFactory (LoadBalancerClient, Transformers); } @Configuration @conditionalonMissingClass ("org.springframework.retry.support.retryTemplate") class static loadBalancerInterceptorConfig {@bean public LoadBalancerInterceptor rubbonInterceptor (chargé LoadBalancerInterceptor (LoadBalancerClient, RequestFactory); } @Bean @conditionalonMissingBean public restTemplateCustomzer resttemplateCustomzer (final loshBalancerInterceptor loshBalancerInterceptor) {return new resttemplatecustomzer () {@Override public void personnaliser (resttemplate resttemplate) restTemplate.getInterceptors ()); list.add (loadBalancerInterceptor); restTemplate.setInterceptors (list); }}; }} // omis}Examinons d'abord les conditions de configuration:
Il est nécessaire qu'il y ait une classe Restyplate dans l'environnement du projet.
Il est nécessaire qu'il y ait une instance de la classe d'implémentation de l'interface LoadBalancerClient, c'est-à-dire le rubanloadBalancerClient généré à l'étape précédente.
3. Configurez toutes les instances RestTemplate via le RestTemplateCustomage créé à l'étape ci-dessus, qui est de définir l'intercepteur d'équilibrage de charge sur l'instance RestTemplate.
@ Configuration @ conditionalOnClass (restLEmplate.class) @conditionalonbean (loadBalancerClient.class) @enableConfigurationProperties (LoadBalanceRetStProperties.class) Public Class LoadBalianceRautOnfiguration {// omit @Bean public SmartInitializingSingLeTOr Liste <RESTTEMPLATECUSUMIZER> CUSTOLERS) {Return New SmartInitializingSingleton () {@Override public void AfterSingletonsInstantiated () {for (RestTemplate RestTemplate: LoadBalanceraToConfiguration.This.resttemplate) {for (restemplatempizizer cumestom: Customessers) {Customessizer.Customer (RESTTEMPOSIZER CUSTOMIZER: CUSTOMIZERS) {CUSTOMIZER.COSOSIMIE); }}}}}}}; } // omettre @configuration @conditionalonMissingClass ("org.springframework.retry.support.retryTemplate") class static loshBalancerInterceptorConfig {@bean Public LoadBalancerInterceptor RibBonInterceptor (chargefactory) {ReturnBerlient, chargeBalancerReQuestFactory) LoadBalancerInterceptor (LoadBalancerClient, RequestFactory); } @Bean @conditionalonMissingBean public restTemplateCustomzer resttemplateCustomzer (final loshBalancerInterceptor loshBalancerInterceptor) {return new resttemplatecustomzer () {@Override public void personnaliser (resttemplate resttemplate) restTemplate.getInterceptors ()); list.add (loadBalancerInterceptor); restTemplate.setInterceptors (list); }}; }} // omis}restTemplate.setInterceptors (list) Cet endroit est l'endroit où l'intercepteur d'équilibrage de charge est injecté.
Il peut en fait être deviné à partir de cet endroit que Rest -mplate peut créer des demandes correspondantes pour atteindre l'équilibrage de charge via des intercepteurs injectés.
On peut également voir que vous pouvez personnaliser l'intercepteur pour atteindre d'autres fins.
4. Configuration RibBonClientConfiguration génère une instance ZoneAwareLoadBalancer
Emplacement du code:
Spring-Cloud-netflix-core-1.3.5.release.jar
org.springframework.cloud.netflix.ribbon
RubanClientConfiguration.class
@SuppressWarnings ("Deprécation") @ configuration @ perteConfigurationProperties // L'ordre est important ici, le dernier devrait être la valeur par défaut, d'abord devrait être facultatif // voir https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653@import({okhttpribbonconfiguration.class, restclientribbonconfiguration.class, httclientconconfiguration.class}) // omettre @bean @conditionalonMissingBean public iloadbalancer rubbonloadBalancer (IclientConfig Config, serverlist <Server> ServerList, serverListFilter <Server> ServerListFilter, Irule Rule, iping Ping.SetLepDater ServerListDater) {if (this.propertiesfactory.isset this.propertiesfactory.get (iloadbalancer.class, config, nom); } return new ZoneAwareLoadBalancer <> (config, règle, ping, serverlist, serverlistFilter, serverListUpDater); } // omis}Zoneawareloadbalancer hérite de l'interface iloadbalancer, qui a une méthode:
/ ** * Choisissez un serveur auprès de Balancer Balancer. * * @param clé Un objet que l'équilibreur de charge peut utiliser pour déterminer le serveur à retourner. null si * l'équilibreur de charge n'utilise pas ce paramètre. * @return server choisi * / public server ChooseServer (clé d'objet);
ZoneAwareLoadbalancer est une classe d'implémentation d'équilibrage de charge spécifique, et c'est également la classe d'équilibrage de charge par défaut. Une certaine instance de service est sélectionnée en implémentant la méthode ChoiceServer.
Intercepter et demander
1. Utilisez RestTemplate pour effectuer diverses demandes telles que GET et POST, qui sont toutes implémentées via la méthode DOEXcute.
Emplacement du code:
printemps-web-4.3.12.release.jar
org.springframework.web.client
Rostemplate.class
Classe publique RestTemplate étend IntercepingHttpaccessor implémente les restaurations {// légèrement protégées <T> T DOEXECUTE (URL URL, HttpMethod Method, requestCallback requestCallback, réponsextractor <t> réponsextractor) lance RestClientException {assert.notnull (url, "'url' ne doit pas être null"); Assert.notnull (méthode, "'méthode' ne doit pas être nul"); ClientHttpResponse Response = null; essayez {clientHttpRequest request = CreateResest (URL, méthode); if (requestCallback! = null) {requestCallback.DowithRequest (request); } réponse = request.execute (); HandlerResponse (URL, méthode, réponse); if (réponsextractor! = null) {return réponsextractor.extractData (réponse); } else {return null; }} catch (ioException ex) {String Resource = url.ToString (); String Query = url.getRawquery (); Ressource = (query! = null? Resource.Substring (0, Resource.Indexof ('?'))): Resource); lancer une nouvelle ressourceAccessException ("Erreur d'E / S sur" + méthode.name () + "Demande pour /" "+ ressource +" / ":" + ex.getMessage (), ex); } enfin {if (réponse! = null) {réponse.close (); }}} // omis}Les différentes méthodes de demande HTTP prises en charge appellent finalement la méthode DOEXcute, qui appelle la méthode de création pour créer l'instance de demande et exécute la demande pour obtenir l'objet de réponse.
2. Générer une instance de demande pour créer une usine
Dans le code précédent, la méthode CreateRequest est appelée pour créer l'instance de demande, qui est définie dans la classe parent.
Organisez d'abord la principale relation d'héritage:
La méthode CreateRequest est en fait définie dans la classe abstraite HTTPACCESSOR.
Classe de résumé public httpaccessor {private clientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory (); public void setRequestFactory (clientHttpRequestFactory requestFactory) {assert.notnull (requestFactory, "ClientHttpRequestFactory ne doit pas être nul"); this.requestFactory = requestFactory; } public ClientHttpRequestFactory getRequestFactory () {return this.requestFactory; } ClientHttpRequest CreaterEQuest (URL URL, méthode httpMethod) lève IOException {ClientHttpRequest Request = GetRequestFactory (). CreaterEQuest (URL, méthode); if (logger.isdebugeNabled ()) {logger.debug ("créé" + méthode.name () + "demande pour /" "+ url +" / ""); } requête de retour; }}Appelez la méthode GetRequestFactory dans la méthode CreateRequest pour obtenir l'instance de demande pour créer l'usine. En fait, GetRequestFactory n'est pas défini dans la classe HTTPACCESSOR actuelle, mais est défini dans la sous-classe IntercepingHTTPACCESSOR.
Classe de résumé public IntercepingHttpaccessor étend httpaccessor {private list <ClientHttpRequestInterceptor> interceptors = new ArrayList <ClientHttpRequestInterceptor> (); public void setInterceptors (list <clientHttpRequestInterceptor> intercepteurs) {this.interceptors = intercepteurs; } public list <ClientHttpRequestInterceptor> getInterceptors () {return interceptors; } @Override public ClientHttpRequestFactory getRequestFactory () {clientHttpRequestFactory Delegate = super.getRequestFactory (); if (! CollectionUtils.Isempty (getInterceptors ())) {return new IntercepingClientHttpRequestFactory (Delegate, getInterceptors ()); } else {return Delegate; }}}J'ai fait une petite action ici. Tout d'abord, j'ai créé et obtenu l'usine SimpleClientHttpRequestFactory via la classe httpaccessor. Cette usine crée principalement des instances de demande de base lorsqu'il n'y a pas d'intercepteur.
Deuxièmement, lorsqu'il y a l'injection d'interceptor, créez l'usine IntercedingClientHttpRequestFactory. Cette usine crée une instance de demande avec un intercepteur. Étant donné que l'intercepteur d'équilibrage de charge est injecté, il est créé à partir de l'usine IntercedingClientHttpRequestFactory.
3. Créez une instance de demande via l'usine
Lors de la création d'une instance, cela dépend de la méthode CreaterEquest de l'usine.
classe publique IntercepingClientHttpRequestFactory étend AbstractClientHttpRequestFactoryWrapper {private final list <ClientHttpRequestInterceptor> Interceptors; public intercepingClientHttpRequestFactory (ClientHttpRequestFactory requestFactory, list <ClientHttpRequestInterceptor> Interceptors) {super (requestFactory); this.Interceptors = (intercepteurs! = null? Interceptors: Collections. <ClientHttpRequestInterceptor> videList ()); } @Override Protected ClientHttpRequest CreaterEQuest (URI URI, HttpMethod HttpMethod, ClientHttpRequestFactory RequestFactory) {return new IntercepingClientHttpRequest (requestfactory, this.interceptors, uri, httpmethod); }}Cela signifie une nouvelle instance IntercedingClientHttpRequest et injecter l'intercepteur et l'usine de création d'instance de demande de base.
4. Demandez à l'instance d'appeler la méthode d'interception de l'équilibrage de charge Intercept injecté à l'étape de configuration
On peut voir à partir de l'étape 1 qu'après avoir créé l'instance de demande, la demande est exécutée en exécutant la méthode d'exécution de l'instance de demande.
ClientHttpRequest request = CreateRequest (url, méthode); if (requestCallback! = Null) {requestCallback.dowithRequest (request);} réponse = request.execute ();L'instance de demande réelle est d'intercepterClientHttpRequest, et EXECUTE est en fait dans sa classe parent.
Emplacement de définition de classe:
printemps-web-4.3.12.release.jar
org.springframework.http.client
Intercepingclienthttprequest.class
Jetez un œil à leur relation d'héritage.
La méthode ExecuteInternal implémentée par la sous-classe est en fait appelée dans la méthode Execute.
Classe abstraite publique AbstractClientHttpRequest implémente ClientHttpRequest {private final httpheaders en-têtes = new httpheaders (); Boolean privé exécuté = false; @Override public final httpheaders getheaders () {return (this.exécuté? Httpheaders.readonlyhttpheaders (this.heders): this.heders); } @Override public Final OutputStream getBody () lève ioException {assertNotexEcuted (); return getBodyInternal (this.headers); } @Override public final ClientHttpResponse execute () lève ioException {ASSERTNOTEXECUTED (); ClientHTTPResponse Result = ExecuteInternal (this.headers); this.exécuté = true; Résultat de retour; } protégé void ASSERTNOTEXECUTED () {ASSERT.STATE (! } Résumé Protected OutputStream GetBodyInternal (en-têtes httpheaders) lève ioException; Résumé protégé ClientHttpResponse ExecuteInternal (en-têtes httpheaders) lève IOException;}En fait, il s'agit de la méthode EXECUTE INCERNIERE de la classe IntercedingClientHttpRequest. Dans lequel, l'exécution de l'exécuteur exécuteur intercevant la question de l'Exécution est appelée. Passez le niveau et déterminez que si un intercepteur a été injecté, la méthode d'interception de l'intercepteur est appelée.
L'intercepteur ici est en fait l'instance de chargement de chargement chargebalancerInterceptor injectée dans l'instance RestTemplate pendant l'étape de configuration. Vous pouvez vous référer à l'étape 2 de l'étape de configuration ci-dessus.
Classe IntercepingClientHttpRequest étend AbstractBufferingClientHttpRequest {// omettre @Override Protected Final ClientHttpResponse ExecuteInternal (Httpheaders en-têtes, Byte [] Bufferedoutput) lance ioException {intercepreequequestEcution requestExecution = new interceptured return requestExecution.exécute (ceci, tampondOutput); } classe privée IntercetingReprequestExecution implémente ClientHttpRequestExECUTUTION {private final iterator <ClientHttpRequestInterceptor> iterator; public intercetingrequequestExecution () {this.iterator = interceptors.iterator (); } @Override public ClientHTTPResponse EXECUTE (HttpRequest Request, Byte [] Body) lève IoException {if (this.iterator.hasnext ()) {clientHttpRequestInterceptor nextInterceptor = this.iterator.next (); return nextInterceptor.Intercept (demande, corps, this); } else {clientHttpRequest Delegate = requestFactory.CreaReQuest (request.getUri (), request.getMethod ()); for (map.entry <string, list <string>> entrée: request.getheaders (). entryset ()) {list <string> valeurs = entry.getValue (); for (String Value: Values) {Delegate.GetHeaders (). Add (entrée.GetKey (), valeur); }} if (body.length> 0) {streamUtils.copy (body, delegate.getBody ()); } return delegate.execute (); }}}}5. L'intercepteur d'équilibrage de charge appelle le client d'équilibrage de chargement
Dans la méthode d'interception de la classe d'interceptrice d'équilibrage de charge LoadBalancerInterceptor Classe, la méthode d'exécution de la classe d'implémentation LoadBalancerClient de charge d'équilibrage de charge est appelée.
classe publique LoadBalancerInterceptor implémente ClientHttpRequestInterceptor {Private LoadBalancerClient LoadBalancer; Private LoadBalancerRequestFactory requestFactory; Public LoadBalancerInterceptor (LoadBalancerClient LoadBalancer, LoadBalancerRequestFactory RequestFactory) {this.loadBalancer = LoadBalancer; this.requestFactory = requestFactory; } public LoadBalancerInterceptor (LoadBalancerClient LoadBalancer) {// Pour une compatibilité en arrière this (LoadBalancer, New LoadBalancerRequestFactory (LoadBalancer)); } @Override public ClientHTTPResponse Intercept (Final HttpRequest Request, Final Byte [] Body, Final ClientHttpRequestExecution Execution) lève IoException {final uri originalUri = request.geturi (); String ServiceName = originalUri.Gethost (); Assert.state (ServiceName! = Null, "La demande d'Uri ne contient pas de nom d'hôte valide:" + originalUri); Renvoie this.loadBalancer.Execute (ServiceName, requestFactory.CreaterEQuest (demande, corps, exécution)); }}À l'étape 1 de la phase de configuration, vous pouvez voir que la classe d'implémentation est RibBonLoDBalancerClient.
6. Le client d'équilibrage de charge appelle la politique d'équilibrage de chargement pour sélectionner l'instance de service cible et lancer une demande
Dans la première méthode d'exécution et la méthode GetServer de RibBonLoadBalancerClient, nous pouvons voir qu'en fait, la méthode ChoiceServer de la classe d'implémentation Iloadbalancer est sélectionnée et remise à l'objet de demande suivant pour initier une demande.
La classe d'implémentation d'équilibrage de charge ici est l'instance d'équilibrage de chargement ZoneAwareLareloadBalancer par défaut par défaut, et il sélectionne un service en interne via la politique d'équilibrage.
La création de ZoneAwareLoadbalancer peut être trouvée à l'étape 4 de la phase de configuration.
classe publique RibbonLoDalBanCerLIENT implémente LoadBalancerClient {@Override public <T> T Execute (String ServiceId, LoadBalancerRequest <T> Request) lance ioException {iloadbalancer LoadBalancer = GetLoadBalancer (serviceId); Server server = getServer (loadBalancer); if (server == null) {lancez new illégalStateException ("Aucune instance disponible pour" + serviceId); } RibBonServer RibBonServer = new RibBonServer (ServiceId, Server, ISSECure (Server, ServiceId), ServerIntrospector (ServiceId) .GetMetAdata (serveur)); return execute (ServiceId, RibBonServer, demande); } @Override public <T> T EXECUTE (String ServiceId, ServiceInstance ServiceInstance, LoadBalancerRequest <T> Request) lance ioException {Server Server = NULL; if (ServiceInstance instanceof rubbonServer) {server = ((RibBonServer) ServiceInstance) .getServer (); } if (server == null) {lancez new illégalStateException ("Aucune instance disponible pour" + serviceId); } RibbonloadBalancerContext context = this.clientfactory .getloadBalancerConText (serviceId); RIBBONSTATRECORDER STATSRECORDEL = NOUVEAU RIBBONSTATRESCORD (Context, Server); try {t returnVal = request.Apply (serviceInstance); statSRecorder.RecordStats (returnVal); retour returnVal; } // Catch ioException et rethrow So RestTemplate se comporte correctement (ioException ex) {statsRecorder.RecordStats (ex); jeter ex; } catch (exception ex) {statSRecorder.RecordStats (ex); ReflectionUtils.rethrowruntimeException (ex); } return null; } // Server légèrement protégé GetServer (iloadbalancer loadbalancer) {if (loadBalancer == null) {return null; } return loadbalancer.chooseServer ("par défaut"); // TODO: meilleure gestion de la clé} Iloadbalancer protégé getloadBalancer (String ServiceId) {return this.clientfactory.getloadBalancer (serviceId); } Classe statique publique RibBonServer implémente ServiceInstance {private final String ServiceId; serveur final privé du serveur; Final Boolean Sécurisé privé; Carte privée <chaîne, string> métadonnées; public rubbonServer (String ServiceId, serveur serveur) {this (serviceId, server, false, collections. <String, String> videMap ()); } public rubbonServer (String ServiceId, serveur serveur, booléen sécurisé, map <string, string> métadonnées) {this.serviceId = serviceId; this.server = server; this.secure = sécurisé; this.metadata = métadonnées; } // omis}}Après avoir terminé le code, résumons-le.
Lorsque vous utilisez RestTemplate pour demander d'autres services, l'instance de demande HTTP ordinaire est utilisée en interne pour envoyer des demandes.
Après avoir ajouté l'annotation @Loanbalanced à Restemplate, en fait, via la configuration, un intercepteur d'équilibrage de charge est injecté dans RestTemplate, permettant à l'équilibreur de charge de choisir le service approprié en fonction de sa politique correspondante avant d'envoyer une demande.
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.