Spring Cloud Ribbon 原理解析

Spring Cloud Ribbon 原理解析

commons的视角下看待Ribbon

spring cloud 为我们提供了 负载均衡的统一客户端接口,commons 模块下。

public interface LoadBalancerClient extends ServiceInstanceChooser {

	
	<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

	
	<T> T execute(String serviceId, ServiceInstance serviceInstance,
			LoadBalancerRequest<T> request) throws IOException;

	
	URI reconstructURI(ServiceInstance instance, URI original);

}

ServiceInstanceChooser接口如下:

public interface ServiceInstanceChooser {

	/**
	 * 通过服务id查询具体的服务实例
	 * @param serviceId The service ID to look up the LoadBalancer.
	 * @return A ServiceInstance that matches the serviceId.
	 */
	ServiceInstance choose(String serviceId);

}

Ribbon 下是如何实现的

RibbonLoadBalancerClient 是其实现类

public class RibbonLoadBalancerClient implements LoadBalancerClient {
    //通过id查询服务实例
	@Override
	public ServiceInstance choose(String serviceId) {
		return choose(serviceId, null);
	}

	//具体通过调用getServer方法查询服务
	public ServiceInstance choose(String serviceId, Object hint) {
		Server server = getServer(getLoadBalancer(serviceId), hint);
		if (server == null) {
			return null;
		}
		return new RibbonServer(serviceId, server, isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));
	}

	@Override
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
			throws IOException {
		return execute(serviceId, request, null);
	}

	
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
        //也是调用getServer方法获取服务实例
		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);
	}
	protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
		if (loadBalancer == null) {
			return null;
		}
		// 通过调用loadBalancer  选择服务,所以我们的 负载均衡的实质能力,就是 loadBalancer给我们提供的
		return loadBalancer.chooseServer(hint != null ? hint : "default");
	}

	protected ILoadBalancer getLoadBalancer(String serviceId) {
		return this.clientFactory.getLoadBalancer(serviceId);
	}

}

LoadBalancer 中

  public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
                //具体的能力负载均衡能力交给了rule  为我们选择一个服务
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }

IRule接口定义如下

public interface IRule{
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     * 
     * @return choosen Server object. NULL is returned if none
     *  server is available 
     */

    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

我们可以挑选一个IRule的具体显现类来看一下

public class RandomRule extends AbstractLoadBalancerRule {

    /**
     * Randomly choose from all living servers
     */
    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        Server server = null;

        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> upList = lb.getReachableServers();
            List<Server> allList = lb.getAllServers();

            int serverCount = allList.size();
            if (serverCount == 0) {
                /*
                 * No servers. End regardless of pass, because subsequent passes
                 * only get more restrictive.
                 */
                return null;
            }

            int index = chooseRandomInt(serverCount);
            server = upList.get(index);

            if (server == null) {
                /*
                 * The only time this should happen is if the server list were
                 * somehow trimmed. This is a transient condition. Retry after
                 * yielding.
                 */
                Thread.yield();
                continue;
            }

            if (server.isAlive()) {
                return (server);
            }

            // Shouldn't actually happen.. but must be transient or a bug.
            server = null;
            Thread.yield();
        }

        return server;

    }

    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }

	@Override
	public Server choose(Object key) {
		return choose(getLoadBalancer(), key);
	}

	@Override
	public void initWithNiwsConfig(IClientConfig clientConfig) {
		// TODO Auto-generated method stub
		
	}
}

到此为止,我们就通过负载均衡,获取到了一个具体的服务实例。

我们是如何将负载均衡能力整合进我们的系统中的

但是还有一个问题摆在我们面前,我们确实有了client端了,但是我们是从哪里调用该client并使用她为我们提供服务能力的呢?

那就是RestTemplate。

public class LoadBalancerAutoConfiguration {

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

	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
			final List<RestTemplateCustomizer> customizers) {
		return new SmartInitializingSingleton() {
			@Override
			public void afterSingletonsInstantiated() {
				for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
					for (RestTemplateCustomizer customizer : customizers) {
						customizer.customize(restTemplate);
					}
				}
			}
		};
	}

	@Configuration
	@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
	static class LoadBalancerInterceptorConfig {
		@Bean
		public LoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}
		//RestTemplateCustomizer的作用就是将LoadBalancerInterceptor 拦截器,加入到 restTemplate 中
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return new RestTemplateCustomizer() {
				@Override
				public void customize(RestTemplate restTemplate) {
					List<ClientHttpRequestInterceptor> list = new ArrayList<>(
							restTemplate.getInterceptors());
					list.add(loadBalancerInterceptor);
					restTemplate.setInterceptors(list);
				}
			};
		}
	}
}

到此为止我们明白了,我们在实现具体网络请求之前,会经过一个拦截器,LoadBalancerInterceptor,那在该拦截器中具体做了什么操作呢?

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);
        //会调用我们详细讨论的client 进行服务实例的选取
		return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
	}
}

最终,经过层层调用,最终我们走完了全流程。

Ribbon 是如何和 nacos的交合

就是在Nacos 为我们提供的nacos能力。通过使用 nacosServiceManager 使用 NamingService 获取服务列表的。

public class NacosRule extends AbstractLoadBalancerRule {

   private static final Logger LOGGER = LoggerFactory.getLogger(NacosRule.class);

   @Autowired
   private NacosDiscoveryProperties nacosDiscoveryProperties;

   @Autowired
   private NacosServiceManager nacosServiceManager;

   @Override
   public Server choose(Object key) {
      try {
         String clusterName = this.nacosDiscoveryProperties.getClusterName();
         String group = this.nacosDiscoveryProperties.getGroup();
         DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
         String name = loadBalancer.getName();

         NamingService namingService = nacosServiceManager
               .getNamingService(nacosDiscoveryProperties.getNacosProperties());
         List<Instance> instances = namingService.selectInstances(name, group, true);
         if (CollectionUtils.isEmpty(instances)) {
            LOGGER.warn("no instance in service {}", name);
            return null;
         }

         List<Instance> instancesToChoose = instances;
         if (StringUtils.isNotBlank(clusterName)) {
            List<Instance> sameClusterInstances = instances.stream()
                  .filter(instance -> Objects.equals(clusterName,
                        instance.getClusterName()))
                  .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(sameClusterInstances)) {
               instancesToChoose = sameClusterInstances;
            }
            else {
               LOGGER.warn(
                     "A cross-cluster call occurs,name = {}, clusterName = {}, instance = {}",
                     name, clusterName, instances);
            }
         }

         Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose);

         return new NacosServer(instance);
      }
      catch (Exception e) {
         LOGGER.warn("NacosRule error", e);
         return null;
      }
   }

   @Override
   public void initWithNiwsConfig(IClientConfig iClientConfig) {
   }

}

学习资料推广

我已经将springamqp 源码解析录制为视频上传到bibi,分为六个章节详细介绍了各个模块的具体内容

https://www.bilibili.com/video/BV1hN411Z7fn?share_source=copy_web

感兴趣的小伙伴可以看看
学习一下
录制不易,记得三联哦!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud是一个基于Spring Boot的开发工具集,提供了一系列用于构建分布式系统的解决方案。其中,Feign和RibbonSpring Cloud中常用的两个组件。 Feign是一个声明式的Web服务客户端,用于简化使用Spring Cloud的服务之间进行通信的过程。它通过支持注解方式来定义和使用服务接口,底层使用的是基于反射的动态代理技术,将接口和实际调用的服务进行映射。Feign通过集成Ribbon来实现负载均衡的功能。 Ribbon是一个负载均衡的组件,它根据一系列的负载均衡策略,从多个服务实例中选择一个要调用的实例。Ribbon通过监听Eureka注册中心上已注册的服务列表,并通过默认的轮询算法选择一个实例。在Feign中,Ribbon用于根据服务接口定义的URL和方法,选择一个具体的服务进行调用。 Feign和Ribbon原理可以简单总结如下:首先,Feign通过使用@EnableFeignClients注解开启Feign功能,扫描包中带有@FeignClient注解的接口定义。接着,Feign将这些接口定义转化为动态代理对象,在调用接口方法时,实际上是通过动态代理对象进行了解析和转发,最终会调用到具体的服务实例上。此时,Ribbon会根据一定的策略从多个服务实例中选择一个实例进行调用,并返回调用结果。整个调用过程是通过HTTP协议进行通信的。 总之,Spring Cloud中的Feign和Ribbon组件能够实现微服务之间的通信和负载均衡功能。Feign简化了基于HTTP的服务接口定义和调用的过程,而Ribbon则负责根据一定的负载均衡策略选择合适的服务实例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值