Ribbon use
ribbon is very simple to use. You only need to add configuration to the configuration class
@Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); }
When calling, the service name registered in eureka is directly used for calling, and the ribbon can perform load balancing
@GetMapping("/checkAndBegin/{userId}") public Integer findResumeOpenStateEureka(@PathVariable Long userId) { // List<ServiceInstance> list = discoveryClient.getInstances("lagou-service-resume"); // ServiceInstance serviceInstance = list.get(0); // String host = serviceInstance.getHost(); // int port = serviceInstance.getPort(); String url = "http://zhao-service-resume/resume/openstate/"+userId; System.out.println("from eureka The requested address was obtained in"+url); Integer forObject = restTemplate.getForObject(url, Integer.class); return forObject; }
According to the requirements, you can find that load balancing has been realized by opening multiple Zhao service resume items and printing the request information. In addition, the Ribbon's built-in load balancing strategy is currently available At present, random load balancing random rule is used by default. It takes effect globally by default, but different load balancing policies can be set for different calling services
zhao-service-resume: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #Load policy adjustment
At the same time, you can customize the load balancing strategy and configure it
Ribbon source code analysis
Generally speaking, the auto assembly class is the entrance to load configuration.
@Configuration @Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class) @RibbonClients @AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration") @AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class}) @EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class}) public class RibbonAutoConfiguration { }
Through the above configuration, we can first look at the specific contents of loadbalancenautoconfiguration
@LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList();
The RestTemplate object with @ LoadBalanced annotation will be automatically injected here, and a RestTemplate customizer will also be injected
@Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated( final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) { return () -> restTemplateCustomizers.ifAvailable(customizers -> { for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } }); }
Continuing to the customization code of RestTemplateCustomizer, we find that an interceptor is added to the customization
@Configuration @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate") static class LoadBalancerInterceptorConfig { @Bean public LoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) { return new LoadBalancerInterceptor(loadBalancerClient, requestFactory); } @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; } }
The specific interception content of clienthttprequeinterceptor is to perform load balancing according to the obtained request path and request address
@Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { final URI originalUri = request.getURI(); String serviceName = originalUri.getHost(); Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri); return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution)); }
Code to perform load balancing
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException { ILoadBalancer loadBalancer = getLoadBalancer(serviceId); Server server = getServer(loadBalancer, hint); if (server == null) { throw new IllegalStateException("No instances available for " + serviceId); } RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server)); return execute(serviceId, ribbonServer, request); }
As can be seen from this code, the first line selects the corresponding load balancing strategy according to the configuration. The second line is to select a server for service request according to the corresponding load balancing policy to achieve the purpose of load balancing. Finally, the BaseLoadBalancer performs the operation of selecting services according to different policies
public Server chooseServer(Object key) { if (counter == null) { counter = createCounter(); } counter.increment(); if (rule == null) { return null; } else { try { return rule.choose(key); } catch (Exception e) { logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e); return null; } } }
After considering the above main logic, another question is when the service list is obtained. Spring clientfactory is injected into ribbonautoconfiguration, and spring clientfactory injects RibbonClientConfiguration
public SpringClientFactory() { super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name"); }
RibbonClientConfiguration carries out operations related to injecting client operations, including load balancing policy, client configuration, service list, etc., of which the most important is how to obtain and update the service list
@ConditionalOnMissingBean @SuppressWarnings("unchecked") public ServerList<Server> ribbonServerList(IClientConfig config) { if (this.propertiesFactory.isSet(ServerList.class, name)) { return this.propertiesFactory.get(ServerList.class, config, name); } ConfigurationBasedServerList serverList = new ConfigurationBasedServerList(); serverList.initWithNiwsConfig(config); return serverList; } @Bean @ConditionalOnMissingBean public ServerListUpdater ribbonServerListUpdater(IClientConfig config) { return new PollingServerListUpdater(config); } @Bean @ConditionalOnMissingBean public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList<Server> serverList, ServerListFilter<Server> serverListFilter, IRule rule, IPing ping, ServerListUpdater serverListUpdater) { if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) { return this.propertiesFactory.get(ILoadBalancer.class, config, name); } return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList, serverListFilter, serverListUpdater); }
There is no operation to obtain serverList in the ribbonList method, but it is used in the ribbonLoadBalancer. What's the matter? In fact, the parent class DynamicServerListLoadBalancer of zoneawarelooadbalancer is reassigned, and a scheduled task is executed to update.
void restOfInit(IClientConfig clientConfig) { boolean primeConnection = this.isEnablePrimingConnections(); // turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList() this.setEnablePrimingConnections(false); enableAndInitLearnNewServersFeature(); updateListOfServers(); if (primeConnection && this.getPrimeConnections() != null) { this.getPrimeConnections() .primeConnections(getReachableServers()); } this.setEnablePrimingConnections(primeConnection); LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString()); }
First, through updateaction doUpdate(); Update, and then get it through getRefreshExecutor()
@Override public synchronized void start(final UpdateAction updateAction) { if (isActive.compareAndSet(false, true)) { final Runnable wrapperRunnable = new Runnable() { @Override public void run() { if (!isActive.get()) { if (scheduledFuture != null) { scheduledFuture.cancel(true); } return; } try { updateAction.doUpdate(); lastUpdated = System.currentTimeMillis(); } catch (Exception e) { logger.warn("Failed one update cycle", e); } } }; scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay( wrapperRunnable, initialDelayMs, refreshIntervalMs, TimeUnit.MILLISECONDS ); } else { logger.info("Already active, no-op"); } }
Welcome to search for my official account [micro view] and summarize the classified interview questions. https://github.com/zhendiao/JavaInterview