@LoadBalanced 原理

工程版本

<spring-boot.version>2.2.6.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
<spring-cloud.version>Hoxton.SR4</spring-cloud.version>

RestTemplate与Ribbon实现负载均衡

@SpringBootApplication
@EnableDiscoveryClient
public class App2Application {
    public static void main(String[] args) {
        SpringApplication.run(App2Application.class, args);
    }

    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @RestController
    public class TestController {
        @Autowired
        private RestTemplate restTemplate;

        @RequestMapping(value = "/test/{str}", method = RequestMethod.GET)
        public String echo(@PathVariable String str) {
            // 直接通过服务名调用接口
            return restTemplate.getForObject("http://app-provider/app/" + str,String.class);
        }
    }
}

通过 Spring Cloud 原生注解 @EnableDiscoveryClient 开启服务注册发现功能。给 RestTemplate 实例添加 @LoadBalanced 注解,开启 @LoadBalancedRibbon 的集成。

RestTemplate是Spring提供的用于访问Rest服务的客户端,通过@LoadBalanced注解就可以将RestTemplate与Ribbon集成并实现负载均衡。

下面来看RestTemplateRibbon是如何联系起来并实现客户端负载均衡的。

@LoadBalanced

/**
 * Annotation to mark a RestTemplate or WebClient bean to be configured to use a
 * LoadBalancerClient.
 * @author Spencer Gibb
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {

}

查看@LoadBalanced源码的注释可以知道,它的作用就是用来给RestTemplate标记,以使用负载均衡客户端(LoadBalancerClient)。

LoadBalancerClient

// 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;

	// 为系统构建一个合适的“host:port”形式的URI。
    // 在分布式系统中,我们使用逻辑上的服务名称作为host来构建URI(替代服务实例的“host:port”形式)
    // 进行请求,比如:http://myservice/path/to/service。
	URI reconstructURI(ServiceInstance instance, URI original);

}

public interface ServiceInstanceChooser {
    // 该方法前面已经分析过了
    // 根据传入的serviceId,从负载均衡器中挑选一个实例
	ServiceInstance choose(String serviceId);
}

LoadBalancerAutoConfiguration

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

    // 维护了一个被@LoadBalanced注解修饰的RestTemplate对象列表
	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();

	@Autowired(required = false)
	private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

    // 通过调用RestTemplateCustomizer的实例
    // 来给需要客户端负载均衡的RestTemplate增加LoadBalancerInterceptor拦截器。
	@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);
		}
        
        // 用于给RestTemplate增加LoadBalancerInterceptor拦截器。
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}

	}
}

通过上面这个自动配置类可以发现,当一个被@LoadBalanced注解修饰的RestTemplate对象向外发起HTTP请求时,会被LoadBalancerInterceptor类的intercept函数所拦截。

LoadBalancerInterceptor

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

	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
		final URI originalUri = request.getURI();
        // 获取服务名
		String serviceName = originalUri.getHost();
        // 最后委托给 LoadBalancerClient 来实现
        // 主要实现负载均衡策略
		return this.loadBalancer.execute(serviceName,
				this.requestFactory.createRequest(request, body, execution));
	}
}

RibbonLoadBalancerClient

public class RibbonLoadBalancerClient implements LoadBalancerClient {

	private SpringClientFactory clientFactory;

	public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
		this.clientFactory = clientFactory;
	}
    
    @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 {
        // 获取 ILoadBalancer 实例
		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));
        // 这里会返回一个 ClientHttpResponse 
        // ClientHttpResponse  里面封装了请求信息
        // RestTemplate 拿到后再去发请求
		return execute(serviceId, ribbonServer, request);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值