暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Eureka源码分析

Java Miraculous 2021-05-27
630
Eureka这篇文章的最后大概分析了Eureka的运行机制,我们都知道,服务注册中心、服务提供者、服务消费者是Eureka的三个主要元素,其中服务注册中心又叫做Eureka的server端,服务提供者和服务消费者又叫做Eureka的client端,client端在整个运行机制中是大部分通信行为的主动发起者,而server端主要是处理请求的接收者,所以源码,我们就从client作为入口开始分析,看看它是怎么完成这些主动通信行为的。
一、怎么将服务注册到Eureka或者从Eureka获取服务的
其实就是通过下面两步
  • 在应用的启动类上添加一个叫@EnableDiscoveryClient的注解

  • 指定服务注册中心的位置,也就是zone

关于zone,在Eureka中的region和zone中已经演示过它的用法了,这里再说下,对于访问实例的选择,Eureka中有region和zone的概念,一个region可以包含多个zone,每个Eureka的client端需要被注册到一个zone中,所以一个客户端对应一个region和一个zone,在进行服务调用的时候,优先访问处于同一个zone中的服务提供者,如果访问不到,再访问其他zone中的服务提供者。
@EnableDiscoveryClient注解的意思是开启DiscoveryClient实例,在idea中ctrl+n搜索DiscoveryClient(注意,在provider或者consumer对应的应用里搜,因为它们是client端)
首先是com.netflix.discovery.DiscoveryClient

看下类继承图:

ImplementedBy、Singleton这两个没用的去掉,然后在当前图谱里右键->Add Class to Diagram...添加org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient

因为EurekaDiscoveryClient实现了org.springframework.cloud.client.discovery.DiscoveryClient接口,所以继续右键->Add Class to Diagram...添加org.springframework.cloud.client.discovery.DiscoveryClient

然后再在图谱里右键->Show Dependencies

上图中,有几个关键的api:
  • DiscoveryClientorg.springframework.cloud.client.discovery.DiscoveryClient

springcloud框架里的,它定义了用来发现服务的常用抽象方法,通过该接口可以有效地屏蔽服务治理的实现细节,所以使用springcloud构建的微服务应用可以方便地切换不同服务治理框架,而不用改动程序代码,只需要另外添加一些针对服务治理框架的配置即可。
  • EurekaDiscoveryClientorg.springframework.cloud.netflix.eureka.EurekaDiscoveryClient

对上面DiscoveryClient接口的实现,从命名可以看出来,它就是对Eureka服务发现的封装,所以它依赖了Netflix Eureka的com.netflix.discovery.EurekaClient接口,EurekaClient继承了com.netflix.discovery.shared.LookupService接口,它们都是Netflix开源包中的内容,主要定义了针对Eureka服务发现的抽象方法,而真正实现服务发现的是Netflix包中的com.netflix.discovery.DiscoveryClient类。
  • DiscoveryClientcom.netflix.discovery.DiscoveryClient

