1、在使用eureka的时候,我们分别演示了服务端的使用以及客户端的使用,今天我们来分析一下eureka服务是如何实现的,我们先抛出几个问题:
1.1、eureka服务端是如何整合spring-cloud的?
1.2、eureka服务端是如何存储服务实例数据的?
1.3、eureka服务端是如何跟客户端进行交互的?
2、上面抛出两个问题接下来我们一 一解答:
2.1、解答eureka服务端是如何整合spring-cloud的?
答:我们先来看看构建eureka服务端的时候我们的maven依赖:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
我们依赖了spring-boot-starter-web,那么我们肯定会启动一个web服务,我们又依赖了spring-cloud-starter-netflix-eureka-server,我们来看看spring-cloud-starter-netflix-eureka-server的核心maven依赖:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-netflix-eureka-server</artifactId><version>2.2.5.RELEASE</version><scope>compile</scope></dependency>
由上面可以看出主要就是依赖了spring-cloud-netflix-eureka-server,我们找到spring-cloud-netflix-eureka-server这个jar包,如下图:果然发现了spring.factories文件
我们打开包下面的spring.factories文件,其内容如下:不了解spring.factories的请异步我的spring boot相关博客(手动狗头);
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
那么我们自然就打开 EurekaServerAutoConfiguration 这个类,这个类太长,不一 一 列举代码,我们找核心的地方讲解:
Ⅰ:先看这个自动装配类的激活条件:核心的激活条件就是@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class),也就是需要存在Marker的Bean定义,那么这个Bean定义是如何添加的呢?答案就在注解@EnableEurekaServer 上面;
@Configuration(proxyBeanMethods = false)导入EurekaServer的初始者配置类@Import(EurekaServerInitializerConfiguration.class)当前的spring boot上下文中需要有EurekaServerMarkerConfiguration.Marker的B定义@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)@EnableConfigurationProperties({ EurekaDashboardProperties.class,InstanceRegistryProperties.class })@PropertySource("classpath:/eureka/server.properties")public class EurekaServerAutoConfiguration implements WebMvcConfigurer {
@EnableEurekaServer 注解源码:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(EurekaServerMarkerConfiguration.class)public @interface EnableEurekaServer {}@Configuration(proxyBeanMethods = false)public class EurekaServerMarkerConfiguration {添加一个Marker的Bean定义@Beanpublic Marker eurekaServerMarkerBean() {return new Marker();}class Marker {}}
Ⅱ:EurekaServerAutoConfiguration 自动装配的核心代码
@Configuration(proxyBeanMethods = false)
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration implements WebMvcConfigurer {/*** List of packages containing Jersey resources required by the Eureka server.*/private static final String[] EUREKA_PACKAGES = new String[] {"com.netflix.discovery", "com.netflix.eureka" };@Autowiredprivate ApplicationInfoManager applicationInfoManager;@Autowiredprivate EurekaServerConfig eurekaServerConfig;@Autowiredprivate EurekaClientConfig eurekaClientConfig;@Autowiredprivate EurekaClient eurekaClient;@Autowiredprivate InstanceRegistryProperties instanceRegistryProperties;. . . 配置一个实例注册器,默认使用的是InstanceRegistry@Beanpublic PeerAwareInstanceRegistry peerAwareInstanceRegistry(ServerCodecs serverCodecs) {this.eurekaClient.getApplications(); // force initializationreturn new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,serverCodecs, this.eurekaClient,this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(),this.instanceRegistryProperties.getDefaultOpenForTrafficCount());}配置一个eureka server 节点管理器@Bean@ConditionalOnMissingBeanpublic PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry,ServerCodecs serverCodecs,ReplicationClientAdditionalFilters replicationClientAdditionalFilters) {return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig,this.eurekaClientConfig, serverCodecs, this.applicationInfoManager,replicationClientAdditionalFilters);}配置一个eureka server 上下文类@Bean@ConditionalOnMissingBeanpublic EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs,PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,registry, peerEurekaNodes, this.applicationInfoManager);}配置一个eureka serve 的引导类@Beanpublic EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry,EurekaServerContext serverContext) {return new EurekaServerBootstrap(this.applicationInfoManager,this.eurekaClientConfig, this.eurekaServerConfig, registry,serverContext);}. . .
}
我们先来看看这个自动配置类上的如下代码:含义就是导入 EurekaServerInitializerConfiguration 这个配置类, 我们来看看这个配置类到底做了什么事情:
@Import(EurekaServerInitializerConfiguration.class)
@Configuration(proxyBeanMethods = false)实现了ServletContextAware 感知器,以及生命周期接口SmartLifecycle
public class EurekaServerInitializerConfigurationimplements ServletContextAware, SmartLifecycle, Ordered {private static final Log log = LogFactory.getLog(EurekaServerInitializerConfiguration.class);@Autowiredprivate EurekaServerConfig eurekaServerConfig;private ServletContext servletContext;@Autowiredprivate ApplicationContext applicationContext;@Autowiredprivate EurekaServerBootstrap eurekaServerBootstrap;private boolean running;private int order = 1;@Overridepublic void setServletContext(ServletContext servletContext) {this.servletContext = servletContext;}主要看生命周期接口的start方法。。。@Overridepublic void start() {new Thread(() -> {try {// TODO: is this class even needed now?初始化eureka server,这里是核心。。。eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);log.info("Started Eureka Server");发布eureka 注册器可用事件publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));EurekaServerInitializerConfiguration.this.running = true;发布eureka server 已经启动事件publish(new EurekaServerStartedEvent(getEurekaServerConfig()));}catch (Exception ex) {// Help!log.error("Could not initialize Eureka servlet context", ex);}}).start();}。 。 。}核心方法初始化的实现:public void contextInitialized(ServletContext context) {try {初始化eureka server基本配置信息initEurekaEnvironment();初始化eureka server 的上下文,注意不是spring boot上下文。这里会触发
PeerAwareInstanceRegistry.openForTraffic(ApplicationInfoManager applicationInfoManager,
int count);方法,这个方法的作用就是更改eureka server的状态为UP,同事也会调用其父类
AbstractInstanceRegistry的postInit()进行续约于剔除任务的初始化。initEurekaServerContext();context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);}catch (Throwable e) {log.error("Cannot bootstrap eureka server :", e);throw new RuntimeException("Cannot bootstrap eureka server :", e);}}postInit()的实现:重点方法:protected void postInit() {统计最后1min事件内的续约次数任务开始renewsLastMin.start();if (evictionTaskRef.get() != null) {evictionTaskRef.get().cancel();}开始剔除任务,默认60秒清除一次90没有续约的节点。evictionTaskRef.set(new EvictionTask());evictionTimer.schedule(evictionTaskRef.get(),serverConfig.getEvictionIntervalTimerInMs(),serverConfig.getEvictionIntervalTimerInMs());}
我们是不是还发现 EurekaServerAutoConfiguration 源码里面有很多@Autowire依赖注入的bean : 那这些Bean是如何添加到应用上下文中的呢?
@Autowiredprivate ApplicationInfoManager applicationInfoManager;@Autowiredprivate EurekaServerConfig eurekaServerConfig;@Autowiredprivate EurekaClientConfig eurekaClientConfig;@Autowiredprivate EurekaClient eurekaClient;@Autowiredprivate InstanceRegistryProperties instanceRegistryProperties;
我们来看看spring-cloud-netflix-eureka-server依赖的jar:也就是说spring-cloud-netflix-eureka-server 也依赖了 spring-cloud-netflix-eureka-client,这就是eureka-server能自我注册的实现方式,上面自动注入的5个Bean也都是在 spring-cloud-netflix-eureka-client中装配的。
. . .<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-netflix-eureka-client</artifactId><version>2.2.5.RELEASE</version><scope>compile</scope></dependency>. . .
接下来我们来看看自动装配的 EurekaServerContext:默认是 DefaultEurekaServerContext 类型:
@Bean@ConditionalOnMissingBeanpublic EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs,PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,registry, peerEurekaNodes, this.applicationInfoManager);}
DefaultEurekaServerContext的源码如下:
@Singleton
public class DefaultEurekaServerContext implements EurekaServerContext {private static final Logger logger = LoggerFactory.getLogger(DefaultEurekaServerContext.class);private final EurekaServerConfig serverConfig;private final ServerCodecs serverCodecs;private final PeerAwareInstanceRegistry registry;private final PeerEurekaNodes peerEurekaNodes;private final ApplicationInfoManager applicationInfoManager;@Injectpublic DefaultEurekaServerContext(EurekaServerConfig serverConfig,ServerCodecs serverCodecs,PeerAwareInstanceRegistry registry,PeerEurekaNodes peerEurekaNodes,ApplicationInfoManager applicationInfoManager) {this.serverConfig = serverConfig;this.serverCodecs = serverCodecs;this.registry = registry;this.peerEurekaNodes = peerEurekaNodes;this.applicationInfoManager = applicationInfoManager;}核心就在这里,初始化方法@PostConstruct@Overridepublic void initialize() {启动一个任务去刷新 eureka其他节点的配置信息,例如eureka集群添加删除节点的时候需要更新集群的节点信息。logger.info("Initializing ...");peerEurekaNodes.start();try {初始化前面配置的实例注册器就是前面配置的InstanceRegistry实例。registry.init(peerEurekaNodes);} catch (Exception e) {throw new RuntimeException(e);}logger.info("Initialized");}@PreDestroy@Overridepublic void shutdown() {logger.info("Shutting down ...");registry.shutdown();peerEurekaNodes.shutdown();ServoControl.shutdown();EurekaMonitors.shutdown();logger.info("Shut down");}@Overridepublic EurekaServerConfig getServerConfig() {return serverConfig;}@Overridepublic PeerEurekaNodes getPeerEurekaNodes() {return peerEurekaNodes;}@Overridepublic ServerCodecs getServerCodecs() {return serverCodecs;}@Overridepublic PeerAwareInstanceRegistry getRegistry() {return registry;}@Overridepublic ApplicationInfoManager getApplicationInfoManager() {return applicationInfoManager;}}
由于 InstanceRegistry 继承自 PeerAwareInstanceRegistryImpl 因此我们来到 PeerAwareInstanceRegistryImpl 的 init(...)方法:
@Overridepublic void init(PeerEurekaNodes peerEurekaNodes) throws Exception {开启集群最后1分钟同步次数统计的任务,默认60s执行一次this.numberOfReplicationsLastMin.start();this.peerEurekaNodes = peerEurekaNodes;初始化响应缓存,这个响应缓存里面放的就是eureka-client 需要获取的服务实例列表。initializedResponseCache();开启续约阈值的更新任务,默认是15分钟执行一次,这里在讲解自我保护机制的时候设涉及过。scheduleRenewalThresholdUpdateTask();初始化远程区域注册表,如果分区域注册服务的话,这里就有用了。initRemoteRegionRegistry();try {Monitors.registerObject(this);} catch (Throwable e) {logger.warn("Cannot register the JMX monitor for the InstanceRegistry :", e);}}
我们主要来分析一下初始化响应缓存的代码:
@Overridepublic synchronized void initializedResponseCache() {if (responseCache == null) {直接new一个响应缓存的实现类ResponseCacheImpl实例responseCache = new ResponseCacheImpl(serverConfig, serverCodecs, this);}}
ResponseCacheImpl(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, AbstractInstanceRegistry registry) {this.serverConfig = serverConfig;this.serverCodecs = serverCodecs;默认使用只读缓存进行eureka-client请求的响应值,默认为true,可配置this.shouldUseReadOnlyResponseCache = serverConfig.shouldUseReadOnlyResponseCache();this.registry = registry;响应缓存的跟行事件间隔,默认30s , 可配置。long responseCacheUpdateIntervalMs = serverConfig.getResponseCacheUpdateIntervalMs();实例化可读可写缓存,默认使用 CacheLoader 去注册的服务的数据中
(AbstractInstanceRegistry.registry 的ConcurrentHashMap)进行载入。使用的技术是guava cache。this.readWriteCacheMap =CacheBuilder.newBuilder().initialCapacity(serverConfig.getInitialCapacityOfResponseCache()).expireAfterWrite(serverConfig.getResponseCacheAutoExpirationInSeconds(), TimeUnit.SECONDS).removalListener(new RemovalListener<Key, Value>() {@Overridepublic void onRemoval(RemovalNotification<Key, Value> notification) {Key removedKey = notification.getKey();if (removedKey.hasRegions()) {Key cloneWithNoRegions = removedKey.cloneWithoutRegions();regionSpecificKeys.remove(cloneWithNoRegions, removedKey);}}})构建CacheLoader用于载入服务的注册数据到可读可写的缓存中。.build(new CacheLoader<Key, Value>() {@Overridepublic Value load(Key key) throws Exception {if (key.hasRegions()) {Key cloneWithNoRegions = key.cloneWithoutRegions();regionSpecificKeys.put(cloneWithNoRegions, key);}Value value = generatePayload(key);return value;}});如果使用了只读缓存作为响应,那就启动一个任务按配置刷新间隔进行自动刷新只读缓存,默认每30s从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);}}
只读缓存同步的任务实现如下:
private TimerTask getCacheUpdateTask() {return new TimerTask() {@Overridepublic void run() {logger.debug("Updating the client cache from response cache");这里是不是很奇怪,为啥只有当只读缓存中有数据才同步,但是又是因为同步才有数据,矛
盾,其实在服务只注册完成后只要有客户端进行了服务列表获取,在获取的时候,服务端会先手动做如下操作:1、获取可读写缓存中的服务数据,如果readWriteCacheMap中没有就会使用CacheLoader进行载入。2、将获取到的 或者是载入的数据写入到只读缓存readOnlyCacheMap中。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);} finally {CurrentRequestVersion.remove();}}}};}
手动初始化只读缓存 readOnlyCacheMap 的实现如下:ResponseCacheImpl 中
@VisibleForTestingValue 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;}
触发手动初始化只读缓存的点如下:
1、ApplicationResource.getApplication 的rest接口。
2、ApplicationResource.getContainers 的rest接口。
3、ApplicationResource.getContainerDifferential 的rest接口。
上述说得三个缓存就是eureka-server的三级缓存,模型图如下:
2.2、eureka server是如何存储服务提供者数据的呢???
通过上面我们大概知道了是使用 ConcurrentHashMap 来进行服务提供者数据存储的,那么存储的结构是啥呢?我们来到 AbstractInstanceRegistry 类,找到储存数据的Map,如下:
private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry= new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();
结构是key = appName
value = 节点信息Map( key = 节点名称, value = Lease实例)
案例数据截图:
Lease的hodler实例Instanceinfo实例截图:
2.3、eureka server 端是如何与客户端进行交互的呢?
eureka 是基于http通讯的,因此在eureka server端提供了一些rest接口,用于跟客户端进行交互,我们来看看都有那些rest接口:
1、注册接口,在 ApplicationResource 中提供了如下rest接口:
@POST@Consumes({"application/json", "application/xml"})public Response addInstance(InstanceInfo info,@HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {logger.debug("Registering instance {} (replication={})", info.getId(), isReplication);// validate that the instanceinfo contains all the necessary required fieldsif (isBlank(info.getId())) {return Response.status(400).entity("Missing instanceId").build();} else if (isBlank(info.getHostName())) {return Response.status(400).entity("Missing hostname").build();} else if (isBlank(info.getIPAddr())) {return Response.status(400).entity("Missing ip address").build();} else if (isBlank(info.getAppName())) {return Response.status(400).entity("Missing appName").build();} else if (!appName.equals(info.getAppName())) {return Response.status(400).entity("Mismatched appName, expecting " + appName + " but was " + info.getAppName()).build();} else if (info.getDataCenterInfo() == null) {return Response.status(400).entity("Missing dataCenterInfo").build();} else if (info.getDataCenterInfo().getName() == null) {return Response.status(400).entity("Missing dataCenterInfo Name").build();}// handle cases where clients may be registering with bad DataCenterInfo with missing dataDataCenterInfo dataCenterInfo = info.getDataCenterInfo();if (dataCenterInfo instanceof UniqueIdentifier) {String dataCenterInfoId = ((UniqueIdentifier) dataCenterInfo).getId();if (isBlank(dataCenterInfoId)) {boolean experimental = "true".equalsIgnoreCase(serverConfig.getExperimental("registration.validation.dataCenterInfoId"));if (experimental) {String entity = "DataCenterInfo of type " + dataCenterInfo.getClass() + " must contain a valid id";return Response.status(400).entity(entity).build();} else if (dataCenterInfo instanceof AmazonInfo) {AmazonInfo amazonInfo = (AmazonInfo) dataCenterInfo;String effectiveId = amazonInfo.get(AmazonInfo.MetaDataKey.instanceId);if (effectiveId == null) {amazonInfo.getMetadata().put(AmazonInfo.MetaDataKey.instanceId.getName(), info.getId());}} else {logger.warn("Registering DataCenterInfo of type {} without an appropriate id", dataCenterInfo.getClass());}}}registry.register(info, "true".equals(isReplication));return Response.status(204).build(); // 204 to be backwards compatible}
@Overridepublic void register(final InstanceInfo info, final boolean isReplication) {handleRegistration(info, resolveInstanceLeaseDuration(info), isReplication);super.register(info, isReplication);}
@Overridepublic void register(final InstanceInfo info, final boolean isReplication) {int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS;if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {leaseDuration = info.getLeaseInfo().getDurationInSecs();}super.register(info, leaseDuration, isReplication);同步给其他eureka server 集群的节点。replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication);}
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {try {read.lock();Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());REGISTER.increment(isReplication);if (gMap == null) {final ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap<String, Lease<InstanceInfo>>();存储服务的注册信息gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);if (gMap == null) {gMap = gNewMap;}}Lease<InstanceInfo> existingLease = gMap.get(registrant.getId());// Retain the last dirty timestamp without overwriting it, if there is already a leaseif (existingLease != null && (existingLease.getHolder() != null)) {Long existingLastDirtyTimestamp = existingLease.getHolder().getLastDirtyTimestamp();Long registrationLastDirtyTimestamp = registrant.getLastDirtyTimestamp();logger.debug("Existing lease found (existing={}, provided={}", existingLastDirtyTimestamp, registrationLastDirtyTimestamp);// this is a > instead of a >= because if the timestamps are equal, we still take the remote transmitted// InstanceInfo instead of the server local copy.if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) {logger.warn("There is an existing lease and the existing lease's dirty timestamp {} is greater" +" than the one that is being registered {}", existingLastDirtyTimestamp, registrationLastDirtyTimestamp);logger.warn("Using the existing instanceInfo instead of the new instanceInfo as the registrant");registrant = existingLease.getHolder();}} else {// The lease does not exist and hence it is a new registrationsynchronized (lock) {if (this.expectedNumberOfClientsSendingRenews > 0) {// Since the client wants to register it, increase the number of clients sending renews更新自我保护机制的阈值this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews + 1;updateRenewsPerMinThreshold();}}logger.debug("No previous lease information found; it is new registration");}Lease<InstanceInfo> lease = new Lease<InstanceInfo>(registrant, leaseDuration);if (existingLease != null) {lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());}gMap.put(registrant.getId(), lease);recentRegisteredQueue.add(new Pair<Long, String>(System.currentTimeMillis(),registrant.getAppName() + "(" + registrant.getId() + ")"));// This is where the initial state transfer of overridden status happensif (!InstanceStatus.UNKNOWN.equals(registrant.getOverriddenStatus())) {logger.debug("Found overridden status {} for instance {}. Checking to see if needs to be add to the "+ "overrides", registrant.getOverriddenStatus(), registrant.getId());if (!overriddenInstanceStatusMap.containsKey(registrant.getId())) {logger.info("Not found overridden id {} and hence adding it", registrant.getId());overriddenInstanceStatusMap.put(registrant.getId(), registrant.getOverriddenStatus());}}InstanceStatus overriddenStatusFromMap = overriddenInstanceStatusMap.get(registrant.getId());if (overriddenStatusFromMap != null) {logger.info("Storing overridden status {} from map", overriddenStatusFromMap);registrant.setOverriddenStatus(overriddenStatusFromMap);}// Set the status based on the overridden status rulesInstanceStatus overriddenInstanceStatus = getOverriddenInstanceStatus(registrant, existingLease, isReplication);registrant.setStatusWithoutDirty(overriddenInstanceStatus);// If the lease is registered with UP status, set lease service up timestampif (InstanceStatus.UP.equals(registrant.getStatus())) {lease.serviceUp();}registrant.setActionType(ActionType.ADDED);recentlyChangedQueue.add(new RecentlyChangedItem(lease));registrant.setLastUpdatedTimestamp();过期当前应用的服务缓存数据invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), registrant.getSecureVipAddress());logger.info("Registered instance {}/{} with status {} (replication={})",registrant.getAppName(), registrant.getId(), registrant.getStatus(), isReplication);} finally {read.unlock();}}
2、获取服务列表在 ApplicationsResource 的 getContainers,源码如下:
表示地址是/apps
@Path("/{version}/apps")
@Produces({"application/xml", "application/json"})
public class ApplicationsResource {. . .@GETpublic 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();}CurrentRequestVersion.remove();return response;}. . .}
核心的就是这两个接口,我们在分析eureka-client的时候会详细讲解。