一、服务注册的入口
第一种方式是,对实现了SmartLifeCycle接口的Bean,在Spring boot应用启动的时候最终调用DefaultLifeCycleProcessor.doStart()方法循环调用所有实现了SmartLifeCycle接口的Bean中的start方法;EurekaAutoServiceRegistration实现了SmartLifeCycle接口,其中start方法最终调用StatusChangeListener.notify进行服务状态变更的监听,而这个监听的方法受到事件之后会去执行服务注册
EurekaClientAutoConfiguration自动装配的时候,会将CloudEurekaClient注入到了容器,CloudEurekaClient构造方法会调用父类DiscoveryClient,DiscoveryClient初始化的时候,发起服务注册register();通过InitScheduledTasks建立心跳检测机制,通过内部类来实例化StatusChangeListener实例状态监控接口,定时上报服务状态;regiseter()调用**AbstractJerseyEurekaHttpClient.register()**会去请求apps/服务名
二、Eureka Server收到请求之后的处理
发起请求之后,其实就是调用服务端的接口ApplicationResource.addInstance(),
最终会调用PeerAwareInstanceRegistryImpl.register()方法,这里边的逻辑包含,通过leaseDuration设置了续约过期时间,默认90s;也就是当服务端超过90s没有接受到客户端的心跳则主动剔除该节点;调用super.register()中的注册方法;将信息复制到Eureka Server集群中的其他机器上,同步的实现也很简单,就是获得集群中所有的节点,然后逐个发起注册。
下边进入到AbstractInstanceRegistry.regsiter()方法中,实际上是将客户端传递过来的实例数据保存到Eureka-server中的ConcurrentHashMap中;首先根据appName获取当前实例信息,如果AppName是第一次注册,就初始化一个ConcurrentHashMap;然后根据InstanceInfo.id获取具体实例,与客户端比较,以最新的最新的为主。 当原来存在Lease的信息时,设置serviceUpTimestamp, 保证服务启动的时间一直是第一次注册的那个;将新的实例数据放到gMap中完成注册,将变化加到 队列recentlyChangedQueue用于增量获取,设置让缓存失效。
三、Eureka 的多级缓存设计
Eureka Server存在三个变量:(registry、readWriteCacheMap、readOnlyCacheMap)保存服务注册信息,默认情况下定时任务每30s将readWriteCacheMap同步至readOnlyCacheMap,每60s清理超过90s未续约的节点,Eureka Client每30s从readOnlyCacheMap更新服务注册信息,而客户端服务的注册则从registry更新服务注册信息。
多级缓存的意义
这里为什么要设计多级缓存呢?原因很简单,就是当存在大规模的服务注册和更新时,如果只是修改一个ConcurrentHashMap数据,那么势必因为锁的存在导致竞争,影响性能。
而Eureka又是AP模型,只需要满足最终可用就行。所以它在这里用到多级缓存来实现读写分离。注册方法写的时候直接写内存注册表,写完表之后主动失效读写缓存。
获取注册信息接口先从只读缓存取,只读缓存没有再去读写缓存取,读写缓存没有再去内存注册表里取(不只是取,此处较复杂)。并且,读写缓存会更新回写只读缓存
responseCacheUpdateIntervalMs : readOnlyCacheMap 缓存更新的定时器时间间
隔,默认为30秒responseCacheAutoExpirationInSeconds : readWriteCacheMap 缓存过期时间,默认为 180 秒
在AbstractInstanceRegistry.regsiter()方法中调用invalidateCache方法让写缓存失效;写缓存失效后,会定期向读缓存更新数据,在ResponseCacheImpl构造方法中启动一个getCacheUpdateTask来判断写缓存与读缓存的值是否一致,如果不一致则更新读缓存。