文章目录
本文仅是学习记录,如有错误,还请大佬们不吝指出.
SpringCloud组件之Ribbon使用及源码分析
一、什么是Ribbon?
Ribbon 是一个具有内置软件负载平衡器的进程间通信(远程过程调用)库。简单来说Ribbon就是一个客户端负载均衡器,其作用就是解析目标服务的可调用的服务列表,基于负载均衡算法来实现请求的分发。
二、Ribbon的基本使用
1.测试代码编写
编写一个简单的user服务和一个order服务来测试,通过user服务来远程调用order服务获取用户订单信息
order服务
@RestController
public class OrderService {
@Value("${server.port}")
private String port;
@GetMapping("/orders")
public String getAllOrder(){
System.err.println("order service port : " + port);
return "return all order..";
}
}
order服务配置文件
server:
port: 9000
spring:
application:
name: order-service
user服务
@RestController
public class UserController{
@Autowired
private RestTemplate restTemplate;
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(RestTemplateBuilder restTemplateBuilder){
return restTemplateBuilder.build();
}
@GetMapping("/user/{userId}")
public String getAllOrder(@PathVariable("userId") String userId){
//调用订单服务查询用户订单信息
String result = restTemplate.getForObject("http://order-service/orders", String.class);
return "userId: " + userId + " order info : " + result;
}
}
user服务配置文件
server:
port: 8000
spring:
application:
name: user-service
# 配置order服务的提供者的地址列表,这里就相当于是注册中心中的生产者的信息
order-service:
ribbon:
listOfServers: localhost:9000,localhost:9100
2.结果测试
访问用户服务接口测试
这里我调用了两次,可以看出在user服务调用order服务时,是负载均衡调用的
三、Ribbon源码分析
1.@LoadBalanced注解
为什么仅仅加了一个LoadBalanced注解就实现了负载均衡,来看一下LoadBalanced注解
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
从LoadBalanced中可以看出,除了元注解信息,就只有一个Qualifier注解,这个Qualifier就相当于是一个标记,用来标识当前的RestTemplate,在LoadBalancerAutoConfiguration自动装配类中会去解析LoadBalanced注解,在这里面会获取到所有加了LoadBalanced注解的RestTemplate的Bean对象
@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();
2.LoadBalancerAutoConfiguration自动装配
那么获取到这些加了**@LoadBalanced**注解的RestTemplate有什么作用?在LoadBalancerAutoConfiguration自动装配类中,获取到restTemplates后,对每个restTemplate做了一个包装,增加了一个拦截器
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
//遍历restTemplates,对每个restTemplate进行包装,其实就是对每个restTemplate都增加一个拦截器
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
//先获取默认的拦截器,再将loadBalancerInterceptor添加进拦截器链
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
现在已经将所有添加了LoadBalanced注解的RestTemplate添加了一个拦截器,所有当user服务在调用order服务的时候一定会进入拦截器
restTemplate.getForObject("http://order-service/orders", String.class);
3. LoadBalancerInterceptor拦截器
那么进入拦截器之后,拦截器又做了什么? 我们进到LoadBalancerInterceptor,
这里LoadBalancerInterceptor将请求拦截到之后,就委托给了LoadBalancerClient去执行,LoadBalancerClient是一个接口,最终由RibbonLoadBalancerClient实现类执行
private LoadBalancerClient 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();
Assert.state(serviceName != null,"Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName,this.requestFactory.createRequest(request, body, execution));
}
4.ILoadBalancer负载均衡器
我们接着看RibbonLoadBalancerClient.execute()方法,这里面做了两个操作,一个就是根据服务名称获取负载均衡器,第二个就是根据负载均衡器获得某个Server实例
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException {
//获取负载均衡器
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
//根据负载均衡器获得一个Server实例,这里的Server是从集群中获得的某个Server实例
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));
return execute(serviceId, ribbonServer, request);
}
我们先看获取负载均衡器是如何操作的,跟进方法可以看到在getInstance()实例化过程中,基于工厂模式通过服务名称,获得一个上下文环境,然后再通过type(ILoadBalancer.class)从上下文环境中得到一个ILoadBalancer 的实例,这里getInstance()的时候还做了一个操作就是基于SpringClientFactory初始化了RibbonClientConfiguration类
public SpringClientFactory() {
super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
}
public <C> C getInstance(String name, Class<C> type) {
C instance = super.getInstance(name, type);
if (instance != null) {
return instance;
}
IClientConfig config = getInstance(name, IClientConfig.class);
return instantiateWithConfig(getContext(name), type, config);
}
public <T> T getInstance(String name, Class<T> type) {
AnnotationConfigApplicationContext context = this.getContext(name);
return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type).length > 0 ? context.getBean(type) : null;
}
ILoadBalancer 的类继承实现关系图
这里BEBUG可以看出,就构造出了一个实例ZoneAwareLoadBalancer
5.服务列表更新与获取
拿到了负载均衡器后,紧接着看第二个操作,getServer(loadBalancer, hint),其中chooseServer()就是基于负载均衡算法选择集群中的某个服务节点
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
if (loadBalancer == null) {
return null;
}
// 这里 default 就是采用默认的负载均衡算法,
return loadBalancer.chooseServer(hint != null ? hint : "default");
}
//截取ZoneAwareLoadBalancer类chooseServer方法中部分代码
if (server != null) {
return server;
} else {
logger.debug("Zone avoidance logic is not invoked.");
//这里调用了父类的chooseServer方法
return super.chooseServer(key);
}
protected IRule rule = DEFAULT_RULE;
//根据不同key选择相应的负载均衡规则,再选择出服务节点
public Server chooseServer(Object key) {
if (this.counter == null) {
this.counter = this.createCounter();
}
this.counter.increment();
if (this.rule == null) {
return null;
} else {
try {
//调用PredicateBasedRule中的choose(key)方法
return this.rule.choose(key);
} catch (Exception var3) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", new Object[]{this.name, key, var3});
return null;
}
}
}
public Server choose(Object key) {
ILoadBalancer lb = getLoadBalancer();
//这里lb.getAllServers()获取了所有服务列表信息,再对集群中的order-servier服务进行轮训筛选
Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
if (server.isPresent()) {
return server.get();
} else {
return null;
}
}
从上面choose方法中可以看到调用了lb.getAllServers() 方法来获取所有的服务列表信息,那么这里到底是如何获得服务列表信息的嘞? 我们进入到getAllServers()方法中
/**
* @return All known servers, both reachable and unreachable.
*/
// ILoadBalancer 接口类中的一个方法
public List<Server> getAllServers();
@Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL)
protected volatile List<Server> allServerList = Collections.synchronizedList(new ArrayList<Server>());
@Monitor(name = PREFIX + "UpServerList", type = DataSourceType.INFORMATIONAL)
protected volatile List<Server> upServerList = Collections.synchronizedList(new ArrayList<Server>());
@Override
public List<Server> getAllServers() {
return Collections.unmodifiableList(allServerList);
}
可以看出getAllServers()方法操作了一个allServerList变量,并返回一个不可修改的服务列表,这里的allServerList就是客户端用来缓存服务列表信息的,那具体allServerList是什么时候被赋值得嘞?这时候我们就要回到ILoadBalancer实例初始化的时候了,我们回到RibbonClientConfiguration中ZoneAwareLoadBalancer被初始化的地方
@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);
}
当子类ZoneAwareLoadBalancer被初始化的时候会去调用父类的构造方法,进入父类DynamicServerListLoadBalancer
public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
ServerList<T> serverList, ServerListFilter<T> filter,
ServerListUpdater serverListUpdater) {
super(clientConfig, rule, ping);
this.serverListImpl = serverList;
this.filter = filter;
this.serverListUpdater = serverListUpdater;
if (filter instanceof AbstractServerListFilter) {
((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
}
restOfInit(clientConfig);
}
从这里我们可以看出在父类初始化的时候调用了一个restOfInit()方法,在这个方法中有两个操作方法enableAndInitLearnNewServersFeature()和updateListOfServers()
void restOfInit(IClientConfig clientConfig) {
boolean primeConnection = this.isEnablePrimingConnections();
// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
this.setEnablePrimingConnections(false);
enableAndInitLearnNewServersFeature();
updateListOfServers();
if (primeConnection && this.getPrimeConnections() != null) {
this.getPrimeConnections()
.primeConnections(getReachableServers());
}
this.setEnablePrimingConnections(primeConnection);
LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}
我们先看enableAndInitLearnNewServersFeature()方法,这个方法的作用就是启动一个任务定时去更新服务列表信息
public void enableAndInitLearnNewServersFeature() {
LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());
//这里开启一个updateAction的任务
serverListUpdater.start(updateAction);
}
//updateAction任务
protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {
@Override
public void doUpdate() {
updateListOfServers();
}
};
//由于服务列表信息是配置在本地的我们进入到PollingServerListUpdater实现类
@Override
public synchronized void start(final UpdateAction updateAction) {
if (isActive.compareAndSet(false, true)) {
final Runnable wrapperRunnable = new Runnable() {
@Override
public void run() {
if (!isActive.get()) {
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
return;
}
try {
updateAction.doUpdate();
lastUpdated = System.currentTimeMillis();
} catch (Exception e) {
logger.warn("Failed one update cycle", e);
}
}
};
//使用scheduled来做定时任务调用
scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
//定时更新客户端缓存中的服务列表信息的任务
wrapperRunnable,
//任务延迟1s执行
initialDelayMs,
//会先读取配置文件中的ServerListRefreshInterval的信息,没有配置就默认30s执行一次
refreshIntervalMs,
TimeUnit.MILLISECONDS
);
} else {
logger.info("Already active, no-op");
}
}
//获取服务列表刷新间隔时间
private static long getRefreshIntervalMs(IClientConfig clientConfig) {
return clientConfig.get(CommonClientConfigKey.ServerListRefreshInterval, LISTOFSERVERS_CACHE_REPEAT_INTERVAL);
}
到这里其实可以看到,最终执行的任务也是updateListOfServers() 目的就是不断更新客户端缓存中的服务列表信息
@VisibleForTesting
public void updateListOfServers() {
List<T> servers = new ArrayList<T>();
if (serverListImpl != null) {
//读取配置文件中配置的服务列表信息
servers = serverListImpl.getUpdatedListOfServers();
LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers);
if (filter != null) {
servers = filter.getFilteredListOfServers(servers);
LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers);
}
}
//将读取到的服务列表信息设置到客户端缓存中,也就是将值赋值给BaseLoadBalancer中的 allServerList
updateAllServerList(servers);
}
//读取配置文件中配置的服务列表信息
@Override
public List<Server> getUpdatedListOfServers() {
String listOfServers = clientConfig.get(CommonClientConfigKey.ListOfServers);
return derive(listOfServers);
}
protected void updateAllServerList(List<T> ls) {
//···
setServersList(ls);
//···
}
//DynamicServerListLoadBalancer
public void setServersList(List lsrv) {
//这里调用父类进行赋值
super.setServersList(lsrv);
//···
}
//BaseLoadBalancer中进行赋值操作
public void setServersList(List lsrv) {
//···
allServerList = allServers;
//···
}
6.负载均衡获取服务节点信息
到这里我们已经获取到了服务列表信息,接下来就是基于负载均衡算法,获取某个服务节点
public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) {
//获取服务节点信息
List<Server> eligible = getEligibleServers(servers, loadBalancerKey);
if (eligible.size() == 0) {
return Optional.absent();
}
//这里是轮训算法实现,将服务节点数量传进去,这里incrementAndGetModulo(eligible.size())就是算出下一个服务节点的下标,eligible.get(index),返回目标服务节点
return Optional.of(eligible.get(incrementAndGetModulo(eligible.size())));
}
//轮训算法实现
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = nextIndex.get();
int next = (current + 1) % modulo;
if (nextIndex.compareAndSet(current, next) && current < modulo)
return current;
}
}
List<Server> eligible
获取的服务节点信息,以及通过轮训计算后获得的order-service服务节点信息
7.重构请求Url
拿到服务节点信息之后,最后一个操作就是重构请求 Url ,将http://order-service/orders请求地址重构为服务节点信息对应的地址http://localhost:9100/orders
@Override
public ListenableFuture<ClientHttpResponse> intercept(final HttpRequest request, final byte[] body, final AsyncClientHttpRequestExecution execution) throws IOException {
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 {
//ServiceRequestWrapper中进行了请求地址的重构
HttpRequest serviceRequest = new ServiceRequestWrapper(request,instance, AsyncLoadBalancerInterceptor.this.loadBalancer);
return execution.executeAsync(serviceRequest, body);
}
});
}
//请求重构方法
@Override
public URI getURI() {
URI uri = this.loadBalancer.reconstructURI(this.instance, getRequest().getURI());
return uri;
}
四、总结
从整个流程上来看,Ribbon 先获取所有带 @LoadBalanced 注解的RestTemplate对象,并对其增加一个拦截器对其增强,当客户端发起请求的时候,会被拦截器拦截。并委托给了LoadBalancerClient去执行,之后由实现类SpringClientFactory去获取负载均衡器ILoadBalancer(接口) ,在获取负载均衡器ZoneAwareLoadBalancer(实现类)的时候会去调用父类DynamicServerListLoadBalancer的构造方法,其中的restOfInit()的方法会被执行,该方法会去创建一个schedule,该任务会定时去拉取最新的服务端的列表信息同步至客户端缓存,之后服务端的服务状态信息会通过IPing不同的实现策略来定时检测服务端服务是否有效。如果失效会被剔除。在获取到服务端的有效列表之后,会通过负载均衡规则IRule来对服务进行筛选从而从集群中获得某个具体的Server实例,获得Server实例后会去重构请求Url,将Url中的服务名称替换为具体的Server服务地址,最后向目标服务发送请求.