A fita é um projeto de código aberto lançado pela Netflix. Sua principal função é fornecer algoritmos de balanceamento de carga do software do lado do cliente para conectar os serviços de camada média da Netflix. O componente do cliente da faixa de opções fornece uma série de itens de configuração completos, como tempo limite de conexão, repetição, etc. Simplificando, é para listar todas as máquinas por trás do balanceador de carga (lb para curta) no arquivo de configuração. A fita ajudará você a conectar automaticamente essas máquinas com base em determinadas regras (como pesquisa simples, conexão instantânea etc.). Também temos uma maneira muito fácil de implementar algoritmos de balanceamento de carga personalizados usando fita.
Quando se trata de balanceamento de carga, você geralmente pensa no balanceamento de carga no servidor. Os produtos comumente usados incluem serviços de hardware ou nuvem da LBS, NGINX, etc., que são todos produtos familiares.
A Spring Cloud fornece fita, que permite que o chamador de serviço tenha recursos de balanceamento de carga. Através da estreita integração com Eureka, não há necessidade de configurar serviços de balanceamento de carga no cluster de serviço, o que simplifica bastante a arquitetura no cluster de serviço.
De qualquer forma, não quero escrever mais introduções virtuais, posso ver as apresentações relevantes em todos os lugares.
Basta abrir o código e ver como a fita é implementada através do código.
Configuração
Explicação detalhada:
1. A configuração do RibBonAutoconfiguration gera uma instância do RibBonLloadBalancerclient.
Localização do código:
Spring-Cloud-Netflix-core-1.3.5.release.jar
org.springframework.cloud.netflix.ribbon
Ribnautoconfiguration.class
@Configuration@condicionalOnClass ({iclient.class, Resttemplate.class, AsyncrestTemplate.class, ribbon.class})@ribbonlients@autoconfigureefter (name = "Org.springframework.cloud.netflix.eureka.eurekaclientautoconfiguration")@auto -figureefore ({loadBalanceRoutoconfiguration.cllSlclls) PublicLoadBalancerautoconFiguration.class})@EnableConFiGrativeRies Ribnautoconfiguration {// omit @bean @conditionalonMissingBean (loadBalancerclient.class) public LoadBalncerclient loadBalancerclient () {return novo ribbonloadBalncerclient (SpringClientFactory ()); } // omita}Vamos primeiro olhar para os itens condicionais de configuração. A configuração do RibBonautoconfiguration deve ser executada antes da configuração do LoadBalancerautoconfiguration, porque a instância do RibBonLloadBalancerclient será usada na configuração do LoadBalancerautoconfiguration.
O RibbonLloadBalancerclient herda da interface LoadBalancerclient, é um cliente de balanceamento de carga e o chamador da política de balanceamento de carga.
2.LoadBalancerInterceptConfig Geração de configuração:
1). Carregue a instância do BalancerIntercept
Incluir:
Instância do RibbonloadBalancerclient da classe de implementação do LoadBalncerclient
LoadBalancerRequestFactory: Instância
2) .RestTemplate personalizado RestTemplateCustomizer instância
Localização do código:
Spring-Cloud-Commons-louns -.2.4.release.jar
org.springframework.cloud.client.loadBalancer
LoadBalancerautoconfiguration.class
@Configuration@condicionalOnClass (RestTemplate.class) @ConditionalonBean (LoadBalancerclient.class) @enableConfigurationProperties (LoadBalancerRetriaProperties.class) LoadBalancerRequestFactory (LoadBalancerclient loadBalancerclient) {retorna new loadBalancerRequestFactory (LoadBalancerclient, Transformers); } @Configuration @conditionalonMissingClass ("org.springframework.retry.support.retrytemplate") classe estática loadBalancerIntercetorConfig {@BeRaBean lotbalancerInterceptor RiboninterCorre (LoadBalâmboryClient LoadCercient, LoadBalancerReptReptReCtorMeRQUERREQUEMOR (LoadBalerCortory) LoadBalancerIntercept (LoadBalancerclient, RequestFactory); } @Bean @ConditionalonMissingBean ResttemplateCustomizer RestTemplateCustomizer (Final LoadBalancerIntercept LoadBalancerInterceptor) {Return new RestTemplateCustomizer () {@Override public Void Customize (Resttemplate Resttemplate) {listhttttpTPRPER (@Override público RestTemplate.getInterceptores ()); list.add (loadBalancerInterceptor); RestTemplate.SetInterceptores (Lista); }}; }} // omitido}Vejamos as condições de configuração primeiro:
É necessário que deve haver uma classe Resttemplate no ambiente do projeto.
É necessário que deve haver uma instância da classe de implementação da interface LoadBalncerclient, ou seja, o RibbonLoadBalancerclient gerado na etapa anterior.
3. Configure todas as instâncias Resttemplate através do RestTemplateCustomizer criado na etapa acima, que é definir o interceptor de balanceamento de carga para a instância do RestTemplate.
@Configuration@ConditionalOnClass(RestTemplate.class)@ConditionalOnBean(LoadBalancerClient.class)@EnableConfigurationProperties(LoadBalancerRetryProperties.class)public class LoadBalancerAutoConfiguration { // omit @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializer( final LIST <RestTemplatecustomizer> Customizers) {Retorne new SmartInitializingingleton () {@Override public void depoisSingLetonsInsinAntiated () {for (Resttemplate Resttemplate: LOADBalanceraUtoconfiguration.Thresttemplates) {para (RestTemplation (RestTemLatCUSTOMENTOMIVER.THSTEMERSERSERSERSERSERSERSERS (para (RestTemLatCUSTOMEstomizer. personalizador.Customize (RestTemplate); }}}}}}}; } // omit @configuration @conditionalonMissingClass ("org.springframework.retry.support.retrytemplate") classe Static loadBalancerIntercetorConfig {@Bean public LOUTBALANCERINTECTOR RIPBONINTERCORPORT (LoadBalerClient Load) LoadBalancerIntercept (LoadBalancerclient, RequestFactory); } @Bean @ConditionalonMissingBean ResttemplateCustomizer RestTemplateCustomizer (Final LoadBalancerIntercept LoadBalancerInterceptor) {Return new RestTemplateCustomizer () {@Override public Void Customize (Resttemplate Resttemplate) {listhttttpTPRPER (@Override público RestTemplate.getInterceptores ()); list.add (loadBalancerInterceptor); RestTemplate.SetInterceptores (Lista); }}; }} // omitido}RestTemplate.SetInterceptores (Lista) Este local é o local onde o interceptador de balanceamento de carga é injetado.
Na verdade, pode ser adivinhado a partir deste local que o RestTemplate pode criar solicitações correspondentes para obter o balanceamento de carga através de interceptores injetados.
Também pode ser visto que você pode personalizar o interceptador para alcançar outros propósitos.
4. Configuração do RibbonClientConfiguration gera instância de zoneawareloadbalancer
Localização do código:
Spring-Cloud-Netflix-core-1.3.5.release.jar
org.springframework.cloud.netflix.ribbon
RibbonClientConfiguration.class
@Suppresswarnings ("deprecação")@configuration@EnableConfigurationProperties // O pedido é importante aqui, o último deve ser o padrão, primeiro deve ser opcional // consulte Https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653@import( {khttprpritbonConfiguration.clls, restclientRibbonfiguration.cl), httttpclientRabbonfllass, restclientRibbonfiguration.cliSl) omit @Bean @conditionalonMissingBean ILOADBALLANCER RIPBONLOADBALANCER (ICLIENTCONFIG Config, ServerList <Verver> ServerList, ServerListFilter <Verver> serverListFilter, regra Irule, iping ping, serverlistUmerListupDater) {return this.propertiesfactory.get (iloadBalancer.class, config, nome); } Retornar novo ZoneawareloadBalancer <> (config, regra, ping, serverlist, serverlistfilter, serverlistupdater); } // omitido}Zoneawareloadbalancer herda da interface iloadbalancer, que tem um método:
/*** Escolha um servidor no carregamento de balanceador. * * @param chave Um objeto que o balanceador de carga pode usar para determinar qual servidor retornar. nulo se * o balanceador de carga não usa esse parâmetro. * @return servidor escolhido */ servidor público escolheerver (chave do objeto);
O ZoneawareloadBalancer é uma classe de implementação de balanceamento de carga específica e também é a classe de balanceamento de carga padrão. Uma certa instância de serviço é selecionada implementando o método ChoiceServer.
Interceptar e solicitar
1. Use Resttemplate para executar várias solicitações, como get e post, todas implementadas através do método DoExecute.
Localização do código:
Spring-Web-4.3.12.Release.Jar
org.springframework.web.client
Resttemplate.class
A classe pública Resttemplate estende a interceptação de acrescesso implementa as restaurações {// <t> t deexecute ligeiramente protegido (URI URL, método httpmethod, requestcallback requestcallback, respostaextractor <t> Responstroutor) lança RestcientException {Assert.NotNull (url); Assert.NotNull (Método, "'Método' não deve ser nulo"); CLIENTHTTPPRESPOnsion Response = null; tente {clienthttprequest request = createrequest (url, método); if (requestCallback! = null) {requestCallback.dowithRequest (request); } resposta = request.execute (); manutenção de manutenção (URL, método, resposta); if (Responsextractor! = null) {retorna respostaextractor.extractData (resposta); } else {return null; }} catch (ioexception ex) {string Resource = url.toString (); String consulta = url.getrawQuery (); recurso = (consulta! = null? Resource.SubString (0, Resource.Indexof ('?'))): Recurso); lançar novo ResourceAcceSception ("Erro de E/S em" + method.name () + "request for/" " + Resource +"/":" + ex.getMessage (), ex); } finalmente {if (resposta! = null) {Response.close (); }}} // omitido}Os vários métodos de solicitação HTTP suportados chamam o método DoExecute, que chama o método de criação para criar a instância da solicitação e executa a solicitação para obter o objeto de resposta.
2. Gere a instância de solicitação para criar fábrica
No código anterior, o método CreaterEquest é chamado para criar a instância de solicitação, definida na classe pai.
Primeiro organize o principal relacionamento de herança:
O método CreaterEquest é realmente definido na classe abstrata httpaccessor.
Classe de abstração pública httpaccessor {private clientHttPreQuestFactory requestFactory = new SimpleClientHttPrequestFactory (); public void setRequestFactory (clientHttPreQuestFactory RequestFactory) {Assert.NotNull (requestFactory, "clientHttPrequestFactory não deve ser nulo"); this.RequestFactory = requestFactory; } public clientHttPreQuestFactory getRequestFactory () {return this.RequestFactory; } ClientHttPrequest protegido CreaterEquest (URI URL, Método HttpMethod) lança IoException {clientHttPrequest request = getRequestFactory (). CreaterEquest (URL, Método); if (logger.isdebugenabled ()) {logger.debug ("criado" + method.name () + "request for /" "" + url + " /" "); } solicitação de retorno; }}Ligue para o método getRequestFactory no método CreaterEquest para obter a instância da solicitação para criar a fábrica. De fato, o GetRequestFactory não é definido na classe atual HTTPAccessor, mas é definido na subclasse interceptahttpaccessor.
classe abstrata pública interceptinghttpaccessor estende o httpaccessor {list private <clienthttprequestintercept> interceptores = new ArrayList <clientHttPrequestInterceptor> (); public void setInterceptores (list <clienthttprequestintercept> interceptores) {this.intercepts = interceptores; } Lista pública <clientHttPreQuestIntercept> getInterceptores () {return Interceptores; } @Override public clientHttPrequestFactory getRequestFactory () {clientHttPrequestFactory delegate = super.getRequestFactory (); if (! collectionUtils.IssEmpty (getInterceptores ())) {retorne novos interceptaClientHttPrequestFactory (delegado, getInterceptores ()); } else {return delegate; }}}Eu fiz uma pequena ação aqui. Primeiro, criei e obtive a fábrica SimpleClientHttPrequestFactory através da classe Httpaccessor. Esta fábrica cria principalmente instâncias de solicitação básica em que não há interceptador.
Em segundo lugar, quando houver injeção de interceptores, crie a fábrica interceptaClientHttPrequestFactory. Esta fábrica cria uma instância de solicitação com um interceptador. Como o interceptador de balanceamento de carga é injetado, ele é criado a partir da fábrica interceptctcientHttPrequestFactory.
3. Crie uma instância de solicitação através da fábrica
Ao criar uma instância, depende do método CreaterEquest da fábrica.
classe pública interceptingClientHttPrequestFactory estende abstrataClientHttPreQuestFactoryWrapper {Lista final privada <ClientHttPrequestInterception> Interceptores; public interceptingClientHttPreQuestFactory (clientHttPrequestFactory RequestFactory, List <clientHttPrequestIntercept> Interceptores) {super (requestFactory); this.Interceptores = (interceptores! = NULL? Interceptores: Coleções. } @Override protegido clientHttPrequest CreaterEquest (Uri Uri, httpmethod httpmethod, clientHttPrequestFactory requestFactory) {Retorne novo interceptctcientHttPrequest (requestFactory, this.intercepts, Uri, httpMethod); }}Significa nova instância interceptaClientHttPrequest e injete o interceptador e a fábrica de criação de instância de solicitação básica.
4. Solicite a instância para chamar o método de interceptação do interceptação de balanceamento de carga injetado no estágio de configuração
Pode ser visto na Etapa 1 que, após a criação da instância da solicitação, a solicitação é executada executando o método Execute da instância da solicitação.
ClientHtttPreQuest request = CreaterEquest (URL, Método); if (requestCallback! = Null) {requestCallback.dowithRequest (request);} resposta = request.execute ();A instância de solicitação real é interceptar -clienthttrequest e o execute está na sua classe pai.
Localização da definição de classe:
Spring-Web-4.3.12.Release.Jar
org.springframework.http.client
InterceptingClientHttPrequest.class
Dê uma olhada em seu relacionamento de herança.
O método executivo -internal implementado pela subclasse é realmente chamado no método Execute.
Classe abstrata public abstractClientHttPrequest implementa CLIENTHTTPREQUEST {private final httpheaders Headers = New Httpheaders (); Privado booleano executado = false; @Override Public final httpheaders getheaders () {return (this.executed? Httpheaders.readonlyhttpheaders (this.headers): this.headers); } @Override public final OutputStream getBody () lança IoException {AssertNotexecuted (); retornar getBodyinternal (this.Headers); } @Override Public final clientHttpResponse Execute () lança IoException {AssertNotexected (); CLIENTHTTPRESPONCE RESULTO = EXECUTEINTERNAL (this.Headers); this.Executed = true; resultado de retorno; } void protegido assertnotexecuted () {Assert.state (! this.executed, "clientHttPrequest já executado"); } Abstract OutputStream protegido GetBodyinternal (cabeçalhos Httpheaders) lança a ioException; CLIENTE DE CLIENTES ABRATOS PROTECIDOS EXECUTEIRNAL (HTTPHEADERS Cabeçalhos) lança IoException;}De fato, é o método executivo da classe interceptctingClientHttPrequest. Em que é chamada a execução do executor interceptingRequestExecution. Passe o nível e determine que, se um interceptador foi injetado, o método de interceptação do interceptador é chamado.
O interceptador aqui é na verdade a instância interceptora interceptora de balanceamento de carga injetada na instância do RestTemplate durante o estágio de configuração. Você pode se referir à etapa 2 do estágio de configuração acima.
classe interceptingClientHttPrequest estende abstrumBufferingClientHttPRequest {// omit @Override Protected final clientHttProponse ExecuteInternal (httpheaders headers, Byte [] bufferoutput) lança ioexception {-QueSecution (interception); return requestExecution.execute (isto, bufferoutput); } classe privada interceptingRequestExecution implementa o clientHttPreQuestExecution {Private final Iterator <clientHttPrequestInterceptor> iterator; public interceptingRequestExecution () {this.iterator = interceptores.iterator (); } @Override public clienthttproponse Execute (solicitação httprequest, byte [] body) lança a ioexception {if (this.iterator.hasnext ()) {clientHttPrequestInterceptor nextInterceptor = this.iter.next (); return nextInterIntercept.Intercept (solicitação, órgão, isto); } else {clientHttPrequest delegate = requestFactory.createrEquest (request.geturi (), request.getMethod ()); para (map.entry <string, list <string>> Entrada: request.getheaders (). EntrySet ()) {list <tring> valores = entradas.getValue (); para (String Value: valores) {delegate.getheaders (). Add (Entry.getKey (), valor); }} if (body.length> 0) {streamutils.copy (corpo, delegate.getbody ()); } retornar delegate.execute (); }}}}5. O interceptador de balanceamento de carga chama o cliente de balanceamento de carga
No método de interceptação da classe interceptadora de balanceamento de carga classe LoadBalancerInterceptor, é chamado o método Execute da classe de implementação do cliente de balanceamento de carga do cliente é chamado.
classe pública LoadBalancerInterceptor implementa o clientHttPreQuestInterceptor {private loadBalancerclient loadBalancer; LOADBALABERBALANCER SUBRIVADO REQUITATION FABRICO; public loadBalancerIntercept (LoadBalancerclient loadBalancer, loadBalancerRequestFactory requestFactory) {this.loadBalancer = loadBalancer; this.RequestFactory = requestFactory; } public loadBalancerIntercept (LoadBalancerclient loadBalancer) {// Para compatibilidade com versões anteriores (LoadBalancer, new loadBalancerRequestFactory (loadBalancer)); } @Override public clienthttpropSept Intercept (solicitação final httPrequest, byte final [] corporal, execução final do clienteHttPrequeSexecution) lança a ioexception {final URI originaluri = request.geturi (); String serviceName = originaluri.gethost (); Assert.state (serviceName! = Null, "solicitar URI não contém um nome de host válido:" + originaluri); return this.loadbalancer.execute (serviceName, requestFactory.createrEquest (solicitação, corpo, execução)); }}Na etapa 1 da fase de configuração, você pode ver que a classe de implementação é o RibBonLoadBalancerclient.
6. O cliente de balanceamento de carga chama a política de balanceamento de carga para selecionar a instância de serviço de destino e iniciar uma solicitação
No primeiro método de execução e no método GetServer do RibbonLoadBalancerclient, podemos ver que, de fato, o método ChoiceServer da classe de implementação do iloadbalancer é selecionado e entregue ao próximo objeto de solicitação para iniciar uma solicitação.
A classe de implementação de balanceamento de carga aqui é a instância do balanceador de carga da área de Zoneawareloadbalancer por padrão, e seleciona um serviço internamente através da política de equilíbrio.
A criação do zoneawareloadbalancer pode ser encontrada na etapa 4 da fase de configuração.
classe pública RibbonLloadBalancerclient implementa LoadBalancerclient {@Override public <T> t Execute (String ServiceId, LoadBalancerRequest <T> request) lança ioexception {iloadBalancer loadBalancer = getLoadBalancer (ServiceId); Servidor server = getServer (loadBalancer); if (server == null) {lança new ilegalStateException ("sem instâncias disponíveis para" + serviceId); } Ribbonserver ribbonserver = new Ribbonserver (ServiceId, Server, IsSECURE (Server, ServiceId), ServerIntrospector (ServiceId) .getMetadata (Server)); return execute (serviceId, ribbonserver, solicitação); } @Override public <T> t Execute (String ServiceId, ServiceInstance ServiceInstance, LoadBalancerRequest <T> request) lança IoException {Server Server = null; if (instância do serviçoInstanceof ribbonserver) {server = ((ribbonserver) serviceInstance) .getServer (); } if (server == null) {lança new ilegalStateException ("sem instâncias disponíveis para" + serviceId); } RibbonloadBalancerContext context = this.clientFactory .GetLoadBalancerContext (ServiceId); RibbonStatsRecorder statSrecorder = new RibBonStatsRecorder (contexto, servidor); tente {t ReturnVal = request.Apply (ServiceInstance); STATSRECORDER.RECORDSTATS (ReturnVal); return retornVal; } // Pegue a ioException e o RETHROW SO RESTTemplate se comporta corretamente (ioexception ex) {statsRecorder.recordstats (ex); jogar ex; } catch (Exceção ex) {statsRecorder.recordstats (ex); ReflexoTils.rethrowRuntimeException (Ex); } retornar nulo; } // Servidor ligeiramente protegido getServer (iloadBalancer loadBalancer) {if (loadBalancer == null) {return null; } return loadBalancer.chooseserver ("padrão"); // TODO: Melhor manuseio da chave} ILOADBALANCER protegido GetLoBalancer (String ServiceId) {return this.clientFactory.getLoadBalancer (ServiceId); } classe estática pública Ribbonserver implementa o serviçoInstance {private final String ServiceId; servidor final privado servidor; Private final Boolean Secure; mapa privado <string, string> metadados; public ribbonserver (string serviceId, servidor servidor) {this (serviceId, servidor, false, coleções. } public ribbonserver (string serviceId, servidor servidor, boolean seguro, mapa <string, string> metadados) {this.serviceId = serviceId; this.Server = servidor; this.secure = seguro; this.metadata = metadados; } // omitido}}Depois de terminar o código, vamos resumir.
Ao usar o RestTemplate para solicitar outros serviços, a instância regular de solicitação HTTP é usada internamente para enviar solicitações.
Depois de adicionar a anotação @LaNBALanced ao RestTemplate, na verdade, por meio da configuração, um interceptador de balanceamento de carga é injetado no RestTemplate, permitindo que o balanceador de carga escolha o serviço apropriado de acordo com sua política correspondente antes de enviar uma solicitação.
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.