Spring Cloud Ribbon 源码解析

专栏目录

  1. Spring Cloud OpenFeign 源码解析
  2. Spring Cloud Ribbon 源码解析
  3. Spring Cloud Alibaba Sentinel 源码解析
  4. Spring Cloud Gatway 源码解析
  5. Spring Cloud Alibaba Nacos 源码解析

代码准备

依赖关系

1+------------+ +------------+ 2| | | | 3| | | | 4| | | | 5| | | | 6| consumer +------------> | provider | 7| | RestTemplate | | 8| | | | 9| | | | 10| | | | 11+------------+ +------------+

pom 依赖

加入nacos 服务发现即可,内部引用了spring-cloud-ribbon相关依赖

1<dependency> 2 <groupid>com.alibaba.cloud</groupid> 3 <artifactid>spring-cloud-starter-alibaba-nacos-discovery</artifactid> 4</dependency>

调用客户端

我们这里以最简单的 RestTemplate 调用开始使用Ribbon

1@Bean 2@LoadBalanced 3public RestTemplate restTemplate() { 4 return new RestTemplate(); 5} 6 7// Controller 使用restTemplate 调用服务提供方接口 8ResponseEntity<string> forEntity = restTemplate.getForEntity("http://provider/req", String.class); 9

源码解析

创建调用拦截器

1. 获取全部 @LoadBalanced标记的RestTemplate

1public class LoadBalancerAutoConfiguration { 2 @LoadBalanced 3 @Autowired(required = false) 4 private List<resttemplate> restTemplates = Collections.emptyList(); 5}

2. 增加 LoadBalancerInterceptor 处理逻辑

  • 没有引入 spring-retry使用的是

    @Bean public LoadBalancerInterceptor ribbonInterceptor() { return new LoadBalancerInterceptor(); }

  • 引入 spring-retry 使用的是

    @Bean @ConditionalOnMissingBean public RetryLoadBalancerInterceptor ribbonInterceptor() { return new RetryLoadBalancerInterceptor(); }

  • LoadBalancerInterceptor 业务逻辑

    public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept() { final URI originalUri = request.getURI(); // http://demo-provider/req 截取 demo-provider 服务名称 String serviceName = originalUri.getHost();

    1 // 默认注入的 RibbonAutoConfiguration.RibbonLoadBalancerClient 2 return this.loadBalancer.execute(serviceName, 3 // 创建请求对象 4 this.requestFactory.createRequest(request, body, execution)); 5}

    }

执行拦截器

3. RibbonLoadBalancerClient执行

1//RibbonAutoConfiguration默认注入的RibbonLoadBalancerClient 2@Bean 3@ConditionalOnMissingBean(LoadBalancerClient.class) 4public LoadBalancerClient loadBalancerClient() { 5 return new RibbonLoadBalancerClient(springClientFactory()); 6}

4.execute执行