大概翻译下类头部的注释如下
    这个类是用于和Eureka Server进行交互的,它主要有以下任务:
    向Euerka Server注册服务实例
    向Eureka Server续约服务
    当服务下掉后,向Eureka Server取消续约
    查询Eureka Server中的服务实例列表
    Eureka Client还需要配置一个Eureka Server的URL列表
    接下来对com.netflix.discovery.DiscoveryClient进行源码分析,看看Eureka是如何加载服务注册中心并获取和注册服务的。
    • 1.1、加载服务注册中心

    在具体研究Eureka Client负责完成的任务之前,我们先看看哪里对Eureka Server的URL列表进行配置,直接在DiscoveryClient类中ctrl+f搜索serviceUrl,然后找到getServiceUrlsFromConfig方法。

    很明显,从配置文件中加载serviceUrl,但是你注意下这个方法的注解,它过期了,方法注释里也有一个link地址,按着ctrl点击去。

    依旧搜索serviceUrl,注意看带config的,因为我们分析的是从配置文件中加载,可以发现有两个方法。

    那这俩方法有啥区别?
    • 先看getServiceUrlsFromConfig

      public static List<String> getServiceUrlsFromConfig(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone) {
      List<String> orderedUrls = new ArrayList<String>();
          //1、获取region
      String region = getRegion(clientConfig);
          //2、获取region对应的zone
      String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion());
          //如果没有对应的zone
      if (availZones == null || availZones.length == 0) {
      availZones = new String[1];
              //3、就默认zone为default
      availZones[0] = DEFAULT_ZONE;
      }
      logger.debug("The availability zone for the given region {} are {}", region, Arrays.toString(availZones));
          //4、获取当前client所在的zone的地址在配置文件的可用zone列表里的位置
      int myZoneOffset = getZoneOffset(instanceZone, preferSameZone, availZones);
          //5、因为serviceUrl其实就是zone:serviceUrl的地址,所以这里其实就是根据key去map中拿value了
      List<String> serviceUrls = clientConfig.getEurekaServerServiceUrls(availZones[myZoneOffset]);
      if (serviceUrls != null) {
              //如果获取到的serviceUrls不为空,就添加到要返回的集合里
      orderedUrls.addAll(serviceUrls);
      }
          //判断当前所处的zone是否在可用的zone列表的最后,如果不是的话,继续加载当前所处的zone后面的zone
      int currentOffset = myZoneOffset == (availZones.length - 1) ? 0 : (myZoneOffset + 1);
      while (currentOffset != myZoneOffset) {
              //从当前所处的zone的位置,开始,取后面所有的zone放进去,由此可见,当前所处的zone是放在第一个的,
              //而当前所处的zone前面的zone不会加载,后面的zone会加载
      serviceUrls = clientConfig.getEurekaServerServiceUrls(availZones[currentOffset]);
      if (serviceUrls != null) {
      orderedUrls.addAll(serviceUrls);
      }
      if (currentOffset == (availZones.length - 1)) {
      currentOffset = 0;
      } else {
      currentOffset++;
      }
      }
          //最后判断如果没有找到serviceUrl的话,就会抛出异常了
      if (orderedUrls.size() < 1) {
      throw new IllegalArgumentException("DiscoveryClient: invalid serviceUrl specified!");
      }
      返回加载的serviceUrl集合
      return orderedUrls;
      }
      上面给getServiceUrlsFromConfig方法加了注释,里面有五个小的子方法
      1、getRegion方法

      2、getAvailabilityZones
      这个是个多实现,选择EurekaClientConfigBean,因为是eureka

      方法中的availabilityZones属性是什么?

      其实就是配置中的availability-zones。

      3、默认的缺省值

      4、getZoneOffset
      首先看下这个这个方法的入参,它们和配置文件的对应关系如下:
      instanceZone:

      preferSameZone:

      availZones:

      getZoneOffset:

      5、getEurekaServerServiceUrls
      这个也是多实现,无脑Eureka那个

      经过上面的分析可知,取出来配置的zone对应的serviceUrl之后它并没有停止,而是把availZone中所有的zone对应的serviceUrl都加载出来了,注意,是从它配置的zone开始(在availZone中)以后的zone加载了,之前的并没有加载
      再看下getServiceUrlsMapFromConfig方法,你会发现,逻辑一毛一样,就是返回值是一个map,里面的key是zone,值是zone对应的serviceUrl。
      • 1.2、注册服务和获取服务

      下面分析下DiscoveryClient类是如何实现服务注册的,肯定是从构造方法入手了

      构造方法套构造方法,下面那个就是最终执行的,直接看最下面那个,可以看到方法是真的长,看源码只看关键的地方就行了,如果所有的都看,一会蒙了不说,还浪费时间,接下来就大概看下这个构造方法。

      根据上面的分析,注册服务的主要逻辑调用了另外一个方法,叫initScheduledTasks

      接下来就到注册服务的核心方法里了,InstanceInfoReplicator里的run方法

      最后看register

      • 1.3、注册中心怎么处理来注册的服务

      关于注册中心处理服务的类和接口
      LeaseManager是一个顶层接口,定义了四个方法

      然后InstanceRegistry继承自LeaseManager,同时扩展了很多方法,比如更新客户端状态,清空注册中心,获取最后注册的客户端等。

      最后AbstractInstanceRegistry是InstanceRegistry的实现类。有兴趣的可以继续探讨,这里就不再赘述了。
      分析了Eureka的源码之后,大家应该对Eureka的服务治理机制有了进一步的理解,在Eureka的服务治理体系中,主要分为服务端客户端两个角色,服务端为服务注册中心,而客户端是各个提供接口的微服务应用,服务注册中心也一样,只是在高可用的环境下,服务注册中心除了作为客户端以外,还为集群中的其它客户端提供了服务注册的特殊功能,所以Eureka客户端的配置对象存在于所有Eureka服务治理体系下的应用实例中,在实际应用Eureka的时候,我们所做的配置几乎都是对Eureka客户端进行的。
      关于服务注册类的配置信息,可以查看EurekaClientConfigBean这个类,里面的属性都有注释,不会的谷歌翻译下就可以了。

      服务实例类的配置信息,查看EurekaInstanceConfigBean这个类。

      好了,Eureka基本也就这些了,能做到会使用会配置明白它的大概原理就行了,源码实在不想看的也没问题的。
      福利,有宝宝的朋友,不要错过!!
      扫描下方小程序加入会员,首次整箱购买者赠送同等价值优惠劵,同时可享受积分抵现活动!!!!!

      我们有专门的渠道,能让你更便宜的拿到货!!

      都是一线品牌,质量可靠,支持溯源,欢迎选购!!!


      文章转载自Java Miraculous,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

      评论