图源:
在中我们介绍了如何使用 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
类制造一些功能更复杂的负载均衡策略,相关内容可以阅读。
本文的完整示例代码见。
参考资料
文章评论