Eureka系列(四) 获取服务Server端具体实现
获取服务 Server端流程
我们先看下面这张图片,这张图片简单描述了下我们EurekaClient在调用EurekaServer 提供的获取服务Http接口,Server端实现接口执行的大致流程,图中还包含了服务注册的大致流程,因为服务注册和获取服务有关联的部分,因此两个流程合到了一起
Eureka 二级缓存
我们先看看我们Eureka二级缓存的结构:
// 一级缓存 只读缓存
private final ConcurrentMap<Key, Value> readOnlyCacheMap = new ConcurrentHashMap<Key, Value>();
// 二级缓存 读写缓存
private final LoadingCache<Key, Value> readWriteCacheMap;
上面我们看到一级缓存是由我们jdk自带的ConcurrentHashMap实现,而我们的二级缓存却是有google提供的guava包中LoadingCache实现。我们接着看下Eureka二级缓存readWriteCacheMap的初始化:
this.readWriteCacheMap =
CacheBuilder.newBuilder().initialCapacity(1000)
.expireAfterWrite(serverConfig.getResponseCacheAutoExpirationInSeconds(), TimeUnit.SECONDS)
.removalListener(new RemovalListener<Key, Value>() {
@Override
public void onRemoval(RemovalNotification<Key, Value> notification) {
Key removedKey = notification.getKey();
if (removedKey.hasRegions()) {
Key cloneWithNoRegions = removedKey.cloneWithoutRegions();
regionSpecificKeys.remove(cloneWithNoRegions, removedKey);
}
}
})
.build(new CacheLoader<Key, Value>() {
@Override
public Value load(Key key) throws Exception {
if (key.hasRegions()) {
Key cloneWithNoRegions = key.cloneWithoutRegions();
regionSpecificKeys.put(cloneWithNoRegions, key);
}
Value value = generatePayload(key);
return value;
}
});
不太了解LoadingCache类的小伙伴可以自行百度了解下,上面的代码简单描述就是设置了缓存180s会自行过期以及如果我们调用get()方法获取数据,查询不到对应的缓存则会执行load方法,从而得到key对应的数据。
获取服务Server端实现源码分析
获取服务的流程就想文章顶部文章描述的,首先会去查只读(一级)缓存(前提是没有配置只读缓存为false),如果只读缓存没有对应数据,则去查读写缓存(二级)缓存,如果读写缓存也没有,则会触发LoadingCache()的load方法,从内存中读取存取的实例信息。
下面我们通过源码再来看看整个流程:
private final ResponseCache responseCache;
@GET
public Response getContainers(@PathParam("version") String version,
@HeaderParam(HEADER_ACCEPT) String acceptHeader,
@HeaderParam(HEADER_ACCEPT_ENCODING) String acceptEncoding,
@HeaderParam(EurekaAccept.HTTP_X_EUREKA_ACCEPT) String eurekaAccept,
@Context UriInfo uriInfo,
@Nullable @QueryParam("regions") String regionsStr) {
boolean isRemoteRegionRequested = null != regionsStr && !regionsStr.isEmpty();
String[] regions = null;
if (!isRemoteRegionRequested) {
EurekaMonitors.GET_ALL.increment();
} else {
regions = regionsStr.toLowerCase().split(",");
Arrays.sort(regions); // So we don't have different caches for same regions queried in different order.
EurekaMonitors.GET_ALL_WITH_REMOTE_REGIONS.increment();
}
// Check if the server allows the access to the registry. The server can
// restrict access if it is not
// ready to serve traffic depending on various reasons.
if (!registry.shouldAllowAccess(isRemoteRegionRequested)) {
return Response.status(Status.FORBIDDEN).build();
}
CurrentRequestVersion.set(Version.toEnum(version));
KeyType keyType = Key.KeyType.JSON;
String returnMediaType = MediaType.APPLICATION_JSON;
if (acceptHeader == null || !acceptHeader.contains(HEADER_JSON_VALUE)) {
keyType = Key.KeyType.XML;
returnMediaType = MediaType.APPLICATION_XML;
}
Key cacheKey = new Key(Key.EntityType.Application,
ResponseCacheImpl.ALL_APPS,
keyType, CurrentRequestVersion.get(), EurekaAccept.fromString(eurekaAccept), regions
);
Response response;
if (acceptEncoding != null && acceptEncoding.contains(HEADER_GZIP_VALUE)) {
// 取压缩的缓存数据
response = Response.ok(responseCache.getGZIP(cacheKey))
.header(HEADER_CONTENT_ENCODING, HEADER_GZIP_VALUE)
.header(HEADER_CONTENT_TYPE, returnMediaType)
.build();
} else {
// 取缓存数据
response = Response.ok(responseCache.get(cacheKey))
.build();
}
return response;
}
responseCache.getGZIP(cacheKey) 和 responseCache.get(cacheKey)方法的区别是getGZIP方法是压缩后的实例信息,但这两个方法最终都会调用getValue()方法,如下所示:
public byte[] getGZIP(Key key) {
Value payload = getValue(key, shouldUseReadOnlyResponseCache);
if (payload == null) {
return null;
}
return payload.getGzipped();
}
public String get(final Key key) {
return get(key, shouldUseReadOnlyResponseCache);
}
@VisibleForTesting
String get(final Key key, boolean useReadOnlyCache) {
Value payload = getValue(key, useReadOnlyCache);
if (payload == null || payload.getPayload().equals(EMPTY_PAYLOAD)) {
return null;
} else {
return payload.getPayload();
}
}
&emps;我们接着看getValue这个方法,代码如下:
// 只读缓存,使用concurrentHashMap实现
private final ConcurrentMap<Key, Value> readOnlyCacheMap = new ConcurrentHashMap<Key, Value>();
// 读写缓存,使用google提供的LoadingCache实现
private final LoadingCache<Key, Value> readWriteCacheMap;
@VisibleForTesting
Value getValue(final Key key, boolean useReadOnlyCache) {
Value payload = null;
try {
if (useReadOnlyCache) {
final Value currentPayload = readOnlyCacheMap.get(key);
if (currentPayload != null) {
payload = currentPayload;
} else {
payload = readWriteCacheMap.get(key);
readOnlyCacheMap.put(key, payload);
}
} else {
payload = readWriteCacheMap.get(key);
}
} catch (Throwable t) {
logger.error("Cannot get value for key : {}", key, t);
}
return payload;
}
由上可见,我们获取实例信息,会先进行2次判断,判断如果是否启用了只读缓存,如果没有启用则直接从读写缓存中读取,启用了读写缓存,则我们会先尝试从读写缓存中读取数据,如果为空则从读写缓存中读取,然后再把数据put进只读缓存。
注意:readWriteCacheMap.get(key)这个方法如果在原本的LoadingCache中查询不到数据,则会调用load方法取key对应的数据,最终返回给我们对应的数据。
接下来我们来看看我们的只读缓存和读写缓存之间是咋进行更新的:
ResponseCacheImpl(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, AbstractInstanceRegistry registry) {
this.serverConfig = serverConfig;
this.serverCodecs = serverCodecs;
// 是否使用只读缓存
this.shouldUseReadOnlyResponseCache = serverConfig.shouldUseReadOnlyResponseCache();
this.registry = registry;
// 缓存更新的时间间隔(用定时器更新,定时器的时间默认30秒执行一次)
long responseCacheUpdateIntervalMs = serverConfig.getResponseCacheUpdateIntervalMs();
// 构建读写缓存 默认缓存时间180秒
this.readWriteCacheMap =
CacheBuilder.newBuilder().initialCapacity(1000)
.expireAfterWrite(serverConfig.getResponseCacheAutoExpirationInSeconds(), TimeUnit.SECONDS)
.removalListener(new RemovalListener<Key, Value>() {
@Override
public void onRemoval(RemovalNotification<Key, Value> notification) {
Key removedKey = notification.getKey();
if (removedKey.hasRegions()) {
Key cloneWithNoRegions = removedKey.cloneWithoutRegions();
regionSpecificKeys.remove(cloneWithNoRegions, removedKey);
}
}
})
// 缓存加载器,当缓存不存在时,会自动执行load方法,进行缓存加载。同时返回缓存数据
.build(new CacheLoader<Key, Value>() {
@Override
public Value load(Key key) throws Exception {
if (key.hasRegions()) {
Key cloneWithNoRegions = key.cloneWithoutRegions();
regionSpecificKeys.put(cloneWithNoRegions, key);
}
Value value = generatePayload(key);
return value;
}
});
// 使用只读缓存,如果使用,此处则启动一个定时器,用来复制readWriteCacheMap 的数据至readOnlyCacheMap
if (shouldUseReadOnlyResponseCache) {
timer.schedule(getCacheUpdateTask(),
new Date(((System.currentTimeMillis() / responseCacheUpdateIntervalMs) * responseCacheUpdateIntervalMs)
+ responseCacheUpdateIntervalMs),
responseCacheUpdateIntervalMs);
}
try {
Monitors.registerObject(this);
} catch (Throwable e) {
logger.warn("Cannot register the JMX monitor for the InstanceRegistry", e);
}
}
// 复制readWriteCacheMap 的数据至readOnlyCacheMap
private TimerTask getCacheUpdateTask() {
return new TimerTask() {
@Override
public void run() {
logger.debug("Updating the client cache from response cache");
for (Key key : readOnlyCacheMap.keySet()) {
if (logger.isDebugEnabled()) {
logger.debug("Updating the client cache from response cache for key : {} {} {} {}",
key.getEntityType(), key.getName(), key.getVersion(), key.getType());
}
try {
CurrentRequestVersion.set(key.getVersion());
Value cacheValue = readWriteCacheMap.get(key);
Value currentCacheValue = readOnlyCacheMap.get(key);
if (cacheValue != currentCacheValue) {
readOnlyCacheMap.put(key, cacheValue);
}
} catch (Throwable th) {
logger.error("Error while updating the client cache from response cache for key {}", key.toStringCompact(), th);
}
}
}
};
}
由上可知,我们Server启动了两个定时任务,只读缓存定时任务使用java.util.Timer timer类实现,读写缓存是由geegle guava LoadingCache实现。只读缓存定时设置30s,即每隔30s则会读取读写缓存中数据用来更新只读缓存中的数据。而我们的读写缓存则是设置的180s过期。
Eureka系列(四) 获取服务Server端具体实现的更多相关文章
- Eureka系列(三)获取服务Client端具体实现
获取服务Client 端流程 我们先看下面这张图片,这张图片简单描述了下我们Client是如何获取到Server已续约实例信息的流程: 从图片中我们可以知晓大致流程就是Client会自己开启一个 ...
- [Python Study Notes]CS架构远程访问获取信息--SERVER端v2.0
更新内容: 1.增加内存信息获取 2.增加电池信息获取 3.增加磁盘信息获取 4.重新布局窗体 5.增加窗体名称 6.增加连接成功之前,不可按压 ''''''''''''''''''''''''''' ...
- [Python Study Notes]CS架构远程访问获取信息--SERVER端
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ...
- springcloud系列四 搭建服务模块重点讲解
首先这个服务地址:一定不要写错,是自己注册中心开启的地址 如果注意到这些了,可以简单的进行操作,也可以不需要mybatis与数据库连接,在controller里直接返回相应的数据可以了,不用这么幸苦的 ...
- SpringCloud系列四:Eureka 服务发现框架(定义 Eureka 服务端、Eureka 服务信息、Eureka 发现管理、Eureka 安全配置、Eureka-HA(高可用) 机制、Eureka 服务打包部署)
1.概念:Eureka 服务发现框架 2.具体内容 对于服务发现框架可以简单的理解为服务的注册以及使用操作步骤,例如:在 ZooKeeper 组件,这个组件里面已经明确的描述了一个服务的注册以及发现操 ...
- Eureka系列(二) 服务注册Server端具体实现
服务注册 Server端流程 我们先看下面这张图片,这张图片简单描述了下我们EurekaClient 在调用EurekaServer 提供的服务注册Http接口,Server端实现接口执行的大致流 ...
- Eureka系列(七) 服务下线Server端具体实现
服务下线的大致流程图 下面这张图很简单地描述了Server端服务下线的大致流程: 服务下线Server端实现源码分析 Eureka服务实现是通过Server端InstanceResource ...
- Eureka 系列(07)服务注册与主动下线
Eureka 系列(07)服务注册与主动下线 [TOC] Spring Cloud 系列目录 - Eureka 篇 在上一篇 Eureka 系列(05)消息广播 中对 Eureka 消息广播的源码进行 ...
- java版gRPC实战之六:客户端动态获取服务端地址
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
随机推荐
- HBase高级特性、rowkey设计以及热点问题处理
在阐述HBase高级特性和热点问题处理前,首先回顾一下HBase的特点:分布式.列存储.支持实时读写.存储的数据类型都是字节数组byte[],主要用来处理结构化和半结构化数据,底层数据存储基于hdfs ...
- Mac太卡了怎么办?用CleanMyMac四招让它飞起来
许多小伙伴使用Mac后都反馈电脑不如想象中的流畅,甚至有点卡顿的现象,原因可能是因为无用的应用占据了过多的内存,或者是系统盘垃圾过多,导致的电脑卡顿现象. 今天小编教给大家几招,让自己的Mac能够一键 ...
- mac用户怎么保护自己的隐私安全?
使用过Windows系统的小伙伴们应该都知道,Windows系统下有360电脑管家和腾讯电脑管家等几款著名清理软件,专门用于清理电脑缓存.垃圾文件以及清除浏览痕迹,这对于Windows用户是大大节省了 ...
- 对于AQS的理解
1.JUC包中的 CountDownLatch.CyclicBarrier.ReentrantLock和Semaphore都是基于AQS(AbstractQuenedSynchronizer)实现的 ...
- Contest 985
A 均移到黑色或白色即可. 时间复杂度 \(O\left(n\log n\right)\). B 枚举每种开关判断是否有灯只能靠该种开关控制. 时间复杂度 \(O\left(nm\right)\). ...
- 以注解的方式实现api和provider
1.provider import com.alibaba.dubbo.config.annotation.Service; import facade.EchoService; import com ...
- SpringBoot整合阿里短信服务
导读 由于最近手头上需要做个Message Gateway,涉及到:邮件(点我直达).短信.公众号(点我直达)等推送功能,网上学习下,整理下来以备以后使用. 步骤 点我直达 登录短信服务控制台 点我直 ...
- vue跨域请求
浏览器的同源策略 同源 协议相同 域名相同 端口相同 同源目的 保证用户信息安全,防止恶意的网站窃取数据 同源策略解决方法 jsonp cors 代理解决跨域 settings.py INSTALLE ...
- od中低位地址和高位的顺序,以及数据的存放读写
在观察内存的时候应当注意"内存数据"与"数值数据"的区别. 在我们的调试环境中,内存由低到高分布,你可以简单地把这种情形理解成Win32系统在内存中由地位向高位 ...
- drf的权限扩充
drf框架为我们提供了基本的权限验证.主要包括三种验证 1.AllowAny 所有用户 2.IsAuthenticated 验证过的用户 3.IsAdminUser 超级管理员 这些权限人员不一定满足 ...