Jersey是一个RESTFUL请求服务JAVA框架,与常规的JAVA编程使用的struts框架类似,它主要用于处理业务逻辑层。
获取注册信息接口
根据查看 eureka服务端会依赖一个eureka-core包 resources 一般代表资源
在resources包下,发现ApplicationsResource类提供了一个获取注册信息的接口getContainers
在这个方法getContainers里找到了一个key
紧接着,下面根据key 从responseCache.get方法获取服务实例信息
这里ResponseCacheImpl有两个属性
跟到responseCache.get(cachekey)内部,可以看到从一个本地只读缓存ConcurrentHashMap中获取名字为key的实例信息,有的话就直接返回了,没有的话
会从读写缓存readWriteCacheMap里面获取,拿到了的话就放到只读缓存readOnlyCacheMap中去
然后返回。 但是,读写缓存拿不到呢?
在调用responseCache.get(cacheKey)时,会触发ResponseCacheImpl的构造方法
这里面有个generatePayload方法,在初始化readWriteCacheMap的时候,会根据key从generatePayload()方法里面去拿, 注意 [这里有个读写缓存的回调方法]
在这里发现找到了服务端真正的获取注册表的地方 [AbstractInstanceRegistry]
到这可以看出,我们的客户端,先从只读缓存中拿取注册表,只读缓存拿不到,从读写缓存里面去拿,读写缓存里拿不到,调用读写缓存的回调方法,如果拿不到,直接从registry内存注册表里面去拿 Eureka三级缓存机制
registry就是服务端真正的缓存服务注册实例表
为什么要30s更新一次只读缓存,因为读写缓存它的数据更准确,它是离注册表更近的
以上是获取注册信息接口相关说明,下面看下客户端注册接口
往注册表registry增加实例信息 来到入口 ApplicationResource.addInstance方法
该方法末尾会调用registry.register注册方法
来到真正的register方法,将新的实例信息写入注册表的数据map里
注册表map结构
{
MICROSERVICE - PROVIDER - USER = {
localhost:microservice - provider - user:8002 = com.netflix.eureka.lease.Lease @2cd36af6,
localhost:microservice - provider - user:8001 = com.netflix.eureka.lease.Lease @600b7073
}
}
双层map 第一层 map的key是 微服务实例名称
第二层 map的key是 服务实例ip名称+端口号 value就是一些服务信息[客户端带过来的,比如存活时间、注册时间等等]
注册完毕之后 服务端的注册表信息就会改变
注册完主动调用清理缓存接口,清理服务端读写缓存。
每注册完一次,都会主动清理读写缓存使其失效,下一次客户端再从只读缓存里面拿信息,就会从最原始的注册表里面去拿信息
eureka为什么要这么设计?
解决频繁读写问题,读的时候每次从只读缓存,写的时候每次往最原始的注册表里写
如果线上有上千个实例,每个实例每一分钟都要操作多次注册表 [像定时续约、定时拉取等任务]
这些操作有可能读,有可能写,因为读写肯定是要互斥的,效率不高,所以有了只读缓存和读写缓存把读和写都分开了。
读写分离+多级缓存失效
每30s把读写缓存里面的数据更新到只读缓存里面去,读写缓存默认180s会自动过期,下次客户端会从原始注册表拿取
eureka源码bug,服务剔除不是默认90s没心跳的实例,而是180s没心跳的实例
服务续约的时候没有更新为当前时间,还加了一个duration,这里加了两次duration,正常有一处加就可以了。
最终服务剔除变成了180s [官方文档服务剔除90s]