Eureka源码系列 —— 4.服务注册
前言
本文讲解“服务注册”这个功能模块,这个模块涉及到client主动请求server进行服务注册,和server端对client端的请求进行处理。
client向server请求注册服务
client端服务注册的代码入口很难找,方法调用链路如下:
- DiscoveryClient#DiscoveryClient
- DiscoveryClient#initScheduledTasks
- InstanceInfoReplicator#start
- DiscoveryClient#register
- InstanceInfoReplicator#start
- DiscoveryClient#initScheduledTasks
真正的实现在com.netflix.discovery.InstanceInfoReplicator#start
InstanceInfoReplicator
实例信息复制器(吐槽一下这个类名,做的是注册的事情,为什么不叫注册器,而是叫复制器呢🙄)实现了Runnable
接口。start
方法里最终会调用一个jdk的定时调度线程池,默认40s后执行本类的run方法,并且每次执行完之后,会循环调度run方法。
这里说一下eureka对循环调用的实现:他没有用jdk线程的循环调度方法,而是每次都在任务中再次执行下一次的调度任务。感觉也挺巧妙的,但是不知道和原生提供的方法相比,有什么不同之处。
run方法内部会检查实例信息是否是脏数据,即是否变更,如果变更的话,重新将实例信息注册到server。我们第一次服务注册的时候,在start
方法中已经手动将数据置为脏数据,所以也会进行注册。服务注册的具体实现在DiscoveryClient#register
方法中。
饶了一大圈,绕回了DiscoveryClient类。这里有两点疑问:
- 为什么DiscoveryClient源码要把服务注册这么重要的功能,代码隐藏的这么深,感觉在存心难为读者。
- 为什么第一次注册要延迟40s注册呢?
疑问先放在这吧,感觉不是特别重要,去看DiscoveryClient#register
方法:
其实就是包装当前实例信息,发送http请求给server的REST接口,eureka的网络框架使用的是Jersey2,我们不对这个框架进行深究,直接给出发送请求的实现:com.netflix.discovery.shared.transport.jersey2.AbstractJersey2EurekaHttpClient#register
总结:其实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
这里吐槽一下ApplicationResource#addInstance
方法的实现,参数校验和aws云等不重要的逻辑就平铺在方法中,都不抽个子方法,个人认为挺不清晰的
com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#register
方法内部才是真正执行“服务注册”的地方:
这个方法内部会调用注册表父类AbstractInstanceRegistry
进行服务注册,父类方法:
父类的服务注册方法分为五步:
-
从原始注册表中取出目标应用的所有实例信息
-
判断是第一次来注册还是再次注册,处理各自分支的逻辑
- 如果是第一次来注册的话,设置两个和服务踢除有关的属性
-
将注册信息封装到 gMap中,放入原始注册表
-
如果来注册的实例是UP状态,更新信息并添加到最近变更队列,[和拉取增量注册表有关]
-
清缓存,缓存与拉取注册表有关
其中我们现阶段要关注的重点,是第三步,即将要注册的实例放入server端的一个map中,我们来看一下这个map:
这个维护在AbstractInstanceRegistry
对象内部的嵌套Map,就是我们常说的eureka注册中心的原始注册表。之所以称为原始注册表是因为eureka-server会维护缓存注册表,为了区分开,将最底层的注册表成为原始注册表。
将嵌套map展开,类似这样的数据结构:
Over.
总结
对象清单
AbstractInstanceRegistry#registry 服务端原始注册表
数据结构:ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>
InstanceInfo实例对象,主要包含租约信息leaseInfo:心跳续约时间等
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