Ribbon底层执行流程源码剖析

文章目录

  • 代码准备
  • 源码解析 LoadBalancerClient
  • 负载均衡器
  • 总结

代码准备

实际应用中,通常将 RestTemplate 和 Ribbon 结合使用,RestTemplate增加 @LoadBalance注解后,在进行远程调度时能够做到负载均衡,例如:

@Configuration
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

消费者调用服务接口:

@RestController
@Api(tags = "消费端")
public class ConsumerController {

    public static final String PAYMENT_URL = "http://CLOUD-PROVIDER-SERVICE";

    @Autowired
    private RestTemplate restTemplate;

    @ApiOperation(value="查询用户")
    @PostMapping(value = "/search/v1")
    public RestResult search(@RequestBody UserTest userTest) {

        try {

            return restTemplate.postForObject(PAYMENT_URL + "/user/search/v1", userTest, RestResult.class);

        } catch (Exception e) {
            return RestResult.fail(ResultCode.DATA_ACCESS_ERROR);
        }

    }
}

源码解析 LoadBalancerClient

@LoadBalanced,通过源码可以发现这是一个标记注解:

用于标记要配置为使用LoadBalancer客户端的RestTemplate或WebClient bean。

// 用于标记要配置为使用LoadBalancer客户端的RestTemplate或WebClient bean。
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {

}

LoadBalancerAutoConfiguration 负载均衡器自动配置类 在@LoadBalanced同包下

// Ribbon的自动配置(客户端负载平衡)。
@Configuration(proxyBeanMethods = false) // 代理方法 每个@Bean方法被调用多少次返回的组件都是新创建的
@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();

	@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(proxyBeanMethods = false)
	@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);
			};
		}

	}

	/**
	 * Auto configuration for retry mechanism.
	 */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(RetryTemplate.class)
	public static class RetryAutoConfiguration {

		@Bean
		@ConditionalOnMissingBean
		public LoadBalancedRetryFactory loadBalancedRetryFactory() {
			return new LoadBalancedRetryFactory() {
			};
		}

	}

	/**
	 * Auto configuration for retry intercepting mechanism.
	 */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(RetryTemplate.class)
	public static class RetryInterceptorAutoConfiguration {

		@Bean
		@ConditionalOnMissingBean
		public RetryLoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRetryProperties properties,
				LoadBalancerRequestFactory requestFactory,
				LoadBalancedRetryFactory loadBalancedRetryFactory) {
			return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
					requestFactory, loadBalancedRetryFactory);
		}

		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}

	}

}

从源码可以看出,这个类作用主要是使用RestTemplateCustomizer对所有标注了@LoadBalanced的RestTemplate Bean添加了一个LoadBalancerInterceptor拦截器,而这个拦截器的作用就是对请求的URI进行转换获取到具体应该请求哪个服务实例。

// 实现了ClientHttpRequestInterceptor  有个非常重要的拦截方法intercept
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

	private LoadBalancerClient loadBalancer;

	private LoadBalancerRequestFactory requestFactory;

	public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
			LoadBalancerRequestFactory requestFactory) {
		this.loadBalancer = loadBalancer;
		this.requestFactory = requestFactory;
	}

	public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
		// for backwards compatibility
		this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
	}

	/*
	 * 拦截给定的请求,并返回响应。 通过LoadBalancerClient执行具体的请求发送。
	 */
	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
		// http://CLOUD-PROVIDER-SERVICE/user/search/v1	
		final URI originalUri = request.getURI();
		// CLOUD-PROVIDER-SERVICE
		String serviceName = originalUri.getHost();
		Assert.state(serviceName != null,
				"Request URI does not contain a valid hostname: " + originalUri);
		return this.loadBalancer.execute(serviceName,
				this.requestFactory.createRequest(request, body, execution));
	}

}

实现了ClientHttpRequestInterceptor 有个非常重要的拦截方法intercept,拦截给定的请求,并返回响应。 通过LoadBalancerClient执行具体的请求发送。

跟一下LoadBalancerClient源码

public interface LoadBalancerClient extends ServiceInstanceChooser {

	// 根据传入的服务id,指定的负载均衡器中的服务实例执行请求。
	<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

	// 使用来自LoadBalancer的ServiceInstance为指定服务执行请求。
	<T> T execute(String serviceId, ServiceInstance serviceInstance,
			LoadBalancerRequest<T> request) throws IOException;

	// 创建一个带有真实主机和端口的正确URI,供系统使用。有些系统使用逻辑服务名作为主机的URI,
	URI reconstructURI(ServiceInstance instance, URI original);

}

继承了ServiceInstanceChooser

根据传入的服务id,从负载均衡器中为指定的服务选择一个服务实例。
(如三台服务实例,该方法通过serverid 选择一台实例并返回)
ServiceInstance :包含了改服务实例的全部信息 如主机名端口号等等

public interface ServiceInstanceChooser {

