The Spring Cloud Ribbon is primarily used for client load balancing. The most basic usage is to use resttemplate for dynamic load balancing. We only need to add the following configuration to complete the client load balancing.
@Configuration Public class restconfiguration { @Bean @LoadBalanced public resttemplate Resttemplate () { returnnew resttemplate (); }}
/** Annotation to mark a resttemplate beans to is configured to use a loadbalancerclient @author */@Target ({elementtype.field, elementtype.parameter, Elementtype.method}) @Retention ( Retentionpolicy.runtime) @Documented @inherited@qualifier public @interface loadbalanced {}
Here @LoadBalanced使得
RestTemplate
can be used LoadBalancerClient,
in LoadBalancerClient
this way, there is still a LoadBalancerAutoConfiguration
configuration class as long as it is used to LoadBalancerClient
make configuration. We started with the analysis of the Ribbon as an entry point.
Automated configuration of the LoadBalancer
@Configuration@ConditionalOnClass (resttemplate. class) @ConditionalOnBean (loadbalancerclient. class )@EnableConfigurationProperties (loadbalancerretryproperties.class) Public classloadbalancerautoconfiguration {@LoadBalanced @Autowired (required=false) Private list<resttemplate> resttemplates = collections.emptylist (); @Bean PublicSmartinitializingsingleton Loadbalancedresttemplateinitializer (FinalList<resttemplatecustomizer>customizers) { return NewSmartinitializingsingleton () {@Override Public voidaftersingletonsinstantiated () { for(Resttemplate resttemplate:loadbalancerautoconfiguration. This. Resttemplates) { for(Resttemplatecustomizer customizer:customizers) {customizer.customize (resttemplate); } } } }; } @Autowired (Required=false) PrivateList<loadbalancerrequesttransformer> transformers =collections.emptylist (); @Bean @ConditionalOnMissingBean Publicloadbalancerrequestfactory loadbalancerrequestfactory (loadbalancerclient loadbalancerclient) {return Newloadbalancerrequestfactory (Loadbalancerclient, Transformers); } @Configuration @ConditionalOnMissingClass ("Org.springframework.retry.support.RetryTemplate") Static classLoadbalancerinterceptorconfig {@Bean Public loadbalancerinterceptor ribboninterceptor (loadbalancerclient loadbalancerclient, Loadbalancerrequestfactory requestfactory) {return NewLoadbalancerinterceptor (loadbalancerclient, requestfactory); } @Bean @ConditionalOnMissingBean Public resttemplatecustomizer resttemplatecustomizer (Final loadbalancerinterceptor loadbalancerinterceptor) { return NewResttemplatecustomizer () {@Override Public voidCustomize (Resttemplate resttemplate) {List<ClientHttpRequestInterceptor> list =NewArraylist<>(Resttemplate.getinterceptors ()); List.add (Loadbalancerinterceptor); Resttemplate.setinterceptors (list); } }; } }}
This configuration class mainly does the following several things
1. A bean was created to LoadBalancerInterceptor
intercept client-initiated requests in order to achieve client-side load balancing.
2. A bean was created to RestTemplateCustomizer
add interceptors to the Resttemplate LoadBalancerInterceptor
.
3. Maintain a @LoadBalanced
list of annotated RestTemplate
objects, and maintain them here, by invoking RestTemplateCustomizer
an instance to add interceptors to the required client load Balancer RestTemplate
LoadBalancerInterceptor
.
Now let's take a look at Loadbalancerinterceptor's intercept, and we'll hit a breakpoint here to see how a resttemplate request was intercepted.
We're org.springframework.http.client.InterceptingClientHttpRequest.InterceptingRequestExecution#execute
discovering the following code.
if (this. Iterator.hasnext ()) { this. Iterator.next (); return This ); }
Here and mybatis more similar, are through the chain of responsibility mode, a layer of interception. In the end it will be loadbalancerinterceptor. Now look at the Intercept method of Loadbalancerinterceptor.
The first two steps are to obtain the request address and servicename, which are provided here for reference.
At first we said,
@LoadBalanced使得
RestTemplate
You can use LoadBalancerClient,就是在这里使用LoadBalancerClient的executor方法,做出具体的负载均衡。由于这里的LoadBalancerClient是一个接口,他具体的实现类是
ribbonloadbalancerclient, where we analyze the specific Execute method
Public<T> T Execute (String serviceId, loadbalancerrequest<t> request)throwsIOException {iloadbalancer loadbalancer=Getloadbalancer (serviceId); Server server = getserver (loadbalancer); if(Server = =NULL) { Throw NewIllegalStateException ("No instances available for" +serviceId); } ribbonserver ribbonserver=Newribbonserver (serviceId, Server, issecure (server, ServiceId), Serverintrospector (serviceId). GetMetaData (server)); returnExecute (serviceId, ribbonserver, request); }
The first step to get Loadbalancer,loadbalancer is the interface that defines the operation of the software load balancer, as in the following methods. The default configuration is to use Zoneawareloadbalancer.
Public InterfaceIloadbalancer {//adding service instances to the list of instances maintained in the load Balancer Public voidAddservers (list<server>newservers); //pick a specific service instance from the load Balancer PublicServer chooseserver (Object key); //used to notify and flag that a specific instance of the load balancer has stopped service, or the load balancer will assume that the service instance is normal before the next fetch of the service instance manifest. Public voidmarkserverdown (server server);//Gets the list of instances of the current normal service PublicList<server>getreachableservers (); //gets a list of all known service instances, including normal services and stopping service instances. PublicList<server>getallservers ();}
The second step is to obtain the server according to a certain algorithm, this step is also the core of the whole ribbon, about the specific algorithm logic, we analyze behind
protected Server getserver (iloadbalancer loadbalancer) { ifnull) { return null; } return // todo:better handling of key }
The third step is to pack a ribbonserver after getting to the server
Once a specific server is selected, it can then request that the remaining interceptors will be completed.
Public<T> T Execute (String serviceId, serviceinstance serviceinstance, loadbalancerrequest<t> request)throwsIOException {Server server=NULL; if(serviceinstanceinstanceofribbonserver) {Server=((ribbonserver) serviceinstance). Getserver (); } if(Server = =NULL) { Throw NewIllegalStateException ("No instances available for" +serviceId); } Ribbonloadbalancercontext Context= This. Clientfactory. Getloadbalancercontext (SERVICEID); Ribbonstatsrecorder Statsrecorder=NewRibbonstatsrecorder (context, server); Try{ T returnval = request.apply (serviceinstance); Statsrecorder.recordstats (ReturnVal); returnReturnVal; }return NULL; }
Finally, a specific request is created and executed.
PublicClienthttpresponse Execute (HttpRequest request,byte[] body)throwsIOException {if( This. Iterator.hasnext ()) {Clienthttprequestinterceptor Nextinterceptor= This. Iterator.next (); returnNextinterceptor.intercept (Request, Body, This); } Else{ clienthttprequest delegate = requestfactory.createrequest (Request.geturi (), Request.getmethod ()); for(Map.entry<string, list<string>>entry:request.getHeaders (). EntrySet ()) {List<String> values =Entry.getvalue (); for(String value:values) {delegate.getheaders (). Add (Entry.getkey (), value); } } if(Body.length > 0) {streamutils.copy (body, delegate.getbody ()); } returnDelegate.execute (); } }
The above is the analysis of the general process of the Ribbon