
图源:
在中我们介绍了如何使用 Eureka 作为注册中心,并且对注册中心里的服务使用 RestTemplate 做负载均衡调用。本篇文章我们来看负载均衡调用是如何实现的,以及如何设置其中的调度规则。
源码分析
先启动项目的所有模块,其中 shopping-order 模块用 debug 模式启动。
然后在RestTemplate调用接口的地方打上断点,进行调试,一步步查看负载均衡是在何时发生的。

最终我们会定位到一个InterceptingClientHttpRequest类:

这个类的execute方法会从iterator中获取一个ClientHttpRequestInterceptor对象,并执行该对象的intercept方法执行实际的接口调用并返回结果。

查看变量监控不难发现,迭代器中的元素是一个LoadBalanceInterceptor对象。

LoadBalanceInterceptor.intercept方法中,会先通过原始的 Url 地址originalUri(这里是http://shopping-user/user/1)获取服务名serviceName(这里是shopping-user),然后再调用this.loadBalancer.execute执行真正的接口调用。

this.loadBalancer的类型是BlockingLoadBalancerClient。
在BlockingLoadBalancerClient.execute方法中,最关键的一步是调用choose并传入服务名,返回一个ServiceInstance对象,这个对象代表一个具体的接口服务实例(在我们这个示例中,就是子模块 shopping-order 的两个已启动实例的其中之一)。换言之,这个choose方法是真正负责处理的“负载均衡”代码,它会从 Eureka 中查询服务名关联的服务实例,并按规则从中挑选一个实例用于后续的具体接口调用。

BlockingLoadBalancerClient.choose方法中,先通过loadBalancerClientFactory.getInstance方法获取一个ReactiveLoadBalancer对象,这个对象代表负载均衡的规则。

默认的规则是RoundRobinLoadBalancer,所以这里的loadBalancer是这个类型。

ReactiveLoadBalancer是一个接口,RoundRobinLoadBalancer是一个实现,它们的继承关系如下:

可以看到,还有另一个实现RandomLoadBalancer。
从名称就可以看出来,RoundRobinLoadBalancer是轮询策略,而RandomLoadBalancer是乱序(随机)策略。
总结
总结一下,上述过程中负责负载均衡过程可以表示为:

BlockingLoadBalancerClient用于处理阻塞式的负载均衡调用,实际上 LoadBalancer 还可以处理响应式的负载均衡调用。早期的 Spring Cloud 和 Eureka 客户端使用 ribbon 作为负载均衡组件。教新版本的 Spring Cloud 中可以通过配置
spring.cloud.ribbon.enabled控制是否使用 ribbon。
负载均衡策略
Spring Cloud 默认使用的负载均衡策略为轮询,对应RoundRobinLoadBalancer这个实现类。我们可以将其切换为乱序(随机)策略:
(value = "shopping-user", configuration = CustomLoadBalancerConfiguration.class)
public class WebConfig {
// ...
}
这里的@LoadBalancerClient的value属性用于指定服务名,我们可以为不同的服务指定不同的负载均衡策略。configuration指定一个自定义负载均衡策略的类。
public class CustomLoadBalancerConfiguration {
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
在配置类中添加一个ReactorLoadBalancer类型的 bean 工厂方法,并实际返回一个RandomLoadBalancer对象。
除了这种简单方式外,还可以利用ServiceInstanceListSupplier类制造一些功能更复杂的负载均衡策略,相关内容可以阅读。
本文的完整示例代码见。
参考资料

文章评论