A Preliminary Study on Ribbon Principle

Get into the habit of writing together! This is the 11th day of my participation in the "Nuggets Daily New Plan·April Update Challenge", click to view the event details

A Preliminary Study on Ribbon Principle

Determination is the beginning of success

How to use Ribbon

    The way to use Ribbon is to add a **@LoadBalanced ** annotation to the RestTemplate method, and then load balancing requests can be made when this method makes a request.

@Configuration
public class RestConfig {
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
复制代码

Load balancing principle diagram

image.png

RestTemplate method use

    If you don't use Ribbon, when you make an http request, you can directly create a new RestTemplate object and call it through the corresponding method such as getForEntity().
image.png

@Override
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, 
                                          Object... uriVariables)
        throws RestClientException {
    RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
    ResponseExtractor<ResponseEntity<T>> responseExtractor = 
        responseEntityExtractor(responseType);
    // 执行请求
    return nonNull(execute(url, HttpMethod.GET, requestCallback, 
                           responseExtractor, uriVariables));
}
复制代码

@LoadBalanced annotation source code

/**
 * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
复制代码

Looking at the annotation is to show that this annotation is used to configure LoadBalancerClient     on the RestTemplate class . Let's look for the LoadBalancerClient interface to see which package package org.springframework.cloud.client.loadbalancer;

public interface LoadBalancerClient extends ServiceInstanceChooser {
    // 执行负载均衡器指定的服务请求
	<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
    // 执行负载均衡器指定的服务请求
	<T> T execute(String serviceId, ServiceInstance serviceInstance,
			LoadBalancerRequest<T> request) throws IOException;
    // 创建一个具有真实主机和端口的适当URI,供系统使用。
    // 一些系统使用带有逻辑服务名称的URI作为主机,
    // 例如 http://myservice/path/to/service
    // 这将用ServiceInstan中的主机:端口替换服务名称
	URI reconstructURI(ServiceInstance instance, URI original);
}
/**
 * 通过负载均衡器选择一个服务实例
 */
public interface ServiceInstanceChooser {
	ServiceInstance choose(String serviceId);
}
复制代码

Ribbon client configuration class RibbonClientConfiguration

public class RibbonClientConfiguration {
// 客户端链接超时
public static final int DEFAULT_CONNECT_TIMEOUT = 1000;

// 客户端读取超时
public static final int DEFAULT_READ_TIMEOUT = 1000;

@RibbonClientName
private String name = "client";

@Autowired
private PropertiesFactory propertiesFactory;
    ......
}
复制代码

1. RibbonClientConfig client configuration class

// Ribbon 客户端配置类
@Bean
@ConditionalOnMissingBean
public IClientConfig ribbonClientConfig() {
    DefaultClientConfigImpl config = new DefaultClientConfigImpl();
    config.loadProperties(this.name);
    config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
    config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
    config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
    return config;
}
复制代码

2. RibbonRule rules

// Ribbon 规则类
@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
    if (this.propertiesFactory.isSet(IRule.class, name)) {
        return this.propertiesFactory.get(IRule.class, config, name);
    }
    ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
    rule.initWithNiwsConfig(config);
    return rule;
}
复制代码

3. RibbonPing Ping method

// Ribbon Ping类
@Bean
@ConditionalOnMissingBean
public IPing ribbonPing(IClientConfig config) {
    if (this.propertiesFactory.isSet(IPing.class, name)) {
        return this.propertiesFactory.get(IPing.class, config, name);
    }
    return new DummyPing();
}

复制代码

4. Ribbon ribbonServerList service list

// Ribbon 服务列表
@Bean
@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;
}
复制代码

5. RibbonServerListUpdater Ribbon service list updater

// Ribbon 服务列表更新器,默认选择的是 PollingServerListUpdater 定时拉取
@Bean
@ConditionalOnMissingBean
public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
    return new PollingServerListUpdater(config);
}

复制代码

6. Load balancer for RibbonLoadBalancer service

// Ribbon 服务的负载均衡器
@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);
}
复制代码

7. RibbonServerListFilter service list filter

// 服务列表筛选器
@Bean
@ConditionalOnMissingBean
@SuppressWarnings("unchecked")
public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
    if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {
        return this.propertiesFactory.get(ServerListFilter.class, config, name);
    }
    ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
    filter.initWithNiwsConfig(config);
    return filter;
}

复制代码

Load balancer initialization core configuration class

Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();

@Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
// 负载均衡 RestTemplate 初始化配置
@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);
            }
        }
    });
}

@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
        LoadBalancerClient loadBalancerClient) {
    return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
}
// 负载均衡拦截器配置
@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);
        };
    }
                        ......
}
复制代码

Load Balancer Interceptor LoadBalancerInterceptor

@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
        final ClientHttpRequestExecution execution) throws IOException {
    // 解析服务地址
    final URI originalUri = request.getURI();
    // 请求服务名称
    String serviceName = originalUri.getHost();
    // 执行的请求流程
    return this.loadBalancer.execute(serviceName,
            this.requestFactory.createRequest(request, body, execution));
}
复制代码

The load balancer executes the request loadBalancer.execute

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);
    }
    // 创建 Ribbon 服务
    RibbonServer ribbonServer = new RibbonServer(serviceId, server,
            isSecure(server, serviceId),
            serverIntrospector(serviceId).getMetadata(server));
    // 真正的执行请求
    return execute(serviceId, ribbonServer, request);
}
复制代码

the real execution request

    Execute via LoadBalancerRequest request

@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance,
        LoadBalancerRequest<T> request) throws IOException {
    Server server = null;
    if (serviceInstance instanceof RibbonServer) {
        server = ((RibbonServer) serviceInstance).getServer();
    }
    // 如果服务为空的情况下那么就抛出异常没有实例可以使用
    if (server == null) {
        throw new IllegalStateException("No instances available for " + serviceId);
    }

    RibbonLoadBalancerContext context = this.clientFactory
            .getLoadBalancerContext(serviceId);
    RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

    try {
        // 真正的请求方法
        T returnVal = request.apply(serviceInstance);
        statsRecorder.recordStats(returnVal);
        return returnVal;
    }
    return null;
}

复制代码

ServiceRequestWrapper encapsulates requests for service requests

@Override
public ListenableFuture<ClientHttpResponse> intercept(final HttpRequest request,
        final byte[] body, final AsyncClientHttpRequestExecution execution)
        throws IOException {
    // 获取请求的路径URI
    final URI originalUri = request.getURI();
    // 获取服务的地址
    String serviceName = originalUri.getHost();
    return this.loadBalancer.execute(serviceName,
            new LoadBalancerRequest<ListenableFuture<ClientHttpResponse>>() {
                // 执行的方法
                @Override
                public ListenableFuture<ClientHttpResponse> apply(
                        final ServiceInstance instance) throws Exception {
                    // 封装请求
                    HttpRequest serviceRequest = new ServiceRequestWrapper(request,
                            instance, AsyncLoadBalancerInterceptor.this.loadBalancer);
                    // 进行服务调用执行访问
                    return execution.executeAsync(serviceRequest, body);
                }

            });
}
复制代码

summary

    In fact, the RestTemplate request is encapsulated by the interceptor, and then the request address is parsed into the corresponding available service address, and then the request is made, but the service instance of this request is selected by the load balancer

Guess you like

Origin juejin.im/post/7085158150466175013