eureka client初始化的时候,就会自动发送个请求到eureka server拉一次性抓取全量的注册表,我们来看看eureka server端如何处理抓取全量注册表的请求的,eureka client发送的请求是:http://localhost:8080/v2/apps/,get请求
ApplicationsResource的getContainers()方法,获取全量注册表的方法
@Path("/{version}/apps")
@Produces({"application/xml", "application/json"})
public class ApplicationsResource {
@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;
}
}
eureka server端,支持你来读取注册表的时候,搞了一套短小精干的多级缓存机制
也就是说,你eureka client发送请求过来读取全量注册表的时候,其实会从多级缓存里去读取注册表的数据,所以这里的cacheKey,就是全量注册表的缓存key
ResponseCache,就是eureka server端的缓存机制,通过 responseCache.getGZIP(cacheKey) 方法来获取缓存
public byte[] getGZIP(Key key) {
Value payload = getValue(key, shouldUseReadOnlyResponseCache);
if (payload == null) {
return null;
}
return payload.getGzipped();
}
接着调用getValue(key, shouldUseReadOnlyResponseCache)方法实现多级缓存的逻辑
多级缓存机制,用了两个map,来做了两级缓存,只读缓存map,读写缓存map,先从只读缓存里去读,如果没有的话,会从读写缓存里去读。
/**
* Get the payload in both compressed and uncompressed form.
*/
@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;
}
如果读写缓存readWriteCacheMap还是没有呢?如果这个读写缓存,没有缓存的话,会从eureka server的注册表中去读取
从注册表中获取所有的Applications,ServerCodecs,json序列化的组件,将Applications对象序列化为了一个json字符串,将注册表中读取出来的Applications,放入读写缓存,接着放入只读缓存中去
最后呢,就是将从多级缓存机制中读取出来的全量的Applications作为响应来返回
最后总结一张 eureka-server的多级缓存机制 流程图: