范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Ribbon源码2Ribbon工作流程

  0. 环境nacos版本:1.4.1Spring Cloud : Hoxton.SR9Spring Boot :2.4.4Spring Cloud alibaba: 2.2.5.RELEASESpring Cloud openFeign 2.2.2.RELEASE
  测试代码:github.com/hsfxuebao/s…1. Ribbon的工作流程
  我们知道,微服务在启动成功之后,默认30s/次会从注册中心拉取服务注册表到本地缓存起来,而我们使用Ribbon时是通过RestTemplate发起请求,URL以:http://user-server/user/... 服务名方式去调用服务,其实Ribbon干的事情就是根据URL中的服务名去本地的服务注册表中查找服务名对应的服务实例(一个或多个),然后通过负载均衡算法选择其中一个服务后,发起Http请求,那接下来我们就来看一下Ribbon的底层到底是怎么工作的2. Ribbon配合RestTemplate使用
  首选需要定义RestTemplate@SpringBootApplication @EnableEurekaClient public class OrderServerApplication1030 {      //配置一个RestTemplate ,http客户端,支持Rest风格     //@LoadBalanced :负载均衡注册,让RestTmplate可以实现负载均衡请求     //这个标签标记RestTemplate可以使用LoadBalancerClient进行负载均衡     @Bean     @LoadBalanced     public RestTemplate restTemplate(){         return new RestTemplate();     }     //省略... } 复制代码
  调用服务的是否使用服务名调用:@RestController public class OrderController {      //需要配置成Bean     @Autowired     private RestTemplate  restTemplate ;      //浏览器调用该方法     @RequestMapping(value = "/order/{id}",method = RequestMethod.GET)     public User getById(@PathVariable("id")Long id){         //发送http请求调用 user的服务,获取user对象 : RestTemplate         //user的ip,user的端口,user的Controller路径         //String url = "http://localhost:1020/user/"+id;         String url = "http://user-server/user/"+id;          //发送http请求         return restTemplate.getForObject(url, User.class);      } } 复制代码
  被 @LoadBalanced标记的RestTemplate 可以使用LoadBalancerClient负载均衡客户端实现负载均衡,我们在使用RestTemplate 发起请求的时候需要跟上服务名的方式http://user-server/user/3. LoadBalancerClient负载均衡客户端
  我们从 @LoadBalanced入手,先看一下LoadBalancerClient它的源码。/** 注解标记RestTemplate 可以使用LoadBalancerClient 负载均衡客户端  * Annotation to mark a RestTemplate 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 { } 复制代码
  这个注解@LoadBalanced 的作用在注释上说得非常清楚,就是标记RestTemplate可以使用使用LoadBalancerClient来实现负载均衡,LoadBalancerClient就是Ribbon实现负载均衡的一个客户端,它在spring-cloud-commons包下,我们可以直接看LoadBalancerClient的源码:/** 客户端负载均衡  * Represents a client side load balancer  * @author Spencer Gibb  */ public interface LoadBalancerClient extends ServiceInstanceChooser { 	//执行请求,会根据serviceId使用负载均衡查找服务 	/** 	使用负载均衡器执行指定服务 	 * execute request using a ServiceInstance from the LoadBalancer for the specified service 	 * 服务Id - 服务ID来查找负载平衡器 	 * @param serviceId the service id to look up the LoadBalancer 	 *  	 * @param request allows implementations to execute pre and post actions such as 	 * incrementing metrics 	 * 返回选择的服务 	 * @return the result of the LoadBalancerRequest callback on the selected 	 * ServiceInstance 	 */ 	 T execute(String serviceId, LoadBalancerRequest request) throws IOException;  	//执行请求,这个方法多了一个参数ServiceInstance ,即请求指定的服务 	/** 	 * execute request using a ServiceInstance from the LoadBalancer for the specified 	 * service 	 * @param serviceId the service id to look up the LoadBalancer 	 * @param serviceInstance the service to execute the request to 	 * @param request allows implementations to execute pre and post actions such as 	 * incrementing metrics 	 * @return the result of the LoadBalancerRequest callback on the selected 	 * ServiceInstance 	 */ 	 T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException;  	//重构URL,把http://myservice/path/to/service重构成http://ip:端口/path/to/service 	/** 	 * Create a proper URI with a real host and port for systems to utilize. 	 * Some systems use a URI with the logical serivce name as the host, 	 * such as http://myservice/path/to/service.  This will replace the 	 * service name with the host:port from the ServiceInstance. 	 * @param instance 	 * @param original a URI with the host as a logical service name 	 * @return a reconstructed URI 	 */ 	URI reconstructURI(ServiceInstance instance, URI original); } 复制代码
  LoadBalancerClient接口三个方法,excute()为执行请求,reconstructURI()用来重构url,它实现了ServiceInstanceChooser 接口,这个接口的作用是用来选择服务的,看下源码/** 	通过使用负载平衡器,选择一个服务器发送请求。  * Implemented by classes which use a load balancer to choose a server to  * send a request to.  *  * @author Ryan Baxter  */ public interface ServiceInstanceChooser {      /**       从LoadBalancer中为指定服务选择一个ServiceInstance      * Choose a ServiceInstance from the LoadBalancer for the specified service      *       * //根据服务id去LoadBalancer查找服务      * @param serviceId the service id to look up the LoadBalancer      *       * 返回查找到的服务实例ServiceInstance       * @return a ServiceInstance that matches the serviceId      */     ServiceInstance choose(String serviceId); } 复制代码
  提供了一个choose方法,根据服务ID serviceId 查找一个ServiceInstance 服务实例,这里的serviceId其实就是http://user-server/… url中带的服务名。
  LoadBalancerClient 还有一个默认实现类RibbonLoadBalancerClient,这个实现是针对Ribbon的客户端负载均衡,继承关系如下:
  RibbonLoadBalancerClient是一个非常核心的类,最终的负载均衡的请求处理由它来执行,源码如下:public class RibbonLoadBalancerClient implements LoadBalancerClient {  	private SpringClientFactory clientFactory;  	public RibbonLoadBalancerClient(SpringClientFactory clientFactory) { 		this.clientFactory = clientFactory; 	} 	//重构URL,找到服务之后,把http://服务名/  格式 重构成 http://ip:port/ 格式 	@Override 	public URI reconstructURI(ServiceInstance instance, URI original) { 		Assert.notNull(instance, "instance can not be null"); 		String serviceId = instance.getServiceId(); 		RibbonLoadBalancerContext context = this.clientFactory 				.getLoadBalancerContext(serviceId);  		URI uri; 		Server server; 		if (instance instanceof RibbonServer) { 			RibbonServer ribbonServer = (RibbonServer) instance; 			server = ribbonServer.getServer(); 			uri = updateToSecureConnectionIfNeeded(original, ribbonServer); 		} else { 			server = new Server(instance.getScheme(), instance.getHost(), instance.getPort()); 			IClientConfig clientConfig = clientFactory.getClientConfig(serviceId); 			ServerIntrospector serverIntrospector = serverIntrospector(serviceId); 			uri = updateToSecureConnectionIfNeeded(original, clientConfig, 					serverIntrospector, server); 		} 		return context.reconstructURIWithServer(server, uri); 	} 	//根据服务名,查找服务实例,选择一个返回ServiceInstance  	@Override 	public ServiceInstance choose(String serviceId) { 		//查找服务 		Server server = getServer(serviceId); 		if (server == null) { 			return null; 		} 		return new RibbonServer(serviceId, server, isSecure(server, serviceId), 				serverIntrospector(serviceId).getMetadata(server)); 	} 	//执行请求 	@Override 	public  T execute(String serviceId, LoadBalancerRequest request) throws IOException { 		//获取负载均衡器[重要] 		ILoadBalancer loadBalancer = getLoadBalancer(serviceId); 		//选择服务,使用负载均衡器,根据服务的ID,选择一个服务 		Server server = getServer(loadBalancer); 		if (server == null) { 			throw new IllegalStateException("No instances available for " + serviceId); 		} 		//选择的服务封装成一个RibbonServer:RibbonServer implements ServiceInstance 		RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, 				serviceId), serverIntrospector(serviceId).getMetadata(server)); 		//执行请求调用服务 		return execute(serviceId, ribbonServer, request); 	} 	//执行请求调用服务 	@Override 	public  T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException { 		Server server = null; 		if(serviceInstance instanceof RibbonServer) { 			server = ((RibbonServer)serviceInstance).getServer(); 		} 		if (server == null) { 			throw new IllegalStateException("No instances available for " + serviceId); 		}  		RibbonLoadBalancerContext context = this.clientFactory 				.getLoadBalancerContext(serviceId); 		RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);  		try { 			//使用 LoadBalancerRequest 向服务发请求 			T returnVal = request.apply(serviceInstance); 			statsRecorder.recordStats(returnVal); 			return returnVal; 		} 		// catch IOException and rethrow so RestTemplate behaves correctly 		catch (IOException ex) { 			statsRecorder.recordStats(ex); 			throw ex; 		} 		catch (Exception ex) { 			statsRecorder.recordStats(ex); 			ReflectionUtils.rethrowRuntimeException(ex); 		} 		return null; 	}  	private ServerIntrospector serverIntrospector(String serviceId) { 		ServerIntrospector serverIntrospector = this.clientFactory.getInstance(serviceId, 				ServerIntrospector.class); 		if (serverIntrospector == null) { 			serverIntrospector = new DefaultServerIntrospector(); 		} 		return serverIntrospector; 	} 	//是否是https请求 	private boolean isSecure(Server server, String serviceId) { 		IClientConfig config = this.clientFactory.getClientConfig(serviceId); 		ServerIntrospector serverIntrospector = serverIntrospector(serviceId); 		return RibbonUtils.isSecure(config, serverIntrospector, server); 	} 	//根据服务ID选择服务 	protected Server getServer(String serviceId) { 		return getServer(getLoadBalancer(serviceId)); 	} 	 	//负载均衡器选择服务 	protected Server getServer(ILoadBalancer loadBalancer) { 		if (loadBalancer == null) { 			return null; 		} 		return loadBalancer.chooseServer("default"); // TODO: better handling of key 	} 	//根据服务id得到负载均衡器 	protected ILoadBalancer getLoadBalancer(String serviceId) { 		return this.clientFactory.getLoadBalancer(serviceId); 	} ...省略... 复制代码
  解释一下:
  这里的ServiceInstance choose(String serviceId)方法的作用是根据ServideId选择一个服务,底层实现是通过LoadBalancer.chooseServer 负载均衡器LoadBalancer来完成的服务的选择的
  选择到服务之后调用execute向选择到的服务发起请求,通过LoadBalancerRequest来完成其请求。4. RestTemplate的执行流程
  RestTmplate发请求时地址 "http://user-server/user/"+id 中 user-server是当前服务需要调用的目标服务的服务名,那么Ribbon到底是如何实现负载均衡调用的呢?我们可以从这里跟踪一下RestTemplate的执行流程:public class RestTemplate extends InterceptingHttpAccessor implements RestOperations { ...省略...  @Nullable     protected  T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor responseExtractor) throws RestClientException {         Assert.notNull(url, "URI is required");         Assert.notNull(method, "HttpMethod is required");         ClientHttpResponse response = null;          Object var14;         try {         	//创建请求对象,使用SimpleClientHttpRequestFactory创建ClientHttpRequest              ClientHttpRequest request = this.createRequest(url, method);             if (requestCallback != null) {             	//设置header和body                 requestCallback.doWithRequest(request);             }              response = request.execute();             this.handleResponse(url, method, response);             var14 = responseExtractor != null ? responseExtractor.extractData(response) : null;         } catch (IOException var12) {             String resource = url.toString();             String query = url.getRawQuery();             resource = query != null ? resource.substring(0, resource.indexOf(63)) : resource;             throw new ResourceAccessException("I/O error on " + method.name() + " request for "" + resource + "": " + var12.getMessage(), var12);         } finally {             if (response != null) {                 response.close();             }          }          return var14;     } 复制代码
  请求来到RestTemplate#doExecute方法,首选是通过使用SimpleClientHttpRequestFactory根据url和method创建ClientHttpRequest 请求对象,使用的实现是InterceptingClientHttpRequestFactory,然后使用response = request.execute();去执行请求,一路跟踪,请求来到InterceptingClientHttpRequest#executeInternal:class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest { 	//headers请求头 , bufferedOutput输出内容     protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {     	//创建拦截器执行器         InterceptingClientHttpRequest.InterceptingRequestExecution requestExecution = new InterceptingClientHttpRequest.InterceptingRequestExecution();         return requestExecution.execute(this, bufferedOutput);     } 复制代码
  这里通过InterceptingClientHttpRequest.InterceptingRequestExecution() 拦截器执行器去执行请求,请求来到InterceptingClientHttpRequest.InterceptingRequestExecution#execute: private class InterceptingRequestExecution implements ClientHttpRequestExecution {         private final Iterator iterator;          public InterceptingRequestExecution() {             this.iterator = InterceptingClientHttpRequest.this.interceptors.iterator();         }          public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {             if (this.iterator.hasNext()) {             	//[重要]这里取到的正是  LoadBalancerInterceptor                 ClientHttpRequestInterceptor nextInterceptor = (ClientHttpRequestInterceptor)this.iterator.next();                 return nextInterceptor.intercept(request, body, this);             } else {                 HttpMethod method = request.getMethod();                 Assert.state(method != null, "No standard HTTP method");                 //如果iterator中没有拦截器了,就创建一个ClientHttpRequest去执行请求                 ClientHttpRequest delegate = InterceptingClientHttpRequest.this.requestFactory.createRequest(request.getURI(), method);                 request.getHeaders().forEach((key, value) -> {                     delegate.getHeaders().addAll(key, value);                 });                 if (body.length > 0) {                     if (delegate instanceof StreamingHttpOutputMessage) {                         StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage)delegate;                         streamingOutputMessage.setBody((outputStream) -> {                             StreamUtils.copy(body, outputStream);                         });                     } else {                         StreamUtils.copy(body, delegate.getBody());                     }                 } 				//执行请求                 return delegate.execute();             }         }     } 复制代码
  InterceptingRequestExecution 中维护了一个Iterator iterator;其中LoadBalancerInterceptor 就在该集合中,所以请求来到LoadBalancerInterceptor #intercept(request, body, this); 方法//负载均衡拦截器 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)); 	} 	//拦截器核心方法【重要】 	//request请求对象 	//body 内容 	@Override 	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, 			final ClientHttpRequestExecution execution) throws IOException { 		//请求的URL,格式如:http://user-server/user/1 ,user-server是服务名 		final URI originalUri = request.getURI(); 		//URL中的服务名 		String serviceName = originalUri.getHost(); 		 		Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri); 		//通过requestFactory.createRequest(request, body, execution)创建LoadBalancerRequest 		//然后调用负载均衡器执行请求,参数:服务名,LoadBalancerRequest 		return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution)); 	} } 复制代码
  这里蛮重要的,请求调用了LoadBalancerInterceptor #intercept负载均衡拦截器的拦截方法,获取到URL,从中获取到主机名即调用的服务名(Ribbon客户端服务名),然后使用LoadBalancerRequestFactory 创建了LoadBalancerRequest请求对象,调用loadBalancer#execute 负载均衡器执行请求5. ILoadBalancer 选择服务(负载均衡)
  请求来到RibbonLoadBalancerClient#execute:	@Override 	public  T execute(String serviceId, LoadBalancerRequest request) throws IOException { 		//获取负载均衡器 		ILoadBalancer loadBalancer = getLoadBalancer(serviceId); 		//loadBalancer选择服务 		Server server = getServer(loadBalancer); 		if (server == null) { 			throw new IllegalStateException("No instances available for " + serviceId); 		} 		//选择的服务封装成RibbonServer  		RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, 				serviceId), serverIntrospector(serviceId).getMetadata(server)); 		//LoadBalancerRequest对服务执行请求 		return execute(serviceId, ribbonServer, request); 	} 复制代码
  这里就蛮关键了:首选是通过服务名调用getLoadBalancer方法得到负载均衡器然后getServer(loadBalancer)是通过负载均衡器选择一个服务,底层会使用IRule的算法然后将服务封装成RibbonServer 对象,交给LoadBalancerRequest去执行请求
  这里的负载均衡器默认会走ZoneAwareLoadBalancer,它是通过SpringClientFactory 从Ribbon上下文对象中获取到的负载均衡器对象,关于这个我们在上一章讨论过public class RibbonLoadBalancerClient implements LoadBalancerClient { ...省略... 	private SpringClientFactory clientFactory; 	protected ILoadBalancer getLoadBalancer(String serviceId) { 		return this.clientFactory.getLoadBalancer(serviceId); 	} 复制代码
  而得到ILoadBalancer之后,调用getServer(loadBalancer)方法选择服务,我们跟踪一下public class RibbonLoadBalancerClient implements LoadBalancerClient { ...省略... 	protected Server getServer(ILoadBalancer loadBalancer) { 		if (loadBalancer == null) { 			return null; 		} 		//ZoneAwareLoadBalancer#chooseServer 		return loadBalancer.chooseServer("default"); // TODO: better handling of key 	}  复制代码
  这里loadBalancer.chooseServer("default");请求来到ZoneAwareLoadBalancer#chooseServer,源码如下:public class ZoneAwareLoadBalancer extends DynamicServerListLoadBalancer { ...省略... @Override     public Server chooseServer(Object key) {         if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {         	//如果禁用了zone,或者自由一个zone会走这里             logger.debug("Zone aware logic disabled or there is only one zone");             return super.chooseServer(key);         } 		//下面就是根据zone选择服务了,默认情况下不会走下面         Server server = null;         try {             LoadBalancerStats lbStats = getLoadBalancerStats();             //得到zone快照             Map zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);             logger.debug("Zone snapshots: {}", zoneSnapshot);             if (triggeringLoad == null) {                 triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(                         "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d);             }              if (triggeringBlackoutPercentage == null) {                 triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(                         "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d);             }             //得到可用的zone             Set availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());             logger.debug("Available zones: {}", availableZones);             if (availableZones != null &&  availableZones.size() < zoneSnapshot.keySet().size()) {             	//随机选择区域                 String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);                 logger.debug("Zone chosen: {}", zone);                 if (zone != null) {                     BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);                     //选择服务                     server = zoneLoadBalancer.chooseServer(key);                 }             }         } catch (Exception e) {             logger.error("Error choosing server using zone aware logic for load balancer={}", name, e);         }         if (server != null) {             return server;         } else {             logger.debug("Zone avoidance logic is not invoked.");             return super.chooseServer(key);         }     } 复制代码
  这里做了一个判断,如果没有设置zone或者只有一个zone(默认),这里会调用 return super.chooseServer(key);通过父类的BaseLoadBalancer#chooseServer方法选择服务,这也是默认的执行流程,代码走到了BaseLoadBalancer#chooseServer方法中,源码如下public class BaseLoadBalancer extends AbstractLoadBalancer implements         PrimeConnections.PrimeConnectionListener, IClientConfigAware {  public Server chooseServer(Object key) {         if (counter == null) {         	//创建一个计数器             counter = createCounter();         }         //计数器增加         counter.increment();         //如果负载均衡规则为空,返回空         if (rule == null) {             return null;         } else {             try {             	//[重要]调用了负载均衡器算法类的choose方法                 return rule.choose(key);             } catch (Exception e) {                 logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);                 return null;             }         }     } 复制代码
  在BaseLoadBalancer #chooseServer方法中调用了IRule#choose方法进行服务的选择服务,IRule有很多是算法策略实现类,默认会走轮询算法,如果有定义负载均衡算法,这里rule.choose调用的就是定义的算法类。
  这里我打了个端点,跟踪了一下源码发现默认情况下会从BaseLoadBalancer#chooseServer方法中调用PredicateBasedRule#choose ,PredicateBasedRule本身是继承ClientConfigEnabledRoundRobinRule,也就是说PredicateBasedRule是使用的是轮询算法,同时它扩展了Predicate功能,即:提供了服务器过滤逻辑  /**   一个规则,提供了服务器过滤逻辑,具体使用的是AbstractServerPredicate实现过滤功能。 过滤后,服务器从过滤列表中的循环方式返回。  * A rule which delegates the server filtering logic to an instance of {@link AbstractServerPredicate}.  * After filtering, a server is returned from filtered list in a round robin fashion.  *   *   * @author awang  *  */ public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule {         /**     抽象函数,返回AbstractServerPredicate,用来对服务做过滤的      * Method that provides an instance of {@link AbstractServerPredicate} to be used by this class.      *       */     public abstract AbstractServerPredicate getPredicate();              /**      * Get a server by calling {@link AbstractServerPredicate#chooseRandomlyAfterFiltering(java.util.List, Object)}.      * The performance for this method is O(n) where n is number of servers to be filtered.      */     @Override     public Server choose(Object key) {     	//得到负载均衡器         ILoadBalancer lb = getLoadBalancer();          //通过AbstractServerPredicate的chooseRoundRobinAfterFiltering选出具体的服务实例         //AbstractServerPredicate的子类实现的Predicate逻辑来过滤一部分服务实例         //然后在以线性轮询的方式从过滤后的实例中选出一个         Optional server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);         if (server.isPresent()) {             return server.get();         } else {             return null;         }            } } 复制代码
  这里使用了AbstractServerPredicate#chooseRoundRobinAfterFiltering来选择服务从lb.getAllServers()得到所有的服务作为参数,继续跟踪下去    /**      * Choose a server in a round robin fashion after the predicate filters a given list of servers and load balancer key.       */     public Optional chooseRoundRobinAfterFiltering(List servers, Object loadBalancerKey) {     	//得到合格的服务列表,主要根据zone做一个过滤         List eligible = getEligibleServers(servers, loadBalancerKey);         if (eligible.size() == 0) {           //没找到合格的服务             return Optional.absent();         }         //以线性轮询的方式合格的服务列表获取一个实例         //incrementAndGetModulo方法会以轮询的方式计算一个下标值         return Optional.of(eligible.get(incrementAndGetModulo(eligible.size())));     } 	...省略...  /**  	引用于 RoundRobinRule 算法策略 , 轮询      * Referenced from RoundRobinRule      * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.      *      * @param modulo The modulo to bound the value of the counter.      * @return The next value.      */      //增量和取模实现轮询     private int incrementAndGetModulo(int modulo) {         for (;;) {             int current = nextIndex.get();             int next = (current + 1) % modulo;             if (nextIndex.compareAndSet(current, next) && current < modulo)                 return current;         }     } 复制代码
  这里首先会通过zone过滤出可用的服务列表,然后使用轮询算法选择一个服务返回,到这里选择服务的流程调用6. LoadBalancerRequest 执行服务
  代码继续回到 RibbonLoadBalancerClient#execute,选择完服务之后,服务被封装成RibbonServer:	@Override 	public  T execute(String serviceId, LoadBalancerRequest request) throws IOException { 		//得到负载均衡器 		ILoadBalancer loadBalancer = getLoadBalancer(serviceId); 		//选择服务 		Server server = getServer(loadBalancer); 		if (server == null) { 			throw new IllegalStateException("No instances available for " + serviceId); 		} 		//把server 封装成RibbonServer  		RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, 				serviceId), serverIntrospector(serviceId).getMetadata(server)); 		//执行服务调用 		return execute(serviceId, ribbonServer, request); 	} 复制代码
  找到服务后,调用了execute方法执行后续请求@Override 	public  T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException { 		Server server = null; 		if(serviceInstance instanceof RibbonServer) { 			server = ((RibbonServer)serviceInstance).getServer(); 		} 		if (server == null) { 			throw new IllegalStateException("No instances available for " + serviceId); 		} 		//加载Ribbon负载均衡器上下文对象 		RibbonLoadBalancerContext context = this.clientFactory 				.getLoadBalancerContext(serviceId); 		RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);  		try { 			//LoadBalancerRequest.apply执行请求 			T returnVal = request.apply(serviceInstance); 			statsRecorder.recordStats(returnVal); 			return returnVal; 		} 		// catch IOException and rethrow so RestTemplate behaves correctly 		catch (IOException ex) { 			statsRecorder.recordStats(ex); 			throw ex; 		} 		catch (Exception ex) { 			statsRecorder.recordStats(ex); 			ReflectionUtils.rethrowRuntimeException(ex); 		} 		return null; 	} 复制代码
  这里调用LoadBalancerRequest.apply执行请求,后面还会调用LoadBalancerRequestFactory#createRequest方法创建请求,调用ClientHttpRequestExecution#execute执行,然后又会将请求委派给ClientHttpRequest#execute去执行,再往后面走就是创建HttpURLConnection链接对象发送请求了,我们就不继续跟下去了。7. 总结
  纵观Ribbon的工作流程大致如下:初始化的时候创建好Ribbon的上下文,以及相关的组件,如ILoadBalancer,IConfig,IRule等等初始化过程中会通过ServerList从EurekaClient加载负载均衡候选的服务列表,并定时更新服务列表,使用ServerListFilter过滤之后,使用IPing检查是否更新服务列表被注解了@LoadBalance标签的RestTemplate可以使用LoadBalancerClient作负载均衡,并添加好拦截器LoadBalancerInterceptor当请求发起RestTemplate会把请求交给LoadBalancerInterceptor 拦截器,LoadBalancerInterceptor 拦截器调用LoadBalancerClient接收到请求使用ILoadBalancer负载均衡器选择服务,底层用到IRule算法 选择好服务之后,LoadBalancerClient把请求交给LoadBalancerRequest去执行
  参考文章
  spring-cloud-openfeign-2.2.2源码分析
  springcloud-source-study学习github地址
  微服务入门到入土
  SpringCloud之史上最详细Ribbon源码解读
  Spring Cloud——Ribbon 源码解析 Spring Cloud Alibaba源码解析

南极洲西部的冰盖崩塌并非不可避免人类有机会减缓失控的冰撤退一个国际研究小组将卫星图像气候和海洋记录结合起来,以获得迄今为止对南极西部冰原(其所含冰量足以使全球海平面上升3。3米)如何应对气候变化的最详细了解。来自剑桥大学爱丁堡大学和华盛顿名记分析詹姆斯今夏交易前景并提出5大下家老鹰ampampamp公牛ampampamp76人领衔直播吧1月17日讯近日,TheAthletic名记DavidAldridge撰文分析了湖人球星詹姆斯今夏的交易前景。该记者列出了詹姆斯最有可能的5大交易下家和交易方案,并对方案进行冬天大衣要这样穿选深不选浅,位置到膝盖,每套透着高级感冬天到了没有一个大衣怎么行,看街头上有的人穿大衣好看,有的却很一般,除了自身的气质外,在搭配或者大衣挑选上都很有技巧。所以喜欢穿大衣的朋友,一定要选择适合自己的外套,那么怎么选,今想干成事业,就一定要有严格的生活作风日期2022年12月30日星期五想要达成结果想要赚钱,想要做成一番事业,就一定要有严格的生活作风,如果这一点做不好,说啥都是扯淡。在这个充满竞争的社会,可不是像学校一样,有老师管理3万红军被30万国军围困乌江,19岁战士献奇策,毛主席军中神人在阅读此文前,麻烦您点击一下关注,既方便您进行讨论与分享,又可以让您下次继续阅读相关文章,感谢您的支持。1935年1月1日,新年第一天,毛主席正在乌江岸边望着漫天风雪,眉头紧锁。长王思聪打人后,下面一堆奴才跪族洗白叫好文章作者每日怡见最近上海富豪圈里面出了两件大事。一个是严公子1888万的天价彩礼,这事儿在圈子里都沦为笑谈了,一个豪门富二代,因为爱情冲昏头脑,被人家给拿捏,敲竹杠。据说,隔壁昆山杜兰特膝盖受伤退场!篮网一分险胜热火!近20场比赛18胜2负!篮网VS热火,战至最后,方才分出胜负。双方各有一次绝杀机会,欧文超远三分出手,绝杀不进,奥尼尔抢下进攻篮板,强起补篮,命中准绝杀。回过头来,巴特勒接球强杀篮下,面对双人防守拉杆上篮三年三伤!杜兰特脆弱的膝盖,真的伤不起啊!不得不承认,杜兰特运气真的不够好。相同的时间点,相同的方式,KD又一次遭遇了伤病,算下来这已经是杜兰特连续三年在这个时间点受伤了而哪怕我们再诟病杜小帅的选择和欧弟的一些行为,我们还小米成功超越华为!看来雷军是认真的冲击高端不只是一句口号高端手机市场变天了?近日,市场调研机构发布的一组数据引发热议,从数据中可以获悉,中国高端手机市场中,小米已经成功超越华为,成为销量最好的国产手机品牌。目前,苹果小米华为在高端手机市孟晚舟与美女网红合影,脸型大变瘦成锥子脸,五官精致气质优雅近日,有一位美女网红在社交平台上晒出与孟晚舟的合影,也是曝光了回国已久的孟晚舟的近况,引来无数网友的关注和热议,而且在该网红的配文中能看出她当时的心情有多么的激动了,还称孟晚舟是自纵有疾风起靳东栽了!宋佳表现惊人,力抢1号主演宇哥带你读原著,一起来看由靳东,宋佳,田雨等主演的热播电视剧纵有疾风起。看解析,更深入。楔子人世间中,宋佳饰演的周蓉,是周家三兄妹中,最叛逆的一个。梁晓声的原著中,早年间周蓉为了爱
矢野浩二正式官宣启用中文名杨颖却还坚持用外国人名字觉悟低了矢野浩二正式官宣启用中文名杨颖却还坚持用外国人名字觉悟低了人跟人还真的是不能比,娱乐圈明星花里胡哨,甚至有人喜欢改名来改变自己的星途,还有些人喜欢作妖,用一些花里胡哨的名字,企图来朱沙苗任职资格获批担任安盛天平财险董事长9月15日,安盛天平财险官网发布公告,朱沙苗任职资格获批,担任公司董事长。南都湾财社记者从安盛天平财险官网上的资料了解到,朱沙苗女士毕业于南开大学,获货币银行学专业保险学方向硕士学真不给老东家面子!高准翼进球后疯狂庆祝,引起泰山球迷不满在此前结束的一场中超焦点战中,缺少了斯坦丘和马尔康作客挑战泰山队,虽然球队在控球率上不高,但是球队的防守反击却非常出色,在上半场的时候高准翼为球队打进一球,为球队取得领先。然而没想售价17。99万元,东风日产奇骏限定版上市,搭载三缸CVT日前,Auto情报处从相关渠道获悉,东风日产奇骏星月限定版正式上市,新车共推出一款配置车型,售价为17。99万元,限定版车型也将是奇骏的入门版本东风日产奇骏星月限定版在外观方面,没新车雪佛兰探界者EV亮相北美车展,海外约20万起售,续航483公里文懂车帝原创高帅鹏懂车帝原创2022北美车展日前,2022北美车展正式开幕。在本届车展上,雪佛兰旗下全新纯电SUV探界者EV正式亮相。根据此前雪佛兰品牌公布的价格信息,探界者EV入浙江一初三学生刮伤路边轿车,留道歉条和联系方式,教育该做什么近日,浙江丽水一个初三学生在骑自行车时,把路边一轿车刮了一道长痕,这个学生写了一个道歉便条,贴在了车前门的玻璃上,还留下了微信和QQ联系方式。车主徐先生看到道歉条,很感动,没有要求韩国3岁女童在家被饿死,警方调查后发现外婆才是她的亲生母亲2021年,韩国警方受理了一起虐童案。3岁女童被母亲金宝英(化名)丢弃在公寓中,六个月后才被人发现,但孩子早已被饿死。令人惊讶的是,女童的外婆石美淑(化名)就住在楼下,半年时间她从百亿女富豪陈丽华,一语道破婚姻关系,嫁豪门的唐僧过得好吗1990年,百亿女富豪陈丽华,嫁给了唐僧的扮演者迟重瑞,她一语道破了婚姻的关系。在新婚之夜,她对丈夫迟重瑞说迟先生,我对您有四点要求,你听听看,您能答应吗?等陈丽华说完,迟重瑞的心下跌趋势赶紧考虑这么做昨天受米国通胀影响,导致市场对后期加息时间窗口强烈,市场直接撒泼低开,盘中一度想来个V型反弹,最终还是以失败告终,整个市场向上没力度,向下盘面非常轻,个股跌的稀里哗啦。今日各大银行欧冠最新积分榜曼城大巴黎均逆转领跑,尤文爆冷遭遇两连败北京时间9月15日凌晨,欧冠联赛小组赛第2轮比赛再赛9场。在这个比赛日AC米兰主场对阵萨格勒布迪纳摩,顿涅茨克矿工迎战凯尔特人,哥本哈根对阵塞维利亚,那不勒斯客战流浪者,切尔西主场342万人在线预约,任嘉伦李沁新剧请君今晚开播,爆款潜质现在国剧圈最火爆的题材就是古偶剧和仙恋剧,很多明星也因此晋级一线行列,而最受观众喜爱的四大古装男颜天花板当属任嘉伦龚俊肖战王一博。与君初相识和恰似故人归的连续播出,又有蓝焰突击接档