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

SpringCloud原理Ribbon核心组件以及运行原理万字源码剖析

  大家好,本文我将继续来剖析SpringCloud中负载均衡组件Ribbon的源码。本来我是打算接着OpenFeign动态代理生成文章直接讲Feign是如何整合Ribbon的,但是文章写了一半发现,如果不把Ribbon好好讲清楚,那么有些Ribbon的细节理解起来就很困难,所以我还是打算单独写一篇文章来剖析Ribbon的源码,这样在讲Feign整合Ribbon的时候,我就不再赘述这些细节了。好了,话不多说,直接进入主题。  一、Ribbon的核心组件1、Server
  这是个很简单的东西,就是服务实例数据的封装,里面封装了服务实例的ip和端口之类的,一个服务有很多台机器,那就有很多个Server对象。  2、ServerListpublic interface ServerList {      public List getInitialListOfServers();          /**      * Return updated list of servers. This is called say every 30 secs      * (configurable) by the Loadbalancer"s Ping cycle      *       */     public List getUpdatedListOfServers();     }
  ServerList是个接口,泛型是Server,提供了两个方法,都是获取服务实例列表的,这两个方法其实在很多实现类中实现是一样的,没什么区别。这个接口很重要,因为这个接口就是Ribbon获取服务数据的来源接口,Ribbon进行负载均衡的服务列表就是通过这个接口来的,那么可以想一想是不是只要实现这个接口就可以给Ribbon提供服务数据了?事实的确如此,在SpringCloud中,eureka、nacos等注册中心都实现了这个接口,都将注册中心的服务实例数据提供给Ribbon,供Ribbon来进行负载均衡。3、ServerListUpdater
  通过名字也可以知道,是用来更新服务注册表的数据,他有唯一的实现,就是PollingServerListUpdater,这个类有一个核心的方法,就是start,我们来看一下start的实现。@Override     public synchronized void start(final UpdateAction updateAction) {         if (isActive.compareAndSet(false, true)) {             final Runnable wrapperRunnable = new Runnable() {                 @Override                 public void run() {                     if (!isActive.get()) {                         if (scheduledFuture != null) {                             scheduledFuture.cancel(true);                         }                         return;                     }                     try {                         updateAction.doUpdate();                         lastUpdated = System.currentTimeMillis();                     } catch (Exception e) {                         logger.warn("Failed one update cycle", e);                     }                 }             };              scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(                     wrapperRunnable,                     initialDelayMs,                     refreshIntervalMs,                     TimeUnit.MILLISECONDS             );         } else {             logger.info("Already active, no-op");         }     }
  通过这段方法我们可以看出,首先通过isActive.compareAndSet(false, true)来保证这个方法只会被调用一下,然后封装了一个Runnable,这个Runnable干了一件核心的事,就是调用传入的updateAction的doUpdate方法,然后将Runnable扔到了带定时调度功能的线程池,经过initialDelayMs(默认1s)时间后,会调用一次,之后都是每隔refreshIntervalMs(默认30s)调用一次Runnable的run方法,也就是调用updateAction的doUpdate方法。
  所以这个类的核心作用就是每隔30s会调用一次传入的updateAction的doUpdate方法的实现,记住这个结论。4、IRulepublic 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是负责负载均衡的算法的,也就是真正实现负载均衡获取一个服务实例就是这个接口的实现。比如说实现类RandomRule,就是从一堆服务实例中随机选取一个服务实例。5、IClientConfig
  就是一个配置接口,有个默认的实现DefaultClientConfigImpl,通过这个可以获取到一些配置Ribbon的一些配置。6、ILoadBalancerpublic interface ILoadBalancer {    public void addServers(List newServers);      public Server chooseServer(Object key);      public void markServerDown(Server server);      @Deprecated   public List getServerList(boolean availableOnly);    public List getReachableServers();    public List getAllServers(); }
  这个接口的作用,对外主要提供了获取服务实例列表和选择服务实例的功能。虽然对外主要提供获取服务的功能,但是在实现的时候,主要是用来协调上面提到的各个核心组件的,使得他们能够协调工作,从而实现对外提供获取服务实例的功能。
  这个接口的实现有好几个实现类,但是我讲两个比较重要的。
  BaseLoadBalancerpublic class BaseLoadBalancer extends AbstractLoadBalancer implements         PrimeConnections.PrimeConnectionListener, IClientConfigAware {         private final static IRule DEFAULT_RULE = new RoundRobinRule();         protected IRule rule = DEFAULT_RULE;     private IClientConfig config;       protected volatile List allServerList = Collections             .synchronizedList(new ArrayList());     protected volatile List upServerList = Collections             .synchronizedList(new ArrayList());          public BaseLoadBalancer(String name, IRule rule, LoadBalancerStats stats,             IPing ping, IPingStrategy pingStrategy) {            logger.debug("LoadBalancer [{}]:  initialized", name);                  this.name = name;         this.ping = ping;         this.pingStrategy = pingStrategy;         setRule(rule);         setupPingTask();         lbStats = stats;         init();     }      public BaseLoadBalancer(IClientConfig config) {         initWithNiwsConfig(config);     }     public BaseLoadBalancer(IClientConfig config, IRule rule, IPing ping) {         initWithConfig(config, rule, ping, createLoadBalancerStatsFromConfig(config));     }      void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping, LoadBalancerStats stats) {         this.config = clientConfig;         String clientName = clientConfig.getClientName();         this.name = clientName;         int pingIntervalTime = Integer.parseInt(""                 + clientConfig.getProperty(                         CommonClientConfigKey.NFLoadBalancerPingInterval,                         Integer.parseInt("30")));         int maxTotalPingTime = Integer.parseInt(""                 + clientConfig.getProperty(                         CommonClientConfigKey.NFLoadBalancerMaxTotalPingTime,                         Integer.parseInt("2")));          setPingInterval(pingIntervalTime);         setMaxTotalPingTime(maxTotalPingTime);          // cross associate with each other         // i.e. Rule,Ping meet your container LB         // LB, these are your Ping and Rule guys ...         setRule(rule);         setPing(ping);          setLoadBalancerStats(stats);         rule.setLoadBalancer(this);         if (ping instanceof AbstractLoadBalancerPing) {             ((AbstractLoadBalancerPing) ping).setLoadBalancer(this);         }         logger.info("Client: {} instantiated a LoadBalancer: {}", name, this);         boolean enablePrimeConnections = clientConfig.get(                 CommonClientConfigKey.EnablePrimeConnections, DefaultClientConfigImpl.DEFAULT_ENABLE_PRIME_CONNECTIONS);          if (enablePrimeConnections) {             this.setEnablePrimingConnections(true);             PrimeConnections primeConnections = new PrimeConnections(                     this.getName(), clientConfig);             this.setPrimeConnections(primeConnections);         }         init();      }          public void setRule(IRule rule) {         if (rule != null) {             this.rule = rule;         } else {             /* default rule */             this.rule = new RoundRobinRule();         }         if (this.rule.getLoadBalancer() != this) {             this.rule.setLoadBalancer(this);         }     }           public Server chooseServer(Object key) {         if (counter == null) {             counter = createCounter();         }         counter.increment();         if (rule == null) {             return null;         } else {             try {                 return rule.choose(key);             } catch (Exception e) {                 logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);                 return null;             }         }     }     }
  核心属性
  allServerList:缓存了所有的服务实例数据
  upServerList:缓存了能够使用的服务实例数据。
  rule:负载均衡算法组件,默认是RoundRobinRule
  核心方法
  setRule:这个方法是设置负载均衡算法的,并将当前这个ILoadBalancer对象设置给IRule,从这可以得出一个结论,IRule进行负载均衡的服务实例列表是通过ILoadBalancer获取的,也就是 IRule 和 ILoadBalancer相互引用。setRule(rule)一般是在构造对象的时候会调用。
  chooseServer:就是选择一个服务实例,是委派给IRule的choose方法来实现服务实例的选择。
  BaseLoadBalancer这个实现类总体来说,已经实现了ILoadBalancer的功能的,所以这个已经基本满足使用了。
  说完BaseLoadBalancer这个实现类,接下来说一下DynamicServerListLoadBalancer实现类。DynamicServerListLoadBalancer继承自BaseLoadBalancer,DynamicServerListLoadBalancer主要是对BaseLoadBalancer功能进行扩展。
  DynamicServerListLoadBalancerpublic class DynamicServerListLoadBalancer extends BaseLoadBalancer {     private static final Logger LOGGER = LoggerFactory.getLogger(DynamicServerListLoadBalancer.class);      volatile ServerList serverListImpl;     volatile ServerListFilter filter;     protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {         @Override         public void doUpdate() {             updateListOfServers();         }     };     protected volatile ServerListUpdater serverListUpdater;         public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,                                          ServerList serverList, ServerListFilter 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);     }              @Override     public void setServersList(List lsrv) {         super.setServersList(lsrv);         List serverList = (List) lsrv;         Map> serversInZones = new HashMap>();         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 servers = serversInZones.get(zone);                 if (servers == null) {                     servers = new ArrayList();                     serversInZones.put(zone, servers);                 }                 servers.add(server);             }         }         setServerListForZones(serversInZones);     }      protected void setServerListForZones(             Map> zoneServersMap) {         LOGGER.debug("Setting server list for zones: {}", zoneServersMap);         getLoadBalancerStats().updateZoneServerMapping(zoneServersMap);     }      @VisibleForTesting     public void updateListOfServers() {         List servers = new ArrayList();         if (serverListImpl != null) {             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);     }      /**      * Update the AllServer list in the LoadBalancer if necessary and enabled      *       * @param ls      */     protected void updateAllServerList(List ls) {         // other threads might be doing this - in which case, we pass         if (serverListUpdateInProgress.compareAndSet(false, true)) {             try {                 for (T s : ls) {                     s.setAlive(true); // set so that clients can start using these                                       // servers right away instead                                       // of having to wait out the ping cycle.                 }                 setServersList(ls);                 super.forceQuickPing();             } finally {                 serverListUpdateInProgress.set(false);             }         }     } }
  成员变量
  serverListImpl:上面说过,通过这个接口获取服务列表
  filter:起到过滤的作用,一般不care
  updateAction:是个匿名内部类,实现了doUpdate方法,会调用updateListOfServers方法
  serverListUpdater:上面说到过,默认就是唯一的实现类PollingServerListUpdater,也就是每个30s就会调用传入的updateAction的doUpdate方法。
  这不是巧了么,serverListUpdater的start方法需要一个updateAction,刚刚好成员变量有个updateAction的匿名内部类的实现,所以serverListUpdater的start方法传入的updateAction的实现其实就是这个匿名内部类。
  那么哪里调用了serverListUpdater的start方法传入了updateAction呢?是在构造的时候调用的,具体的调用链路是调用 restOfInit -> enableAndInitLearnNewServersFeature(),这里就不贴源码了
  所以,其实DynamicServerListLoadBalancer在构造完成之后,默认每隔30s中,就会调用updateAction的匿名内部类的doUpdate方法,从而会调用updateListOfServers。所以我们来看一看 updateListOfServers 方法干了什么。public void updateListOfServers() {         List servers = new ArrayList();         if (serverListImpl != null) {             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);    }
  这个方法实现很简单,就是通过调用 ServerList 的getUpdatedListOfServers获取到一批服务实例数据,然后过滤一下,最后调用updateAllServerList方法,进入updateAllServerList方法。protected void updateAllServerList(List ls) {         // other threads might be doing this - in which case, we pass         if (serverListUpdateInProgress.compareAndSet(false, true)) {             try {                 for (T s : ls) {                     s.setAlive(true); // set so that clients can start using these                                       // servers right away instead                                       // of having to wait out the ping cycle.                 }                 setServersList(ls);                 super.forceQuickPing();             } finally {                 serverListUpdateInProgress.set(false);             }         }     }
  其实很简单,就是调用每个服务实例的setAlive方法,将isAliveFlag设置成true,然后调用setServersList。setServersList这个方法的主要作用是将服务实例更新到内部的缓存中,也就是上面提到的allServerList和upServerList,这里就不贴源码了。
  其实分析完updateListOfServers方法之后,再结合上面源码的分析,我们可以清楚的得出一个结论,那就是默认每隔30s都会重新通过ServerList组件获取到服务实例数据,然后更新到BaseLoadBalancer缓存中,IRule的负载均衡所需的服务实例数据,就是这个内部缓存。
  从DynamicServerListLoadBalancer的命名也可以看出,他相对于父类BaseLoadBalancer而言,提供了动态更新内部服务实例列表的功能。
  为了便于大家记忆,我画一张图来描述这些组件的关系以及是如何运作的。
  说完一些核心的组件,以及他们跟ILoadBalancer的关系之后,接下来就来分析一下,ILoadBalancer是在ribbon中是如何使用的。8、AbstractLoadBalancerAwareClient
  ILoadBalancer是一个可以获取到服务实例数据的组件,那么服务实例跟什么有关,那么肯定是跟请求有关,所以在Ribbon中有这么一个抽象类,AbstractLoadBalancerAwareClient,这个是用来执行请求的,我们来看一下这个类的构造。public AbstractLoadBalancerAwareClient(ILoadBalancer lb) {         super(lb);     }          /**      * Delegate to {@link #initWithNiwsConfig(IClientConfig)}      * @param clientConfig      */     public AbstractLoadBalancerAwareClient(ILoadBalancer lb, IClientConfig clientConfig) {         super(lb, clientConfig);             }
  通过上面可以看出,在构造的时候需要传入一个ILoadBalancer。
  AbstractLoadBalancerAwareClient中有一个方法executeWithLoadBalancer,这个是用来执行传入的请求,以负载均衡的方式。public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {         LoadBalancerCommand command = buildLoadBalancerCommand(request, requestConfig);          try {             return command.submit(                 new ServerOperation() {                     @Override                     public Observable call(Server server) {                         URI finalUri = reconstructURIWithServer(server, request.getUri());                         S requestForServer = (S) request.replaceUri(finalUri);                         try {                             return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));                         }                          catch (Exception e) {                             return Observable.error(e);                         }                     }                 })                 .toBlocking()                 .single();         } catch (Exception e) {             Throwable t = e.getCause();             if (t instanceof ClientException) {                 throw (ClientException) t;             } else {                 throw new ClientException(e);             }         }              }
  这个方法构建了一个LoadBalancerCommand,随后调用了submit方法,传入了一个匿名内部类,这个匿名内部类中有这么一行代码很重要。URI finalUri = reconstructURIWithServer(server, request.getUri());
  这行代码是根据给定的一个Server重构了URI,这是什么意思呢?举个例子,在OpenFeign那一篇文章我说过,会根据服务名拼接出类似 http:// ServerA 的地址,那时是没有服务器的ip地址的,只有服务名,假设请求的地址是 http:// ServerA/api/sayHello ,那么reconstructURIWithServer干的一件事就是将ServerA服务名替换成真正的服务所在的机器的ip和端口,假设ServerA所在的一台机器(Server里面封装了某台机器的ip和端口)是192.168.1.101:8088,那么重构后的地址就变成 http:// 192.168.1.101:8088/api/ sayHello ,这样就能发送http请求到ServerA服务所对应的一台服务器了。
  之后根据新的地址,调用这个类中的execute方法来执行请求,execute方法是个抽象方法,也就是交给子类实现,子类就可以通过实现这个方法,来发送http请求,实现rpc调用。
  那么这台Server是从获取的呢?其实猜猜也知道,肯定是通过ILoadBalancer获取的,因为submit方法比较长,这里我直接贴出submit方法中核心的一部分代码Observable o =             (server == null ? selectServer() : Observable.just(server))
  就是通过selectServer来选择一个Server的,selectServer我就不翻源码了,其实最终还是调用ILoadBalancer的方法chooseServer方法来获取一个服务,之后就会调用上面的说的匿名内部类的方法,重构URI,然后再交由子类的execut方法来实现发送http请求。
  所以,通过对AbstractLoadBalancerAwareClient的executeWithLoadBalancer方法,我们可以知道,这个抽象类的主要作用就是通过负载均衡算法,找到一个合适的Server,然后将你传入的请求路径 http:// ServerA/api/sayHello 重新构建成类似 http:// 192.168.1.101:8088/api/ sayHello 这样,之后调用子类实现的execut方法,来发送http请求,就是这么简单。
  到这里其实Ribbon核心组件和执行原理我就已经说的差不多了,再来画一张图总结一下
  二、SpringCloud中使用的核心组件的实现都有哪些
  说完了Ribbon的一些核心组件和执行原理之后,我们再来看一下在SpringCloud环境下,这些组件到底是用的哪些实现,毕竟有写时接口,有的是抽象类。
  Ribbon的自动装配类:RibbonAutoConfiguration,我拎出了核心的源码@Configuration @RibbonClients public class RibbonAutoConfiguration {    @Autowired(required = false)   private List configurations = new ArrayList<>();   @Bean   public SpringClientFactory springClientFactory() {     SpringClientFactory factory = new SpringClientFactory();     factory.setConfigurations(this.configurations);     return factory;   } }
  RibbonAutoConfiguration配置类上有个@RibbonClients注解,接下来讲解一下这个注解的作用@Import(RibbonClientConfigurationRegistrar.class) public @interface RibbonClients {    RibbonClient[] value() default {};    Class<?>[] defaultConfiguration() default {};  }
  看过我写的OpenFeign的文章小伙伴肯定知道,要使用Feign,得需要使用@EnableFeignClients,@EnableFeignClients的作用可以扫描指定包路径下的@FeignClient注解,也可以声明配置类;同样RibbonClients的作用也是可以声明配置类,同样也使用了@Import注解注解来实现的,RibbonClientConfigurationRegistrar这个配置类的作用就是往spring容器中注入每个服务的Ribbon组件(@RibbonClient里面可以声明每个服务对应的配置)的配置类和默认配置类,将配置类封装为RibbonClientSpecification注入到spring容器中,其实就跟@FeignClient注解声明配置的作用是一样的。
  RibbonAutoConfiguration的主要作用就是注入了一堆RibbonClientSpecification,就是每个服务对应的配置类,然后声明了SpringClientFactory这个bean,将配置类放入到里面。
  SpringClientFactory是不是感觉跟OpenFeign中的FeignContext很像,其实两个的作用是一样的,SpringClientFactory也继承了NamedContextFactory,实现了配置隔离,同时也在构造方法中传入了每个容器默认的配置类RibbonClientConfiguration。至于什么是配置隔离,我在OpenFeign那篇文章说过,不清楚的小伙伴可以后台回复feign01即可获得文章链接。
  配置优先级问题
  这里我说一下在OpenFeign里没仔细说的配置优先级的事情,因为有这么多配置类,都可以在配置类中声明对象,那么到底使用哪个配置类声明的对象呢。
  优先级最高的是springboot启动的时候的容器,因为这个容器是每个服务的容器的父容器,而在配置类声明bean的时候,都有@ConditionalOnMissingBean注解,一旦父容器有这个bean,那么子容器就不会初始化。
  优先级第二高的是每个客户端声明的配置类,也就是通过@FeignClient和@RibbonClient的configuration属性声明的配置类
  优先级第三高的是@EnableFeignClients和@RibbonClients注解中configuration属性声明的配置类
  优先级最低的就是FeignContext和SpringClientFactory构造时传入的配置类
  至于优先级怎么来的,其实是在NamedContextFactory中createContext方法中构建AnnotationConfigApplicationContext时按照配置的优先级一个一个传进去的。
  RibbonClientConfiguration提供的默认的bean
  接下来我们看一下RibbonClientConfiguration都提供了哪些默认的bean@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,这里设置了ConnectTimeout和ReadTimeout都是1s中。}
  IRule,默认是ZoneAvoidanceRule,这个Rule带有过滤的功能,过滤哪些不可用的分区的服务(这个过滤可以不用care),过滤成功之后,继续采用线性轮询的方式从过滤结果中选择一个出来。至于这个propertiesFactory,可以不用管,这个是默认读配置文件的中的配置,一般不设置,后面看到都不用care。@Bean   @ConditionalOnMissingBean   @SuppressWarnings("unchecked")   public ServerList 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,也就是基于配置来提供服务实例列表。但是在SpringCloud环境中,这是不可能的,因为服务信息是在注册中心,所以应该是服务注册中心对应实现的,比如Nacos的实现NacosServerList,这里我贴出NacosServerList的bean的声明,在配置类NacosRibbonClientConfiguration中@Bean   @ConditionalOnMissingBean   public ServerList<?> ribbonServerList(IClientConfig config,       NacosDiscoveryProperties nacosDiscoveryProperties) {     NacosServerList serverList = new NacosServerList(nacosDiscoveryProperties);     serverList.initWithNiwsConfig(config);     return serverList;   }
  至于为什么容器选择NacosServerList而不是ConfigurationBasedServerList,主要是因为NacosRibbonClientConfiguration这个配置类是通过@RibbonClients导入的,也就是比SpringClientFactory导入的RibbonClientConfiguration配置类优先级高。@Bean   @ConditionalOnMissingBean   public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {     return new PollingServerListUpdater(config);   }
  ServerListUpdater,就是我们剖析的PollingServerListUpdater,默认30s更新一次BaseLoadBalancer内部服务的缓存。@Bean   @ConditionalOnMissingBean   public ILoadBalancer ribbonLoadBalancer(IClientConfig config,       ServerList serverList, ServerListFilter 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,默认是ZoneAwareLoadBalancer,构造的时候也传入了上面声明的的bean,ZoneAwareLoadBalancer这个类继承了DynamicServerListLoadBalancer,所以这个类功能也符合我们剖析的源码,至于ZoneAwareLoadBalancer多余的特性,也不用care。
  到这里,Ribbon在SpringCloud的配置我们就讲完了,主要就是声明了很多核心组件的bean,最后都设置到ZoneAwareLoadBalancer中。但是,AbstractLoadBalancerAwareClient这个对象的声明我们并没有在配置类中找到,主要是因为这个对象是OpenFeign整合Ribbon的一个入口,至于是如何整合的,这个坑就留给下篇文章吧。
  那么在springcloud中,上图就可以加上注册中心。
  三、总结
  本文剖析了Ribbon这个负载均衡组件中的一些核心组件的源码,并且将这些组件之间的关系一一描述清楚,同时也剖析了在发送请求的时候是如何通过ILoadBalancer获取到一个服务实例,重构URI的过程。希望本篇文章能够让你知道Ribbon是如何工作的。
  原文链接:https://www.cnblogs.com/zzyang/p/16308974.html?utm_source=tuicool&utm_medium=referral

做提眉手术后,都怕遇到的4件事人一旦上了年纪,或早或晚会出现这样的困扰1上眼皮耷拉着,严重者还遮挡视线,用手一拉还能拉扯好远2有的人还有肿眼泡3有的本来是双眼皮,现在看不到了,变成了内双等。遇到这种情况,可以考优惠游戏日报11月19日!黑色星期五大促来袭!特惠游戏盘点点个关注不迷路,每日获取游戏优惠资讯!黑五史低游戏合集骨折好价,背刺先行小绿人平台已经率先开始了黑五特惠,包涵STEAM,EPIC,育碧等众多平台游戏。荒野大嫖客2终极版39915哈利波特手游吃货小天使秒变狮院扛把子?玩家罗恩yyds看过哈利波特的伙伴,没有不为铁三角之间的友谊动容的,而在救世主哈利和学霸赫敏的光环下,罗恩仿佛更像我们身边接地气的伙伴讲义气,对好友毫无保留,三人组的可靠后盾,罗恩在剧情中可能不是LPL最烂的5次转会!LGD花钱找揍不算啥,RNG六年前的操作才经典大家好我是没有感情的A电竞江小风。现在正值转会期间,很多水友都非常关心自家俱乐部的前景,但是目前消息非常混杂,而且LPL的队伍不到最后一天是不大可能官宣阵容的,因此,小风就为大家回你还记得耿汉林吗?那个春晚小品常青树去哪了?作为小品演员,耿汉林无疑是很成功的。曾经他是春晚的常青树,给我们留下了许多经典回忆。尤其是和赵丽蓉搭档的小品,经典到每一句台词都能随口道来,这几部小品让他爆红全国却也为他招来了不少中国研发!酸碱度控制运动!水凝胶纳米机器人定向灭癌来源器械之家,未经授权不得转载,且24小时后方可转载。科幻电影中经常出现将微型机器人植入人体,并对人体进行控制或者改造的情节。如今,这种场景或可真正发生在现实生活中。近日,来自中国新世界七大奇迹大兴机场,都用了哪些物联网黑科技?9月25日,备受瞩目的北京大兴国际机场正式投入运营,凤凰展翅,振翅高飞!作为民航局打造的智慧机场30样板工程,北京大兴国际机场利用物联网技术云计算及存储个人智能终端虚拟可视化技术等中国减持530亿美债,186吨黄金从欧美运抵中国,美国人开始狂买黄金虽然,美联储主席鲍威尔在日前宣布从11月开始以每月150亿美元的规模减少购买资产,以收紧自去年3月以来的不限额度的超级宽松政策,但目前美联储仍在以每月1100亿的规模在购买基础债券王振华事件余波荡漾,新城控股再陷退地风波在新城控股原董事长王振华事件后,有人曾说王振华的个人行为不等同于公司行为,舆论重点应该是被猥亵的女童。但是王振华猥亵案后,这位董事长直接影响了新城的表现。在个人的丑闻发酵下,新城及鲁迅去世48年后,一张胸片揭开其真正死因,日本庸医误诊原本可治1949年10月19日,新中国成立后不久,中央人民政府出版总署副署长周建人在人民日报上,发表鲁迅的病疑被须藤先生所耽误。周建人是鲁迅的弟弟,他在文章里对鲁迅的死因提出质疑,矛头直指中国的一票否决权,是打出来的!曾对哪些国家使用过?二战之后联合国的成立,是人类政治史中一件大事。联合国的成立为维护战后和平发挥了重大作用,其中安理会五大常任理事国美国英国法国俄罗斯(苏联)和中国因为拥有一票否决权,而成为其它国家羡
被日本街拍惊艳到!白发老人穿衣比年轻人更有品味,气质很高级提到时尚,似乎就是年轻人的专属,对于上了年纪的女人来说,可谓是和时尚搭不上边。但事实上,老年人的时尚感一点也不输年轻人。比如最近看到的一组日本街拍图,那些白发奶奶的穿搭比年轻人更有真正见过世面的女人,穿裙子不会配丝袜,看刘涛,董卿就知道了让你拿到一条裙子,你的第一反应会是什么?会选择什么单品来搭配它?相信绝大多数的一批人会选择丝袜组合,丝袜就好像是和裙子绑定在一起了一样,裙子搭丝袜也成了固定搭配。但其实它们两个真的热身赛1逆转阿尔巴尼亚格里福造3球迪洛伦佐破门北京时间11月17日凌晨3点45分,无缘世界杯的意大利队和阿尔巴尼亚队进行了一场热身赛。上半场,阿尔巴尼亚队先进一球,随后迪洛伦佐扳平,格里福进球反超下半场,格里福再入一球锁定胜局电动只是过渡,氢能才是未来!盘点各大车企氢能研发,中国不落后知嘹汽车陈欣中石化规划到2025年,在绿氢建设方面成为中国第一大氢能公司。在清华大学2022碳中和经济论坛上,中石化给出了明确的表态。作为中国最大的成品油和石化产品供应商,中石化的刘强东首次成为中国首善据媒体报道,据胡润研究院11月16日发布的2022胡润慈善榜,刘强东过去一年共捐赠149亿元,首次成为中国首善。刘强东又出名了!老叶为刘强东的善举点赞!据报道,刘强东的善款主要用于14亿人养不起中国高速?中国高速连续8年亏损,负债达到了7万亿我国的公路全境总里程已经达到了16。6万公里,稳居世界第一。这么长的里程收费站,每年能营收5000亿元,为什么很多专家说政府在亏欠?据交通主管部门统计,中国高速公路已经连续八年亏损9月中国美债持仓余额下降382亿美元,但仍净买入长期美债记者王玉美国财政部官网数据显示,截至9月末,中国持有美国国债9336亿美元,环比下降382亿美元,创2010年6月以来新低。其中,截至9月底,中国持有美国国库券(一年以下国债)32近年中国风力发电行业政策总结风电是一种新型清洁能源,主要用于偏远地区的电力供应,以及部分偏远地区的电力需求。全球风能总装机容量已达21亿千瓦人民币,其中风电占全球风电装机容量的12。全球风电市场规模目前已达到换位思考好处多在现实生活当中,难免会遇到很多无法称心如意的事情,即使是青少年也不例外,因为人人都是独立的个体,无论在何时何地,都没人能保证所有人都会理解你认同你。当然,你也不能够保证你一定会理解怀念母亲李学华公元2022年10月21日的中午,一个天青云白的日子,却为我刷出了一个阴霾的午后。就在这个深秋平凡日子的暖阳下,就在这个午后的12时41分,母亲突然离开了她生养的四个儿女,带人性密码第六篇规律本身就是残酷的人性都是对失去的锱铢必较,对得到的不太在意。你对别人的坏会在他心里留下一个伤疤,永久存在,但是你对他的好,只是他的一颗糖,吃了就化了。不懂人性,你付出越多伤害自己就越深。不要用嘴去