Spring Cloud之Ribbon原理,及RestTemplate启用负载的原理

Ribbon
        Ribbon是Netflix 公司开源的一个客户端的负载均衡组件,在Spring Cloud的微服务生态中, Ribbon主要有两种使用方式, 一种是和RestTemplate相结合,另一种是和Feign相结合, 这也是Spring Cloud微服务间最主要的调用方式。其中Feign底层已经默认集成了Ribbon。
        Netflix为Ribbon开发了很多子模块,不过目前可用于生产环境下的子模块主要有以下几个

  • ribbon-core:Ribbon 的核心API 。
  • ribbon-loadbalancer:可以独立使用或与其他模块一起使用的负载均衡器API 。
  • ribbon-eureka :ribbon结合Eureka客户端的API ,为负载均衡器提供动态服务注册列表信息。

Ribbon配置RestTemplate的简单使用,及自定义负载策略

@Configuration
public class RibbonConfig {

	@Bean
	@LoadBalanced
	public RestTemplate restTemplate () {
		return new RestTemplate();
	}
   
    // 修改负载策略,同时配置中也需要修改
    @Bean
    public IRule ribbonRule(){
        return new RandomRule();
    }
}
# 修改Ribbion的负载均衡策略
service-name: # 服务名
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule 


Ribbon原理简述
        1)服务列表获取:Ribbon负载的核心接口是LoadBalancerClient, LoadBalancerClient在初始化时会通过Eureka Client向Eureka服务端获取所有服务实例的注册信息,并且每10秒向EurekaClient 发送“ ping ”,来判断服务的可用性。如果服务的可用性发生了改变或者服务数量和之前的不一致,则更新或者重新拉取。
        相关源码如下:

public class BaseLoadBalancer extends AbstractLoadBalancer implements PrimeConnections.PrimeConnectionListener, IClientConfigAware {
    //...
    private final static IRule DEFAULT_RULE = new RoundRobinRule();
    protected IRule rule = DEFAULT_RULE;
    protected int pingIntervalSeconds = 10;
    public BaseLoadBalancer(String name, IRule rule, LoadBalancerStats stats,IPing ping, IPingStrategy pingStrategy) {	
       //...
        setupPingTask();
       //...
    }
    void setupPingTask() {
        //../
        lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + name,
                true);
        lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000);
        forceQuickPing();
    }
}

public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {
    public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,ServerListUpdater serverListUpdater) {
        //...        
        restOfInit(clientConfig);
    } 
    void restOfInit(IClientConfig clientConfig) {
        //...
        updateListOfServers();
        //...
    }
    public void updateListOfServers() {
        List<T> servers = new ArrayList<T>();
        if (serverListImpl != null) {
            servers = serverListImpl.getUpdatedListOfServers();
            //...
        }     
    }
}

public class DiscoveryEnabledNIWSServerList extends bstractServerList<DiscoveryEnabledServer>{
    @Override
    public List<DiscoveryEnabledServer> getUpdatedListOfServers(){
        return obtainServersViaDiscovery();
    }

    private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
       //...
       EurekaClient eurekaClient = eurekaClientProvider.get();
       //...
}

        2)负载算法:LoadBalancerClient的实现类为RibbonLoadBalancerClientRibbonLoadBalancerClient中的choose(..)会根据实例名,提取出对应的服务实例列表,然后交给ILoadBalancer去选择服务实例,集群环境下ILoadBalancer的实现类为ZoneAwareLoadBalancer,该类采用ZoneAvoidanceRule负载算法,即片区域加轮询的负载算法选择实例。  Ribbon默认提供了以下负载算法(即IRule的实现类): 

  • BestAvailableRule : 选择最小请求数。
  • ClientConfigEnabledRoundRobinRule:轮询。
  • RandornRule : 随机选择一个server 。
  • RoundRobinRule : 轮询选择server ,又分为换响应时间权重、和
  • RetryRule : 根据轮询的方式重试。
  • WeightedResponseTimeRule: 根据响应时间去分配一个weight , weight 越低,被选择的可能性就越低。
  • ZoneAvoidanceRule :根据server的zone区域和可用性来轮询选择。集群环境下默认为这种轮询方式,也就是按片区来轮询。     

      负载相关源码如下:

public class RibbonLoadBalancerClient implements LoadBalancerClient {
    //...
    public ServiceInstance choose(String serviceId, Object hint) {
		Server server = getServer(getLoadBalancer(serviceId), hint);
		//...
	}
    //...
    protected ILoadBalancer getLoadBalancer(String serviceId) {
        return this.clientFactory.getLoadBalancer(serviceId);
    }
}

public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {
    public ILoadBalancer getLoadBalancer(String name) {
        return getInstance(name, ILoadBalancer.class);
    }

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

        整体来说,Ribbon底层是通过Eureka Client获取服务实例列表并本地缓存,然后通过心跳检测服务可用性,当访问服务时,根据一定的负载算法获取服务实例。

为什么加上了@LoadBalance之就能使调用有负载均衡效果?

        原因在@LoadBalance注解的处理器LoadBalancerAutoConfiguration 当中,LoadBalancerAutoConfiguration 中维护了一个被@LoadBalanced 修饰的RestTemplate 对象的List。在初始化的过程中,通过调用customizer.customize(restTemplate)方
法来给RestTemplate增加拦截器LoadBalancerlnterceptor ,这样调用restTemplate的方法时就会先进入到LoadBalancerinterceptor中,在LoadBalancerlnterceptor 中实现了负载均衡的方法。源码如下:

Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

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

	@Autowired(required = false)
	private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
			for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
				for (RestTemplateCustomizer customizer : customizers) {
					customizer.customize(restTemplate);  //配置拦截器
				}
			}
		});
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值