1public class RibbonLoadBalancerClient implements LoadBalancerClient { 2 public <t> T execute(){ 3 //获取具体的ILoadBalancer实现 4 ILoadBalancer loadBalancer = getLoadBalancer(serviceId); 5 6 // 调用ILoadBalancer 实现获取Server 7 Server server = getServer(loadBalancer, hint); 8 RibbonServer ribbonServer = new RibbonServer(serviceId, server, 9 isSecure(server, serviceId), 10 serverIntrospector(serviceId).getMetadata(server)); 11 12 //获取状态记录器,保存此次选取的server 13 RibbonLoadBalancerContext context = this.clientFactory 14 .getLoadBalancerContext(serviceId); 15 RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server); 16 T returnVal = request.apply(serviceInstance); 17 statsRecorder.recordStats(returnVal); 18 return returnVal; 19 } 20}

获取ILoadBalancer

5 SpringClientFactory

1// bean 工厂生成LoadBalancer 的实现 2protected ILoadBalancer getLoadBalancer(String serviceId) { 3 return this.springClientFactory.getLoadBalancer(serviceId); 4} 5 6// 具体生成逻辑看 RibbonClientConfiguration,这个Bean 只有工厂调用的时候才会创建 7@Bean 8@ConditionalOnMissingBean 9public ILoadBalancer ribbonLoadBalancer(IClientConfig config, 10ServerList<server> serverList, ServerListFilter<server> serverListFilter, 11IRule rule, IPing ping, ServerListUpdater serverListUpdater) { 12 return new ZoneAwareLoadBalancer&lt;&gt;(); 13}

6.创建LoadBalancer 的依赖要素

名称

默认实现

作用

IClientConfig

DefaultClientConfigImpl

ribbon 客户端配置参数,例如: 超时设置、压缩设置等

ServerList

NacosServerList

目标服务的实例实例表,具体服务发现客户端实现

ServerListFilter

ZonePreferenceServerListFilter

针对ServerList 实例列表的过滤逻辑处理

IRule

ZoneAvoidanceRule

负载均衡选择Server 的规则

IPing

DummyPing

检验服务是否可用的方法实现

ServerListUpdater

PollingServerListUpdater

针对ServerList 更新的操作实现

以上默认实现参考 RibbonClientConfiguration. ZoneAwareLoadBalancer

获取服务实例

1//Server server = getServer(loadBalancer, hint); 4. excute 方法 2protected Server getServer(ILoadBalancer loadBalancer, Object hint) { 3 return loadBalancer.chooseServer(hint != null ? hint : "default"); 4}

7. ZoneAwareLoadBalancer

1public class ZoneAwareLoadBalancer{ 2 public ZoneAwareLoadBalancer() { 3 // 调用父类初始化方法。 这里会开启实例维护的定时任务等 (具体解析参考 扩展部分) 4 super(clientConfig, rule, ping, serverList, filter, serverListUpdater); 5 } 6 @Override 7 public Server chooseServer(Object key) { 8 // 若是使用的 Nacos 服务发现,则没有 Zone 的概念,直接调用父类的实现 9 if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() &lt;= 1) { 10 return super.chooseServer(key); 11 } 12 // 以下为有 Zone 的概念 例如 Eureka (具体) 13 ... 14 return server; 15 } 16}
  • 父类调用IRule实现选择Server

    public Server chooseServer(Object key) { return rule.choose(key); }

8.PredicateBasedRule 选择规则

1public abstract class PredicateBasedRule { 2 @Override 3 public Server choose(Object key) { 4 ILoadBalancer lb = getLoadBalancer(); 5 // 获取断言配置 6 Optional<server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key); 7 if (server.isPresent()) { 8 return server.get(); 9 } else { 10 return null; 11 } 12 } 13}

9. ZoneAvoidancePredicate服务列表断言

1public class ZoneAvoidancePredicate { 2 @Override 3 public boolean apply(@Nullable PredicateKey input) { 4 if (!ENABLED.get()) { 5 return true; 6 } 7 // 还是获取区域配置,如是使用的 Nacos 直接返回true 8 String serverZone = input.getServer().getZone(); 9 if (serverZone == null) { 10 // there is no zone information from the server, we do not want to filter 11 // out this server 12 return true; 13 } 14 // 区域高可用判断 15 ... 16 } 17}

扩展: ServerList 维护

初始化ServerList

在上文 6.创建LoadBalancer 的依赖要素,中 ServerList 目标服务的实例实例表,具体服务发现客户端实现。我们来看下 Nacos 的实现

1public class NacosServerList extends AbstractServerList<nacosserver> { 2 @Override 3 public List<nacosserver> getInitialListOfServers() { 4 return getServers(); 5 } 6 7 @Override 8 public List<nacosserver> getUpdatedListOfServers() { 9 return getServers(); 10 } 11 12 private List<nacosserver> getServers() { 13 String group = discoveryProperties.getGroup(); 14 //调用nacos-sdk 查询实例列表 15 List<instance> instances = discoveryProperties.namingServiceInstance() 16 .selectInstances(serviceId, group, true); 17 // 类型转换 18 return instancesToServerList(instances); 19 20 } 21}

更新ServerListUpdater

  • ServerList 初始化后更新操作通过 PollingServerListUpdater

    public class PollingServerListUpdater implements ServerListUpdater { @Override public synchronized void start(final UpdateAction updateAction) { // 更新任务 交给updateAction 具体实现 final Runnable wrapperRunnable = () -> { updateAction.doUpdate(); lastUpdated = System.currentTimeMillis(); };

    1 // 开启后台线程定时执行 updateAction 2 scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay( 3 wrapperRunnable, 4 initialDelayMs, 5 refreshIntervalMs, 6 TimeUnit.MILLISECONDS 7 ); 8}

    }

  • updateAction 实现

    public void doUpdate() { DynamicServerListLoadBalancer.this.updateListOfServers(); }

    public class PollingServerListUpdater implements ServerListUpdater { public void updateListOfServers() { List<t> servers = new ArrayList();

    1 // 调用NacosServiceList 获取全部服务列表 2 servers = this.serverListImpl.getUpdatedListOfServers(); 3 4 // 如果配置实例过滤器在执行过滤 5 if (this.filter != null) { 6 servers = this.filter.getFilteredListOfServers((List)servers); 7 } 8 9 // 更新LoadBalancer 服务列表 10 this.updateAllServerList((List)servers); 11}

    }

扩展: Server 状态维护

  • LoadBalancer 初始构造时会触发 setupPingTask()

    public BaseLoadBalancer() { this.name = DEFAULT_NAME; this.ping = null; setRule(DEFAULT_RULE); // 开启ping 检查任务 setupPingTask(); lbStats = new LoadBalancerStats(DEFAULT_NAME); }

  • setupPingTask

    void setupPingTask() { // 是否可以ping, 默认的DummyPing 直接 跳过不执行 if (canSkipPing()) { return; } // 执行PingTask lbTimer.schedule(new BaseLoadBalancer.PingTask(), 0, pingIntervalSeconds * 1000); // 开启任务 new BaseLoadBalancer.Pinger(pingStrategy).runPinger(); }

  • SerialPingStrategy 串行执行逻辑

    // 串行调度执行 Iping 逻辑 private static class SerialPingStrategy implements IPingStrategy { @Override public boolean[] pingServers(IPing ping, Server[] servers) { int numCandidates = servers.length; boolean[] results = new boolean[numCandidates];

    1for (int i = 0; i &lt; numCandidates; i++) { 2 results[i] = false; /* Default answer is DEAD. */ 3 if (ping != null) { 4 results[i] = ping.isAlive(servers[i]); 5 } 6 7} 8return results;

    } }

  • 调用url 判断可用性

    public class PingUrl implements IPing { public boolean isAlive(Server server) { urlStr = urlStr + server.getId(); urlStr = urlStr + this.getPingAppendString(); boolean isAlive = false; HttpClient httpClient = new DefaultHttpClient(); HttpUriRequest getRequest = new HttpGet(urlStr); String content = null;

    1 HttpResponse response = httpClient.execute(getRequest); 2 content = EntityUtils.toString(response.getEntity()); 3 isAlive = response.getStatusLine().getStatusCode() == 200; 4 return isAlive; 5}

    }

扩展: RibbonClient 懒加载处理

由上文可知,默认情况下 Ribbon 在第一次请求才会去创建 LoadBalancer ,这种懒加载机制会导致服务启动后,第一次调用服务延迟问题,甚至在整合 断路器(hystrix)等出现超时熔断 。

为了解决这个问题,我们会配置 Ribbon 的饥饿加载

1ribbon: 2 eager-load: 3 clients: 4 - provider
  • RibbonApplicationContextInitializer 服务启动后自动调用 工厂提前创建需要的ribbon clients

    public class RibbonApplicationContextInitializer implements ApplicationListener<applicationreadyevent> { private final List<string> clientNames;

    1protected void initialize() { 2 if (clientNames != null) { 3 for (String clientName : clientNames) { 4 this.springClientFactory.getContext(clientName); 5 } 6 } 7} 8@Override 9public void onApplicationEvent(ApplicationReadyEvent event) { 10 initialize(); 11}

    }

后续计划

欢迎关注我,后边更新 RibbonHystrixSentinelNacos 等组件源码图文解析。

另注: 以上图片素材 (omnigraffle & 亿图) 可以在公众号 JAVA架构日记 获取

『★★★★★』 基于Spring Boot 2.2、 Spring Cloud Hoxton & Alibaba、 OAuth2 的RBAC 权限管理系统 > 项目推荐: Spring Cloud 、Spring Security OAuth2的RBAC权限管理系统 欢迎关注 </string></applicationreadyevent></t></instance></nacosserver></nacosserver></nacosserver></nacosserver></server></server></server></t></resttemplate></string>

点赞
收藏

评论区

加载中...

相关推荐

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Spring5.0源码学习系列之浅谈Bean创建过程(六)

系列文章目录提示:Spring源码学习系列博客专栏(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fblog.csdn.net%2Fu014427391%2Fcategory_10493299.html)文章目录系列文章目录(h

Spring5.0源码学习系列之Spring AOP简述(九)

前言介绍附录:Spring源码学习专栏(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fblog.csdn.net%2Fu014427391%2Fcategory_10493299.html)在前面章节的学习中,我们对Spring框架的IOC实现源码有了一定的了解,接着

Discuz X3.2源码解析 discuz_application类(转自百度)

1.discuz\_application在/source/class/discuz/discuz\_application.php中。!DiscuzX3.2源码解析discuz_application类(https://oscimg.oschina.net/oscnet/99b35d79caf70b7c74ad0838d6

1:dubbo集成spring

dubbo源码地址(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Falibaba%2Fdubbo)查找解析类DubboBeanDefinitionParserdubbo通过spring提供的自定义namespace来解析自己定义的标签,读取META

Android Drawable完全解析(一):Drawable源码分析(下)

AndroidDrawable完全解析(一):Drawable源码分析(上)(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.jianshu.com%2Fp%2F384a70897ba6)AndroidDrawable完全解析(一):Drawable源码分析(中)(htt

Spring Cloud Ribbon 源码解析 - HelloWorld