Eureka源码系列 —— 4.服务注册

前言

本文讲解“服务注册”这个功能模块,这个模块涉及到client主动请求server进行服务注册,和server端对client端的请求进行处理。

client向server请求注册服务

client端服务注册的代码入口很难找,方法调用链路如下:

  • DiscoveryClient#DiscoveryClient
    • DiscoveryClient#initScheduledTasks
      • InstanceInfoReplicator#start
        • DiscoveryClient#register

真正的实现在com.netflix.discovery.InstanceInfoReplicator#start

Eureka源码系列 —— 4.服务注册

InstanceInfoReplicator实例信息复制器(吐槽一下这个类名,做的是注册的事情,为什么不叫注册器,而是叫复制器呢??)实现了Runnable接口。start方法里最终会调用一个jdk的定时调度线程池,默认40s后执行本类的run方法,并且每次执行完之后,会循环调度run方法。

这里说一下eureka对循环调用的实现:他没有用jdk线程的循环调度方法,而是每次都在任务中再次执行下一次的调度任务。感觉也挺巧妙的,但是不知道和原生提供的方法相比,有什么不同之处。

run方法内部会检查实例信息是否是脏数据,即是否变更,如果变更的话,重新将实例信息注册到server。我们第一次服务注册的时候,在start方法中已经手动将数据置为脏数据,所以也会进行注册。服务注册的具体实现在DiscoveryClient#register方法中。

饶了一大圈,绕回了DiscoveryClient类。这里有两点疑问:

  1. 为什么DiscoveryClient源码要把服务注册这么重要的功能,代码隐藏的这么深,感觉在存心难为读者。
  2. 为什么第一次注册要延迟40s注册呢?

疑问先放在这吧,感觉不是特别重要,去看DiscoveryClient#register方法:

Eureka源码系列 —— 4.服务注册

其实就是包装当前实例信息,发送http请求给server的REST接口,eureka的网络框架使用的是Jersey2,我们不对这个框架进行深究,直接给出发送请求的实现:com.netflix.discovery.shared.transport.jersey2.AbstractJersey2EurekaHttpClient#register

Eureka源码系列 —— 4.服务注册

总结:其实client向server请求注册服务的逻辑比较简单,就是延迟40s向eureka-server发送一个http请求,请求中携带自己的服务实例信息instanceInfo。每个四十秒会检查一次实例信息是否有更新,如果有更新,再次将实例信息注册到服务端

server端处理client的服务注册请求

server端请求”服务注册“的方法在 com.netflix.eureka.resources.ApplicationsResource#getApplicationResource

这里设计到eureka使用的mvc框架——jersey,这里不会对这个框架进行详细的讲解,能找到请求入口,不耽误我们找到eureka的核心源码就达到目的了。

调用链路:

  • ApplicationsResource#getApplicationResource
    • ApplicationResource#addInstance
      • 参数校验
      • 判断是否在aws云上
      • PeerAwareInstanceRegistryImpl#register

这里吐槽一下ApplicationResource#addInstance方法的实现,参数校验和aws云等不重要的逻辑就平铺在方法中,都不抽个子方法,个人认为挺不清晰的

Eureka源码系列 —— 4.服务注册

com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#register方法内部才是真正执行“服务注册”的地方:

Eureka源码系列 —— 4.服务注册

这个方法内部会调用注册表父类AbstractInstanceRegistry进行服务注册,父类方法:

Eureka源码系列 —— 4.服务注册

父类的服务注册方法分为五步:

  1. 从原始注册表中取出目标应用的所有实例信息

  2. 判断是第一次来注册还是再次注册,处理各自分支的逻辑

    1. 如果是第一次来注册的话,设置两个和服务踢除有关的属性
  3. 将注册信息封装到 gMap中,放入原始注册表

  4. 如果来注册的实例是UP状态,更新信息并添加到最近变更队列,[和拉取增量注册表有关]

  5. 清缓存,缓存与拉取注册表有关

其中我们现阶段要关注的重点,是第三步,即将要注册的实例放入server端的一个map中,我们来看一下这个map:

Eureka源码系列 —— 4.服务注册

这个维护在AbstractInstanceRegistry对象内部的嵌套Map,就是我们常说的eureka注册中心的原始注册表。之所以称为原始注册表是因为eureka-server会维护缓存注册表,为了区分开,将最底层的注册表成为原始注册表

将嵌套map展开,类似这样的数据结构:

Over.

总结

Eureka源码系列 —— 4.服务注册

对象清单

AbstractInstanceRegistry#registry 服务端原始注册表

数据结构:ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>

InstanceInfo实例对象,主要包含租约信息leaseInfo:心跳续约时间等

Eureka源码系列 —— 4.服务注册

Application 注册的应用

即注册到eureka的应用,一个应用一个Application,比如UserTask就是一个Application.

源码类:com.netflix.discovery.shared.Application。

Application实例属性中保存了实例对象集合——Set

配置参数

eureka官方并没有提供完整的配置文件列表,这里是他们对配置的描述:https://github.com/Netflix/eureka/wiki/Configuring-Eureka,所有有一些配置我们要结合源码去网上搜一下,这里列出源码中的key。

前缀namespace,默认为eureka

是否进行服务注册

registration.enabled

默认true

定时检查是否需要注册的时间/第一次注册的延时时间

appinfo.initial.replicate.time

默认40s

Eureka源码系列 —— 4.服务注册

上一篇:界面原型设计——在线教育app


下一篇:Android源码目录结构