	/**
	 * 根据传入的服务id,从负载均衡器中为指定的服务选择一个服务实例。
	 * @param serviceId The service ID to look up the LoadBalancer.
	 * @return A ServiceInstance that matches the serviceId.
	 */
	ServiceInstance choose(String serviceId);

}

LoadBalancerClient 的实现类 RibbonLoadBalancerClient,关键代码如下:

	@Override
	public URI reconstructURI(ServiceInstance instance, URI original) {
		Assert.notNull(instance, "instance can not be null");
		//获取服务实例的id
		String serviceId = instance.getServiceId();
		//获取服务的上下文
		RibbonLoadBalancerContext context = this.clientFactory
				.getLoadBalancerContext(serviceId);

		URI uri;
		Server server;
		//如果当前服务实例已经是ribbon服务实例
		if (instance instanceof RibbonServer) {
			//获取ribbon服务实例
			RibbonServer ribbonServer = (RibbonServer) instance;
			//得到服务
			server = ribbonServer.getServer();
			//通过服务实例和old url(原始url当前请求的url)重新编译成new的url
			uri = updateToSecureConnectionIfNeeded(original, ribbonServer);
		}
		else {
			//如果第一次请求 构建一个新的服务 主机名 端口号 名称
			server = new Server(instance.getScheme(), instance.getHost(),
					instance.getPort());
			//获取serviceId对应配置信息
			IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
			ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
			//构建新的url
			uri = updateToSecureConnectionIfNeeded(original, clientConfig,
					serverIntrospector, server);
		}
		//返回指定实例的新的url
		return context.reconstructURIWithServer(server, uri);
	}
	/**
	执行请求
	*/
	@Override
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
			throws IOException {
		return execute(serviceId, request, null);
	}

	public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
		//通过id得到ILoadBalancer  该方法定义了 新增 删除 查找指定服务等方法
		//具体看具体实现类
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
		//通过serviceId解析出来指定的server (server 形式为 ip:端口号)
		Server server = getServer(loadBalancer, hint);
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		//通过得到的服务信息 创建了一个RibbonServer 特定于Ribbon本身的server实现
		//该server中有对应的主机名 解析的server 源数据 等具体信息
		RibbonServer ribbonServer = new RibbonServer(serviceId, server,
				isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));
		//重载 得到特定的ribbonserver 就开始真正执行操作了
		return execute(serviceId, ribbonServer, request);
	}

	@Override
	public <T> T execute(String serviceId, ServiceInstance serviceInstance,
			LoadBalancerRequest<T> request) throws IOException {
		Server server = null;
		if (serviceInstance instanceof RibbonServer) {
			//如果当前的实例属于RibbonServer 得到当前的server
			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;
		}
		// catch IOException and rethrow so RestTemplate behaves correctly
		catch (IOException ex) {
			statsRecorder.recordStats(ex);
			throw ex;
		}
		catch (Exception ex) {
			statsRecorder.recordStats(ex);
			ReflectionUtils.rethrowRuntimeException(ex);
		}
		return null;
	}

负载均衡器

从 RibbonLoadBalancerClient 代码可以看出,实际负载均衡的是通过 ILoadBalancer 来实现的。

public interface ILoadBalancer {

	/**
	 * 向负载均衡器中添加一个服务实例集合。
	 */
	public void addServers(List<Server> newServers);
	
	/**
	 * 跟据key,从负载均衡器获取服务实例。
	 */
	public Server chooseServer(Object key);
	
	/**
	 * 用来标记某个服务实例下线。
	 */
	public void markServerDown(Server server);
	
	/**
	 * 这个方法被弃用
	 */
	@Deprecated
	public List<Server> getServerList(boolean availableOnly);

	/**
	 * 获取可用的服务实例集合。
     */
    public List<Server> getReachableServers();

    /**
     * 获取所有服务实例集合,包括下线的服务实例。
     */
	public List<Server> getAllServers();
}

ILoadBalancer 的实现 依赖关系示意图如下:

Ribbon底层执行流程源码剖析_第1张图片
默认采用了ZoneAwareLoadBalancer来实现负载均衡器。

public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,
                            IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,
                            ServerListUpdater serverListUpdater) {
   super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
}

为LoadBalancer定义“负载均衡策略”的接口。 如轮询,随机等等

public interface IRule{

    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

总结

当我们使用@LoadBalanced注解赋予RestTemplate负载均衡的能力时

LoadBalancerAutoConfiguration对所有标注了@LoadBalanced的RestTemplate Bean添加了一个LoadBalancerInterceptor拦截器,而这个拦截器的作用就是对请求的URI进行转换获取到具体应该请求哪个服务实例。

此时我们在使用RestTemplate调用服务时就会被intercept()方法拦截进LoadBalancerClient.execute方法。通过负载均衡器得到特定的Server然后通过request.apply(serviceInstance);执行请求得到 响应并返回。

你可能感兴趣的:(SpringCloud,spring,cloud)