spring cloud Ribbon 源码分析

前两文写了ribbon的根据传参进行自定义负载均衡,今天要解释一下,为什么能够这样做。将这几天的源码查看,做一个记录,以备日后分享。

当我们使用了@LoadBalanced注解后,就具有了负载均衡的能力。现在开始解密之旅。

一,@LoadBalanced

@LoadBalanced
@bean
public RestTemplate restTemplate() {
   return new RestTemplate();
}

发生了一些神秘的变化,一切开始不同。

看一下这个注解

/**
 * 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 {

}

注释说,使restTemplate 使用了LoadBalancerClient。重点说明一下已集成了@Qualifier注解,这说明LoadBalanced注解也具有了排重的能力。使用者可以通过LoadBalanced注解或@Qualifier,找到包含此注解的对象。

我们通过Alt+f7看下哪里使用了@LoadBalanced注解。
在loadBalancerAutoConfiguration类里找到了

二,loadBalancerAutoConfiguration

public class LoadBalancerAutoConfiguration {

	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();
。。。

这个是指,将所有带有@LoadBalanced(内置@Qualifier)的bean 装配过来。

再看LoadBalancerAutoConfiguration的静态内部类LoadBalancerInterceptorConfig

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

		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}

	}

可以看到restTemplateCustomizer方法,将上文中restTemplate增加一个拦截器loadBalancerInterceptor。

那我们看下loadBalancerInterceptor类具体做了什么。

三,loadBalancerInterceptor

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

	private LoadBalancerClient loadBalancer;

	private LoadBalancerRequestFactory requestFactory;
	。。。。
	@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));
	}
}

在此拦截器中,使用this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution))。可以看到是查出serviceName,如service1。交由loadBalancer.execute执行。
那么就要看LoadBalancerClient类了

四,LoadBalancerClient

LoadBalancerClient是个接口,那么具体是使用哪个类做的bean呢,我们在自动装配类xxAutoConfiguration找一下。
果然,在RibbonAutoConfiguration类中找到该bean

	@Bean
	@ConditionalOnMissingBean(LoadBalancerClient.class)
	public LoadBalancerClient loadBalancerClient() {
		return new RibbonLoadBalancerClient(springClientFactory());
	}

所以,在不存在其他LoadBalancerClient的情况下,使用的是RibbonLoadBalancerClient。

五,RibbonLoadBalancerClient

这是一个今天的重点类之一

public class RibbonLoadBalancerClient implements LoadBalancerClient {

	private SpringClientFactory clientFactory;
	。。。
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
			//获取一个loadBalancer
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
		//获取真正的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);
	}
	。。。
	//从LoadBalancer里挑选server
	protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
		if (loadBalancer == null) {
			return null;
		}
		// Use 'default' on a null hint, or just pass it on?
		return loadBalancer.chooseServer(hint != null ? hint : "default");
	}

其中这行代码 ILoadBalancer loadBalancer = getLoadBalancer(serviceId);会通过一个工厂获取到一个ILoadBalancer,其中代码较多,不是本次重点。先知道可以获取一个已注册的ILoadBalancer即可。
那么究竟是哪个ILoadBalancer的实现呢?再次使用alt+f7,找xxautoConfiguration类。发现生成在RibbonClientConfiguration类。

六,RibbonClientConfiguration

public class RibbonClientConfiguration {
@Bean
	@ConditionalOnMissingBean
	public IClientConfig ribbonClientConfig() {
		DefaultClientConfigImpl config = new DefaultClientConfigImpl();
		config.loadProperties(this.name);
		config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
		config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
		config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
		return config;
	}

	@Bean
	@ConditionalOnMissingBean
	public IRule ribbonRule(IClientConfig config) {
		if (this.propertiesFactory.isSet(IRule.class, name)) {
			return this.propertiesFactory.get(IRule.class, config, name);
		}
		ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
		rule.initWithNiwsConfig(config);
		return rule;
	}

	@Bean
	@ConditionalOnMissingBean
	public IPing ribbonPing(IClientConfig config) {
		if (this.propertiesFactory.isSet(IPing.class, name)) {
			return this.propertiesFactory.get(IPing.class, config, name);
		}
		return new DummyPing();
	}

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

此类中提供了ILoadBalancer,IRule,IClientConfig几个bean的默认实现。
其中IRule采用了ZoneAvoidanceRule,
ILoadBalancer最终采用了 ZoneAwareLoadBalancer。

那么回到loadBalancer.chooseServer(hint != null ? hint : “default”),我们就要看一下,ZoneAwareLoadBalancer里的具体实现。

七,ZoneAwareLoadBalancer

public class ZoneAwareLoadBalancer<T extends Server> extends DynamicServerListLoadBalancer<T> {
 @Override
    public Server chooseServer(Object key) {
        if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
            logger.debug("Zone aware logic disabled or there is only one zone");
            return super.chooseServer(key);
        }
        。。。

看到默认只有一个zone时,转给了父类 super.chooseServer(key);
进入父类BaseLoadBalancer

八,BaseLoadBalancer

public class BaseLoadBalancer extends AbstractLoadBalancer implements
        PrimeConnections.PrimeConnectionListener, IClientConfigAware {
        protected IRule rule = DEFAULT_RULE;
        。。。
        public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
            //通过Irule和key选择server
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }
    ....

如此便进入了Irule里。
上文我们说到,在RibbonClientConfiguration里,默认装配了一个Irule,即ZoneAvoidanceRule。所以,此时我们就要看ZoneAvoidanceRule里的实现了。

九,ZoneAvoidanceRule

public class ZoneAvoidanceRule extends PredicateBasedRule {
	private static final Random random = new Random();
    
    private CompositePredicate compositePredicate;
	public ZoneAvoidanceRule() {
        super();
        ZoneAvoidancePredicate zonePredicate = new ZoneAvoidancePredicate(this);
        AvailabilityPredicate availabilityPredicate = new AvailabilityPredicate(this);
        compositePredicate = createCompositePredicate(zonePredicate, availabilityPredicate);
    }
    
    private CompositePredicate createCompositePredicate(ZoneAvoidancePredicate p1, AvailabilityPredicate p2) {
        return CompositePredicate.withPredicates(p1, p2)
                             .addFallbackPredicate(p2)
                             .addFallbackPredicate(AbstractServerPredicate.alwaysTrue())
                             .build();
        
    }
    。。。
    @Override
    public AbstractServerPredicate getPredicate() {
        return compositePredicate;
    }    

继承自PredicateBasedRule,目前我们仅需关注最后一个方法,getPredicate(),这是一个重写方法。
而ZoneAvoidanceRule中并没有实现choose()方法,所以我们需要往父类PredicateBasedRule 查找

十,PredicateBasedRule

public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule {
    public abstract AbstractServerPredicate getPredicate();
    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
        if (server.isPresent()) {
            return server.get();
        } else {
            return null;
        }       
    }
}

这是一个抽象类,确实有我们要找的choose(key)方法,可以注意到,getPredicate()是一个抽象方法,由子类提供实现。
Optional server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
注意这句里面的lb.getAllServers(),这行代码很显然是要获取到所有server,然后再使用compositePredicate.chooseRoundRobinAfterFiltering选择一个server。
chooseRoundRobinAfterFiltering中实现了依次选取server的逻辑。

到这里,Irule路由选择server的事情已经说完了。

----------------中场休息分割线--------------

下一个问题就是server从哪来?

显然是这里,lb.getAllServers(),那么lb我们上文说到,默认使用的是ZoneAwareLoadBalancer,而ZoneAwareLoadBalancer的继承关系是:ZoneAwareLoadBalancer–>DynamicServerListLoadBalancer–>BaseLoadBalancer–>AbstractLoadBalancer.

getAllServers()方法定义在父类BaseLoadBalancer中,我们再来看BaseLoadBalancer

十一,BaseLoadBalancer

public class BaseLoadBalancer extends AbstractLoadBalancer implements
        PrimeConnections.PrimeConnectionListener, IClientConfigAware {
        
            @Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL)
    protected volatile List<Server> allServerList = Collections
            .synchronizedList(new ArrayList<Server>());
@Override
    public List<Server> getAllServers() {
        return Collections.unmodifiableList(allServerList);
    }
    ...

可以看到BaseLoadBalancer仅仅是将allServerList返回,那么什么时候初始化和更新的呢,我们看下子类DynamicServerListLoadBalancer

十二,DynamicServerListLoadBalancer

这又是今天的重点类。

public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {
volatile ServerList<T> serverListImpl;
//构造方法,注意最后调用了restOfInit(clientConfig)
 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);
    }
	//同样在 initWithNiwsConfig中调用了restOfInit(clientConfig)
    public DynamicServerListLoadBalancer(IClientConfig clientConfig) {
        initWithNiwsConfig(clientConfig);
    }
   
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        try {
            super.initWithNiwsConfig(clientConfig);
            String niwsServerListClassName = clientConfig.getPropertyAsString(
                    CommonClientConfigKey.NIWSServerListClassName,
                    DefaultClientConfigImpl.DEFAULT_SEVER_LIST_CLASS);

            ServerList<T> niwsServerListImpl = (ServerList<T>) ClientFactory
                    .instantiateInstanceWithClientConfig(niwsServerListClassName, clientConfig);
            this.serverListImpl = niwsServerListImpl;

            if (niwsServerListImpl instanceof AbstractServerList) {
                AbstractServerListFilter<T> niwsFilter = ((AbstractServerList) niwsServerListImpl)
                        .getFilterImpl(clientConfig);
                niwsFilter.setLoadBalancerStats(getLoadBalancerStats());
                this.filter = niwsFilter;
            }

            String serverListUpdaterClassName = clientConfig.getPropertyAsString(
                    CommonClientConfigKey.ServerListUpdaterClassName,
                    DefaultClientConfigImpl.DEFAULT_SERVER_LIST_UPDATER_CLASS
            );

            this.serverListUpdater = (ServerListUpdater) ClientFactory
                    .instantiateInstanceWithClientConfig(serverListUpdaterClassName, clientConfig);

            restOfInit(clientConfig);
        } catch (Exception e) {
            throw new RuntimeException(
                    "Exception while initializing NIWSDiscoveryLoadBalancer:"
                            + clientConfig.getClientName()
                            + ", niwsClientConfig:" + clientConfig, e);
        }
    }
//重点方法,实现了多个重要功能
    void restOfInit(IClientConfig clientConfig) {
        boolean primeConnection = this.isEnablePrimingConnections();
        // turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
        this.setEnablePrimingConnections(false);
        //启动定时器,默认30s执行依次updateListOfServers(),用来更新serverList。默认使用PollingServerListUpdater
        enableAndInitLearnNewServersFeature();
		//调用内部方法,更新serverList
        updateListOfServers();
        if (primeConnection && this.getPrimeConnections() != null) {
            this.getPrimeConnections()
                    .primeConnections(getReachableServers());
        }
        this.setEnablePrimingConnections(primeConnection);
        LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
    }
    //更新serverList
     @VisibleForTesting
    public void updateListOfServers() {
        List<T> servers = new ArrayList<T>();
        if (serverListImpl != null) {
        //调用serverListImpl中的getUpdatedListOfServers实现更新
            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);
            }
        }
        updateAllServerList(servers);
    }
    
    @Override
    public void setServersList(List lsrv) {
        super.setServersList(lsrv);
        List<T> serverList = (List<T>) lsrv;
        Map<String, List<Server>> serversInZones = new HashMap<String, List<Server>>();
        for (Server server : serverList) {
            // make sure ServerStats is created to avoid creating them on hot
            // path
            getLoadBalancerStats().getSingleServerStat(server);
            String zone = server.getZone();
            if (zone != null) {
                zone = zone.toLowerCase();
                List<Server> servers = serversInZones.get(zone);
                if (servers == null) {
                    servers = new ArrayList<Server>();
                    serversInZones.put(zone, servers);
                }
                servers.add(server);
            }
        }
        setServerListForZones(serversInZones);
    }


我们来看下流程,首先两个构造方法调用了restOfInit()方法,在其中执行了updateListOfServers()方法,在其中调用了一个serverListImpl获取了所有servers,然后又调用了updateAllServerList(servers),在updateAllServerList(servers)方法中调用setServersList(List lsrv)方法,最终在其中调用父类方法super.setServersList(lsrv),将获得到的servers,都传给了父类,由父类加载进allServerList等列表,等待开头getAllServers()的调用。
这样默认Loadbalancer即ZoneAwareLoadBalancer,就将自己获取到的serverList贡献给,ZoneAvoidanceRule中lb.getServers()使用了,再由ZoneAvoidanceRule提供的路由规则选取一个,返回给RibbonLoadBalancerClient,并在其中进行名称到ip+port的转换。这样一个完美的robbin负载均衡就走完了。

刚才为了梳理流程,我跳过的一个细节,DynamicServerListLoadBalancer中serverListImpl获取server的具体过程。
serverListImpl是通过构造函数注入进来的,那就要看下有没有相应的实现呢?

十三,serverListImpl的具体实现

在 RibbonClientConfiguration我找到了这样一个默认bean

@Bean
@ConditionalOnMissingBean
@SuppressWarnings("unchecked")
public ServerList<Server> ribbonServerList(IClientConfig config) {
	if (this.propertiesFactory.isSet(ServerList.class, name)) {
		return this.propertiesFactory.get(ServerList.class, config, name);
	}
	ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
	serverList.initWithNiwsConfig(config);
	return serverList;
}

可以看到,默认使用的ConfigurationBasedServerList,产生serverList,这个类允许使用配置文件产生serverList,如在yml中定义:

spring.application.name=shop-order
shop-product.ribbon.listOfServers=http://localhost:8001,http://localhost:8002

就会解析成具有8001,8002的两个server的serverList。

终于走完啦!

十四,最终流程梳理

首先,在loadBalancerAutoConfiguration中,所有被@LoadBalanced注释的restTemplate bean都会被加入loadBalancerInterceptor,并在loadBalancerInterceptor中对原请求做负载均衡转换。
其次,loadBalancerInterceptor使用默认提供的RibbonLoadBalancerClient执行serverList的获取。而在RibbonLoadBalancerClient中注入默认实现的IRule:ZoneAvoidanceRule进行负载均衡,使用ZoneAwareLoadBalancer作为默认的loadbalancer,获取所有的由ZoneAwareLoadBalancer从默认yml配置读过来的serverList。
最后由DynamicServerListLoadBalancer中引入的PollingServerListUpdater,负责对serverList的定时更新。

综上,由loadBalancerInterceptor插入拦截,由RibbonLoadBalancerClient封装调用IRule和loadBalancer,由loadBalancer的实现类ZoneAwareLoadBalancer负责从配置中读取的serverList,由IRule的实现类ZoneAvoidanceRule执行选择server。一个完整的robbin负载均衡流程就结束了。

十五,可扩展性

由于所有bean在xxAutoConfiguration中都是@ConditionalOnMissingBean类型的,所以可扩展性非常好。
比如引入NocosClient的时候,ServerList就被置换为Nocos的ServerList实现,从NacosServer获取列表。而引入Eureka client便会从Eureka获取服务列表。
再比如我们可以自定义路由方式,只需要添加我们自己的IRule类型的bean。正如我上篇文章ribbon按参数路由,实现灰度发布功能
可扩展性非常强。

码字不易,请给点赞哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值