Ribbon es un proyecto de código abierto lanzado por Netflix. Su función principal es proporcionar algoritmos de equilibrio de carga de software del lado del cliente para conectar los servicios de nivel medio de Netflix juntos. El componente del cliente de Ribbon proporciona una serie de elementos de configuración completos, como el tiempo de espera de conexión, el reintento, etc. En pocas palabras, es enumerar todas las máquinas detrás del equilibrador de carga (LB para abreviar) en el archivo de configuración. Ribbon lo ayudará automáticamente a conectar estas máquinas en función de ciertas reglas (como encuestas simples, conexión instantánea, etc.). También tenemos una manera muy fácil de implementar algoritmos de equilibrio de carga personalizados usando cinta.
Cuando se trata de equilibrar la carga, generalmente piensa en el equilibrio de carga en el servidor. Los productos de uso común incluyen hardware de LBS o servicios en la nube, NGINX, etc., que son todos productos familiares.
Spring Cloud proporciona cinta, lo que permite que la persona que llama tenga capacidades de equilibrio de carga. A través de una estrecha integración con Eureka, no es necesario configurar los servicios de equilibrio de carga en el clúster de servicio, lo que simplifica enormemente la arquitectura dentro del clúster de servicio.
No quiero escribir más presentaciones virtuales en detalle, de todos modos, puedo ver las presentaciones relevantes en todas partes.
Simplemente abra el código y vea cómo se implementa la cinta a través del código.
Configuración
Explicación detallada:
1. La configuración de la configuración de RibbonauToconfonfons genera una instancia de RibbonLoadBalancerclient.
Ubicación del código:
Spring-Cloud-Netflix-Core-1.3.5. Release.Jar
org.springframework.cloud.netflix.ribbon
Ribbonautoconfiguration.class
@Configuration@ConditionAlonClass ({iclient.class, Resttemplate.class, AsyncrestTemplate.class, Ribbon.class})@RibbonClients@Autoconfigurainter (name = "org.springframework.cloud.netflix.eureka.eurekaclientAutoconfiguration")@autoconfigureBeFore ({loadBalancerautoconfiguration.class, asyncloadbalancerautoconfiguration.class.class.class. RibbonauToconfiguration {// omitir @Bean @conditionalonmissingBean (LoadBalancercclient.Class) PublicBalancercclient LoadBalancercclient () {return New RibbonLoadBalancerclient (springClientFactory ()); } // omitir}Primero veamos los elementos condicionales de configuración. La configuración de RibbonauToconfiguration debe ejecutarse antes de la configuración LoadBalanceraUtoconfiguration, porque la instancia de RibbonLoadBalancerclient se utilizará en la configuración LoadBalanceraUtoconfiguration.
RibbonloadBalancerclient hereda de la interfaz LoadBalancercclient, es un cliente de equilibrio de carga y la persona que llama de la política de equilibrio de carga.
2.LoadBalancerIntercepteConfig Generación de configuración:
1). Instancia de carga BalancerInterceptor
Incluir:
Instancia de ribbonloadbalancerclient de la clase de implementación de carga de carga de carga
LoadBalancerRequestFactory: instancia
2).
Ubicación del código:
Spring-Cloud-Commons-1.2.4.Release.Jar
org.springframework.cloud.client.loadbalancer
Loadbalancerautoconfiguration.class
@Configuration@condicionalOnclass (RestTemplate.class) @ConditionAlonBean (LoadBalancerClient.Class) @enableconfigurationProperties (loadBalancerretretretretretretretrreperties.class) public class loadBalanceraUtoconfiguration LoadBalancerRequestFactory (LoadBalancerClient LoadBalancerClient) {return New LoadBalancerRequestFactory (LoadBalancerClient, Transformers); } @Configuration @conditionalonmissingclass ("org.springframework.rcry.support.rcrytemplate") estático class loadBalancerinterSceptorConfig {@Bean public loadBalancerinterceptor Ribboninterceptor (LoadBaluscleClient LoadBalancerClient, LoadBalancerRequest Factorial) LoadBalancerInterceptor (LoadBalancerClient, requestFactory); } @Bean @conditionAlonMissingBean RESTTEMPLATECUSTOMIZER RESTTEMPLATECUSTOMIZER (final loadBalancerInterceptor LoadBalancerInterceptor) {return new RestTemplateCustomizer () {@Override public void Customize (RESTTEMPLATE RESTTEPLAPLATA) {list <mindtttpRequesc.> Lista de listar <> (ARRAYLIST <> (ARRAYLIST <> (ARRAYLIST <> (ARRAYLIST <> (ARRAYLIST <> (ARRAYLIST <> (ARRAYLIST <> RestTemplate.getInterceptors ()); list.Add (LoadBalancerInterceptor); RestTemplate.SetInterceptors (lista); }}; }} // omitido}Veamos primero las condiciones de configuración:
Se requiere que debe haber una clase de RestTemplate en el entorno del proyecto.
Se requiere que debe haber una instancia de la clase de implementación de la interfaz LoadBalancerClient, es decir, el cinturón de carga de la carga generada en el paso anterior.
3. Configure todas las instancias RESTTemplate a través del RESTTempLatecustomizer creado en el paso anterior, que es establecer el interceptor de equilibrio de carga en la instancia de RESTTemplate.
@Configuration@condicionalOnclass (RestTemplate.class) @ConditionAlonBean (LoadBalancerClient.Class) @enableconfigurationProperties (LoadBalancerretretretretretretretrreperties.class) Class pública LoadBalanceraUtoconfiguration {// omitir @Bean Public SmartInitializingleton LetBalancedResttTtteMplateMplateiniatiatorer (FinalSpplateMplateiniatorator (Final LIST <RESTTEMPLATECUSTOMIZER> Customizers) {return New SmartInItializingSingLeton () {@Override public void AfterSingLetonSInstantiatiated () {for (ResttEmplate RestTeMplate: LoadBalanceraUtoconfiguration.This.restTeMplates) {para (RESTTMATECHUSTOMIRIZA: CONSEJERIZORIZADOR: CONSTÚNATIVES) Customizer.customize (RestTemplate); }}}}}}}; } // omitir @configuration @conditionalonMissingClass ("org.springframework.rcry.support.rcrytemplate") estática class loadbalancerintercepteConFig {@Bean public loadBalancerInterceptor RibbonInterceptor (LoadBalySclient LoadBalancerClient, LoadBalancerRequestRequestRequesterCactory) LoadBalancerInterceptor (LoadBalancerClient, requestFactory); } @Bean @conditionAlonMissingBean RESTTEMPLATECUSTOMIZER RESTTEMPLATECUSTOMIZER (final loadBalancerInterceptor LoadBalancerInterceptor) {return new RestTemplateCustomizer () {@Override public void Customize (RESTTEMPLATE RESTTEPLAPLATA) {list <mindtttpRequesc.> Lista de listar <> (ARRAYLIST <> (ARRAYLIST <> (ARRAYLIST <> (ARRAYLIST <> (ARRAYLIST <> (ARRAYLIST <> (ARRAYLIST <> RestTemplate.getInterceptors ()); list.Add (LoadBalancerInterceptor); RestTemplate.SetInterceptors (lista); }}; }} // omitido}RestTemplate.setInterceptors (Lista) Este lugar es el lugar donde se inyecta el interceptor de equilibrio de carga.
En realidad, se puede adivinar en este lugar que RestTemplate puede construir las solicitudes correspondientes para lograr el equilibrio de carga a través de interceptores inyectados.
También se puede ver que puede personalizar el interceptor para lograr otros fines.
4. La configuración de RibbonClientConfiguration genera instancia de zoneawareloadbalancer
Ubicación del código:
Spring-Cloud-Netflix-Core-1.3.5. Release.Jar
org.springframework.cloud.netflix.ribbon
RibbonClientConfiguration.class
@Suppleswarnings ("deprecation")@configuration@enableConfigurationProperties // El pedido es importante aquí, el último debe ser el valor predeterminado, primero debería ser opcional // ver https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653@import({okhtttpibbonconfiguration.class, RestClientRibbonconfiguration.classs, httpclientRibonconfiguration.class}) omitir @Bean @conditionalonmissingBean public iLoadBalancer RibbonLoadBalancer (iclientConfig config, serverList <verver> ServerList, ServerListFilter <verver> ServerListFilter, Irule Rule, iPing Ping, ServerListupDater ServerListUpDater) {if esta.propertiesfactory.isset (OlilOaDAscer. {return this.propertiesfactory.get (iloadbalancer.class, config, nombre); } return New ZoneaAwareloadBalancer <> (config, regla, ping, serverlist, serverlistfilter, serverlistupdater); } // omitido}Zoneawareloadbalancer hereda de la interfaz iloadbalancer, que tiene un método:
/*** Elija un servidor del equilibrador de carga. * * @param clave Un objeto que el equilibrador de carga puede usar para determinar qué servidor devolver. NULL Si * el equilibrador de carga no usa este parámetro. * @return servidor elegido */ Public Server eligeServer (clave de objeto);
Zoneawareloadbalancer es una clase de implementación de equilibrio de carga específica, y también es la clase de equilibrio de carga predeterminada. Se selecciona una determinada instancia de servicio implementando el método Choiceserver.
Intercepción y solicitud
1. Use RestTemplate para realizar varias solicitudes, como Get and Post, todas las cuales se implementan a través del método DoExecute.
Ubicación del código:
Spring-Web-4.3.12.Release.Jar
org.springframework.web.client
RestTemplate.class
La clase pública RestTemplate extiende InterceptingHttPacCessor implementa las restaciones de las restaciones {// ligeramente protegidas <T> t doExecute (URI URL, httpmethod Method, requestCallback requestCallback, ResponseExtractor <T> ResponseExtractor) arroja RestclientException {ASSSERTERTNULL (URL, "URL 'no debe ser nevado"); Afirmar.notnull (método, "'método' no debe ser nulo"); ClientHttPResponse Response = NULL; Pruebe {ClientHttpRequest Soly = CreateRequest (url, método); if (requestCallback! = null) {requestCallback.dowithRequest (solicitud); } respuesta = request.exeCute (); HandlerEponse (URL, método, respuesta); if (ResponseExtractor! = NULL) {return responsextractor.extractData (respuesta); } else {return null; }} Catch (ioException ex) {String Resource = url.ToString (); String Query = url.getrawQuery (); Resource = (Query! = NULL? Resource.substring (0, Resource.IndexOf ('?'))): Resource); Agregue nuevo ResourceAccessException ("Error de E/S en" + Method.Name () + "Solicitud para/" " + Resource +"/":" + Ex.getMessage (), ex); } finalmente {if (respuesta! = null) {respuesta.close (); }}} // omitido}Los diversos métodos de solicitud HTTP compatibles finalmente llaman al método DoExecute, que llama al método de creación para crear la instancia de solicitud, y ejecuta la solicitud para obtener el objeto de respuesta.
2. Genere la instancia de solicitud para crear fábrica
En el código anterior, se llama al método CreateRequest para crear la instancia de solicitud, que se define en la clase principal.
Primero organice la relación de herencia principal:
El método CreateRequest en realidad se define en la clase abstracta httpaccessor.
public abstract Class httpaccessor {private ClientHttpRequestFactory requestFactory = new SimpleClientHTTPRequestFactory (); public void setRequestFactory (clientHttpRequestFactory requestFactory) {Afirert.NotNull (requestFactory, "ClientHttpRequestFactory no debe ser nulo"); this.RequestFactory = requestFactory; } public ClientHttpRequestFactory getRequestFactory () {return this.RequestFactory; } Proteged ClientHttpRequest CreateRequest (URI URL, Httpmethod Method) lanza IOException {ClientHttprequest request = getRequestFactory (). CreateRequest (url, método); if (logger.isDebugeNabled ()) {logger.debug ("creado" + método.name () + "solicitud para /" " + url +" /""); } solicitud de retorno; }}Llame al método GetRequestFactory en el método CreateRequest para obtener la instancia de solicitud para crear la fábrica. De hecho, GetRequestFactory no se define en la clase actual HTTPACCessor, pero se define en la subclase InterceptingHTTPAccessor.
Public Abstract Class InterceptingHTTPAccessor extiende httpaccessor {Lista privada <ClientHttPrequestInterceptor> Interceptors = new ArrayList <ClientHttPrequestInterceptor> (); public void setInterceptors (List <ClientHTTPRequestInterceptor> interceptores) {this.interceptors = interceptores; } Lista pública <ClientHttpRequestInterceptor> getInterceptors () {return Interceptors; } @Override public ClientHTTPRequestFactory GetRequestFactory () {ClientHTTPRequestFactory Delegate = Super.GetRequestFactory (); if (! collectionUtils.isEmpty (getInterceptors ())) {return new InterceptingClientHttPrequestFactory (delegate, getInterceptors ()); } else {return delegado; }}}Hice una pequeña acción aquí. Primero, creé y obtuve la fábrica SimpleClientHttpRequestFactory a través de la clase HTTPACSORSER. Esta fábrica crea principalmente instancias de solicitud básicas cuando no hay interceptor.
En segundo lugar, cuando hay inyección de interceptor, cree la fábrica InterceptingClienthttpRequestFactory. Esta fábrica crea una instancia de solicitud con un interceptor. Debido a que se inyecta el interceptor de equilibrio de carga, se crea a partir de la fábrica InterceptingClienthttpRequestFactory.
3. Cree una instancia de solicitud a través de la fábrica
Al crear una instancia, depende del método de creatrutest de la fábrica.
clase pública InterceptingClientHttpRequestFactory extiende AbstractClientHttpRequestFactoryWrapper {Lista final privada <ClientHTPRequestInterceptor> interceptores; Public InterceptingClientHTTPRequestFactory (clientHttpRequestFactory requestFactory, list <ClientHttpRequestInterceptor> interceptores) {super (requestFactory); this.interceptors = (Interceptors! = NULL? Interceptores: colecciones. <ClientHTTPRequestInterceptor> vacilist ()); } @Override ClientHttpRequest CreateRequest (uri uri, httpmethod httpmethod, clienthttprequestFactory requestFactory) {return new InterceptingClienthttprequest (requestFactory, this.interceptores, uri, httpmethod); }}Significa una nueva instancia de InterceptingClientHttpRequest e inyecte la fábrica de creación de instancias de Interceptor e Instance Basic.
4. Solicite la instancia que llame al método de intercepción de la intersección de equilibrio de carga inyectado en la etapa de configuración
Se puede ver desde el paso 1 que después de crear la instancia de solicitud, la solicitud se ejecuta ejecutando el método de ejecución de la instancia de solicitud.
ClientHttpRequest request = CreateRequest (url, método); if (requestCallback! = Null) {requestCallback.dowithRequest (request);} respuesta = request.exeCute ();La instancia de solicitud real es interceptar a ClienthttpRequest, y Execute está realmente en su clase principal.
Ubicación de la definición de clase:
Spring-Web-4.3.12.Release.Jar
org.springframework.http.client
Interceptingclienthttprequest.class
Eche un vistazo a su relación de herencia.
El método ejecutivo implementado por la subclase en realidad se llama en el método de ejecución.
Public Abstract Class AbstractClientHTTPRequest implementa ClientHttpRequest {Private Final Httpheaders encabezados = new httpheaders (); privado booleano ejecutado = falso; @Override public Final HttPheaders Getheaders () {return (this.Executed? Httpheaders.Readonlyhtttpheaders (this.headers): this.headers); } @Override public Final OutputStream getBody () lanza IOException {ASSERTNOTEXECUTE (); regresar getBodyInternal (this.headers); } @Override public final ClientHttPResponse Execute () lanza IOException {ASSERTNOTEXECUTE (); ClientHttPResponse result = ExecUteInternal (this.headers); this.Executed = True; resultado de retorno; } protegido void afirmarnotExecuted () {afirmo.state (! this.Executed, "ClientHttpRequest ya ejecutado"); } protegido de salida abstracta GetBodyInternal (encabezados httpheaders) lanza ioException; Resumen de clienthttTTTTTTTRESPONDE protegido Ejecutal (encabezados httpheaders) lanza ioexception;}De hecho, es el método ejecutivo de la clase InterceptingClienthttpRequest. En el cual, se llama a la ejecución del ejecutor interceptador de medición de medidas. Pase el nivel y determine que si se ha inyectado un interceptor, se llama al método de intercepción del interceptor.
El interceptor aquí es en realidad la instancia de interceptor de carga de carga de carga inyectada en la instancia de RESTTemplate durante la etapa de configuración. Puede consultar el paso 2 de la etapa de configuración anterior.
Class InterceptingClientHttPRequest extiende AbstractBufferingClientHttprequest {// omitir @Override Proteged final CLIENTHTTPSONSPONDE EjecuteInternal (httpheaders encabezados, byte [] bufferedOutput) lanza IOException {intereptación de requisito de requisito. return requitExEcution.Execute (this, BufferedOutput); } Clase privada InterceptingRequestExecution implementa ClientHttpRequestExecution {Private Final Iterator <ClientHttpRequestInterceptor> iterator; public interceptingRequestExecution () {this.iterator = interceptors.iterator (); } @Override public ClientHttPResponse Execute (HttpRequest Soly, Byte [] Body) arroja ioexception {if (this.iterator.hasnext ()) {clienthttprequestinterceptor nextInterceptor = this.iterator.next (); regresar NextInterceptor.intercept (solicitud, cuerpo, esto); } else {clientHttpRequest delegate = requestFactory.CreaterEct (request.getUri (), request.getMethod ()); for (map.entry <string, list <String>> Entry: request.getheaders (). EntrySet ()) {list <String> valores = entry.getValue (); for (valor de cadena: valores) {delegate.getheaders (). add (entry.getKey (), valor); }} if (body.length> 0) {streamUtils.copy (body, delegate.getBody ()); } return delegate.execute (); }}}}5. El interceptor de equilibrio de carga llama al cliente de equilibrio de carga
En el método Intercept de la clase de la clase del interceptor de equilibrio de carga LoadBalancerInterceptor, se llama a la clase de implementación del cliente de equilibrio de carga de carga.
Public Class LoadBalancerInterceptor implementa ClientHTTPRequestInterceptor {private LoadBalancercclient LoadBalancer; LoadBalancerRequestFactory privado SolicTory; Public LoadBalancerInterceptor (LoadBalancerClient LoadBalancer, LoadBalancerRequestFactory RequestFactory) {this.loadBalancer = LoadBalancer; this.RequestFactory = requestFactory; } Public LoadBalancerInterceptor (LoadBalancerClient LoadBalancer) {// para compatibilidad hacia atrás esto (LoadBalancer, NewBalancerRequestFactory (LoadBalancer)); } @Override public ClientHttPSponse Intercept (solicitud final de httprequest, cuerpo de byte final [], ejecución final de clienthttprequestexecution) lanza ioexception {final uri originaluri = request.getUri (); String ServiceName = originaluri.gethost (); Afirmar.state (ServiceName! = NULL, "Solicitar URI no contiene un nombre de host válido:" + OriginalUri); devuelva this.loadBalancer.execute (ServiceName, requestFactory.CreaterEct (solicitud, cuerpo, ejecución)); }}En el paso 1 de la fase de configuración, puede ver que la clase de implementación es RibbonLoadBalancerClient.
6. El cliente de equilibrio de carga llama a la política de equilibrio de carga para seleccionar la instancia del servicio de destino e iniciar una solicitud
En el primer método de ejecución y el método de getserver de RibbonLoadBalancerClient, podemos ver que, de hecho, el método Choiceserver de la clase de implementación de ILOAdBalancer se selecciona y se entrega al siguiente objeto de solicitud para iniciar una solicitud.
La clase de implementación de equilibrio de carga aquí es la instancia de equilibrio de carga del área de ZoneaAwareloadbalancer de manera predeterminada de forma predeterminada, y selecciona un servicio internamente a través de la política de equilibrio.
La creación de ZoneaAwareloadbalancer se puede encontrar en el paso 4 de la fase de configuración.
Class public RibbonLoadBalancercclient Implementa LoadBalancercclient {@Override public <T> t Ejecutar (String ServiceId, LoadBalancerRequest <T> Solicitud) lanza IOException {iloadbalancer LoadBalancer = GetLoadBalancer (ServiceId); Servidor servidor = getserver (loadbalancer); if (server == null) {lanzar nueva ilegalstateException ("No hay instancias disponibles para" + ServiceId); } RibBonserver RibBonserver = new RibBonserver (ServiceId, Server, Issecure (Server, ServiceId), ServerIntrospector (ServiceId) .getMetadata (servidor)); return ejecute (servicioid, ribbonserver, solicitud); } @Override public <T> t Execute (String ServiceId, ServiceInstance ServiceInstance, LoadBalancerRequest <T> solicitud) lanza IOException {Server Server = null; if (ServiceInstance instanceOf RibBonserver) {server = ((RibBonserver) ServiceInstance) .getServer (); } if (server == null) {tirar nueva ilegalStateException ("No hay instancias disponibles para" + ServiceId); } RibbonLoadBalancercontext context = this.ClientFactory .getLoadBalancercontext (ServiceId); RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder (contexto, servidor); intente {t returnval = request.apply (servicioInstance); statsRecorder.RecordStats (returnval); Return Returnval; } // Catch ioException y Rethrow para que RestTemplate se comporte correctamente (ioexception ex) {statsRecorder.RecordStats (ex); tirar ex; } catch (excepción ex) {statsRecorder.RecordStats (ex); ReflectionUtils.rethrowrunteException (ex); } return null; } // servidor ligeramente protegido getServer (iloadbalancer loadbalancer) {if (loadbalancer == null) {return null; } return LoadBalancer.ChoosServer ("predeterminado"); // TODO: mejor manejo de la clave} protegido iloadbalancer getLoadBalancer (String ServiceId) {return this.clientFactory.getLoadBalancer (ServiceId); } public static Class Ribbonserver implementa ServiceInstance {private final String ServiceId; servidor final privado servidor; Secure de booleano final privado; mapa privado <string, string> metadata; public RibBonserver (String ServiceId, Server Server) {this (ServiceId, Server, False, Collections. <String, String> shateMap ()); } public RibBonserver (String ServiceId, Server Server, Boolean Secure, Map <String, String> Metadata) {this.serviceId = ServiceId; this.server = servidor; this.secure = seguro; this.metadata = metadata; } // omitido}}Después de terminar el código, resumamos.
Al usar RESTTemplate para solicitar otros servicios, la instancia de solicitud HTTP regular se usa internamente para enviar solicitudes.
Después de agregar la anotación @loanbalanced a RestTemplate, en realidad, a través de la configuración, se inyecta un interceptor de equilibrio de carga en RestTemplate, lo que permite que el equilibrador de carga elija el servicio apropiado de acuerdo con su política correspondiente antes de enviar una solicitud.
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.