Ribbon 原理初探

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情

Ribbon 原理初探

决心是成功的开始

Ribbon 使用方法

    Ribbon 的使用方法是在 RestTemplate 的方法上面加上一个 **@LoadBalanced **注解,之后这个方法进行请求的时候就可以进行负载均衡请求了

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

负载均衡原理图解

image.png

RestTemplate 方法使用

    如果不用 Ribbon 的话,在进行 http 请求的时候,直接 new 一个 RestTemplate 对象,通过相应的方法比如 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 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 {
}
复制代码

    看注释就是显示这个注解使用在 RestTemplate 类上面去配置 LoadBalancerClient ,我们去找找 LoadBalancerClient 这个接口看看在哪个包里面 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客户端配置类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 客户端配置类

// 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 规则

// 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方式

// 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 服务列表

// 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 服务列表更新器

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

复制代码

6. RibbonLoadBalancer 服务的负载均衡器

// 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 服务列表筛选器

// 服务列表筛选器
@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;
}

复制代码

负载均衡器初始化核心配置类

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);
        };
    }
                        ......
}
复制代码

负载均衡拦截器 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));
}
复制代码

负载均衡器执行请求 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);
}
复制代码

真正的执行请求

    通过 LoadBalancerRequest 请求进行执行

@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 封装请求进行服务请求

@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);
                }

            });
}
复制代码

小结

    其实就是通过拦截器将 RestTemplate 请求进行了封装,再将请求地址进行解析成对应可用的服务地址,再进行请求,不过这个请求的服务实例是通过负载均衡器进行选择出来的

猜你喜欢

转载自juejin.im/post/7085158150466175013
今日推荐