Ribbon源码之LoadBalancerInterceptor

  • Post author:
  • Post category:其他



一 LoadBalancerInterceptor源码

/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.client.loadbalancer;

import java.io.IOException;
import java.net.URI;

import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.Assert;

/**
* @author Spencer Gibb
* @author Dave Syer
* @author Ryan Baxter
* @author William Tran
*/
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();
        Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
    }
}


二 LoadBalancerInterceptor源码分析

在该拦截器中注入了LoadBalancerClient的实现。当一个被@LoadBalanced注解修饰的RestTemplate对象对外发起HTTP请求时,会被LoadBalancerInterceptor类的intercept函数所拦截。由于我们在使用RestTemplate时采用了服务名作为host,所以直接从HttpRequest的URI对象getHost就可以拿到服务名,然后调用execute函数去根据服务名来选择实例并发起实际的请求。


三 RibbonLoadBalancerClient类源码

public class RibbonLoadBalancerClient implements LoadBalancerClient {
    @Override
    public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
        ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
        Server server = getServer(loadBalancer);
        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);
    }
}


四 Ribbon-LoadBalancerClient源码分析

LoadBalancerClient还只是一个抽象的负载均衡器接口,它的具体实现类是Ribbon-LoadBalancerClient

从execute函数的具体实现中,第一步就是通过getServer根据传入的服务名serviceId去获得具体服务实例,所以关键是getServer函数的实现。


五 getServer函数实现

    protected Server getServer(ILoadBalancer loadBalancer) {
        if (loadBalancer == null) {
            return null;
        }
        return loadBalancer.chooseServer("default"); // TODO: better handling of key
    }


六 getServer函数源码分析

通过getServer函数的实现源码,我们可以看到获取具体服务实例的时候,使用了Netflix Ribbon自身的ILoadBalancer接口中定义的chooseServer函数。


七 ILoadBalancer接口源码实现

public interface ILoadBalancer {
    public void addServers(List<Server> newServers);
    public Server chooseServer(Object key);
    public void markServerDown(Server server);
    public List<Server> getReachableServers();
    public List<Server> getAllServers();
}


八 ILoadBalancer接口源码分析

该接口中定义了一个客户端负载均衡器需要的一系列抽象操作

1 addServers:向负载均衡器中维护的实例列表中增加服务实例。

2 chooseServer:通过某种策略,从负载均衡器中挑选出一个具体的服务实例。

3 markServerDown:用来通知和标识负载均衡器中某个具体实例已经停止服务,不然负载均衡器在下一次获取服务实例清单都会认为该服务实例时正常的。

4 getReachableServers:获取当前服务的实例列表。

5 getAllServers:获取所有已经的服务实例列表,包括正常服务和停止服务的实例。

在该接口定义中涉及Server对象定义时一个传统的服务端节点,在该类中存储了服务端节点的一些元数据信息,包括host、port一些部署信息等。


九 ILoadBalancer接口类图

类图如下:

BaseLoadBalancer类实现了基础的负载均衡,而DynamicServerListLoadBalancer和ZoneAwareLoadBalancer在负载均衡的策略上做了一些功能的扩展。



版权声明:本文为chengqiuming原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。