Spring Cloud Eureka(三):Server源码

@EnableEurekaServer:入口

在第一篇搭建的Spring Cloud Eureka服务端项目中,需要在启动类上添加@EnableEurekaServer注解来启动 Eureka 服务端,那我们就以这个注解为入口,到 Eureka Server 内部去看一看。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {}

@Configuration(proxyBeanMethods = false)
public class EurekaServerMarkerConfiguration {

	@Bean
	public Marker eurekaServerMarkerBean() {
		return new Marker();
	}

	class Marker {}
}

可以看到@EnableEurekaServer注解引入了一个类EurekaServerMarkerConfiguration,而这个类又只是把一个内部类Marker注册为Bean,似乎什么都没做就结束了,那 Eureka 是什么时候注册到 Spring 中的呢?
和上一篇一样,找到 spring-cloud-netflix-eureka-server.jarMETA-INF/spring.factories 文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration

该类EurekaServerAutoConfiguration@ConditionalOnBean条件就是必须注册了Marker才能够生效,由于@EnableEurekaServer注册了Marker,所以该类开始生效。而该类又引入了EurekaServerInitializerConfiguration,这个类执行了 EurekaServer 初始化的工作,所以 Eureka Server 得以启动。

@Configuration(proxyBeanMethods = false)
// 初始化EurekaServer
@Import(EurekaServerInitializerConfiguration.class)
// EurekaServerMarkerConfiguration.Marker 注册为 bean,该配置类才生效
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
		InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration implements WebMvcConfigurer {...}

EurekaServerInitializerConfiguration:初始化

@Configuration(proxyBeanMethods = false)
public class EurekaServerInitializerConfiguration
		implements ServletContextAware, SmartLifecycle, Ordered {
		
	private boolean running;
	
	@Autowired
	private EurekaServerBootstrap eurekaServerBootstrap;
	
	@Override
	public void start() {
		new Thread(() -> {
			try {
				// 启动 Eureka Server
				eurekaServerBootstrap.contextInitialized(
						EurekaServerInitializerConfiguration.this.servletContext);
				log.info("Started Eureka Server");
				// 发布事件:Eureka Server 可以接受注册了
				publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
				// 表示状态为正在运行
				EurekaServerInitializerConfiguration.this.running = true;
				// 发布事件:Eureka Server 已经启动完成
				publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
			}
			catch (Exception ex) {
				log.error("Could not initialize Eureka servlet context", ex);
			}
		}).start();
	}
	
	@Override
	public boolean isRunning() {
		return this.running;
	}
	
	@Override
	public void stop() {
		this.running = false;
		eurekaServerBootstrap.contextDestroyed(this.servletContext);
	}
}

实现SmartLifeCycle接口可以在容器创建后,关闭之前做一些自定义的操作,以及判断应用是否在运行。主要看start方法,新起了一个线程来做Eureka Server的初始化,主要通过eurekaServerBootstrap.contextInitialized方法执行初始化。

EurekaServerBootstrap#contextInitialized

public class EurekaServerBootstrap {

	public void contextInitialized(ServletContext context) {
		try {
			// 初始化 Eureka 环境配置
			initEurekaEnvironment();
			// 初始化 EurekaServer 上下文
			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);
		}
	}

	protected PeerAwareInstanceRegistry registry;
	
	protected void initEurekaServerContext() throws Exception {
		
		// 省略一些初始化操作
		
		log.info("Initialized server context");

		//从相邻的 Eureka 节点复制注册表
		int registryCount = this.registry.syncUp();
		// 
		this.registry.openForTraffic(this.applicationInfoManager, registryCount);

		// Register all monitoring statistics.
		EurekaMonitors.registerAllStats();
	}
}

PeerAwareInstanceRegistryImpl#syncUp:同步注册表

	// DiscoveryClient
	protected final EurekaClient eurekaClient;
	
	@Override
    public int syncUp() {
        int count = 0;
        // 从相邻的 Eureka 节点复制注册表
		// 最多重试 eureka.server.registry-sync-retries 次,默认 0 次
        for (int i = 0; ((i < serverConfig.getRegistrySyncRetries()) && (count == 0)); i++) {
            if (i > 0) {
                try {
                	// 重试时间间隔 eureka.server.registry-sync-retry-wait-ms, 默认 30 秒
                    Thread.sleep(serverConfig.getRegistrySyncRetryWaitMs());
                } catch (InterruptedException e) {
                    logger.warn("Interrupted during registry transfer..");
                    break;
                }
            }
            // 获取注册表
            Applications apps = eurekaClient.getApplications();
            for (Application app : apps.getRegisteredApplications()) {
                for (InstanceInfo instance : app.getInstances()) {
                    try {
                    	// 判断是否需要注册,region校验
                        if (isRegisterable(instance)) {
                        	// 注册该实例
                        	// instance.getLeaseInfo().getDurationInSecs() 续约周期默认90s
                            register(instance, instance.getLeaseInfo().getDurationInSecs(), true);
                            count++;
                        }
                    } catch (Throwable t) {
                        logger.error("During DS init copy", t);
                    }
                }
            }
        }
        return count;
    }

这里的EurekaClient就是DiscoveryClient,高可用的注册中心会把自己也当做客户端向其他服务端进行注册,所以这里的DiscoveryClient初始化的时候也会拉取注册表,具体可以看上一篇。

AbstractInstanceRegistry#register:实例信息存到本地注册表

	private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
	private final Lock read = readWriteLock.readLock();
	
	// 将实例信息存到本地注册表中
	public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
		// 读锁
        read.lock();
        try {
        	// 根据注册表中该appName的实例租约
            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());
            // 已有该实例的租约信息
            if (existingLease != null && (existingLease.getHolder() != null)) {
            	// 获取两个实例的最近一次的租约时间
                Long existingLastDirtyTimestamp = existingLease.getHolder().getLastDirtyTimestamp();
                Long registrationLastDirtyTimestamp = registrant.getLastDirtyTimestamp();
                // 已有的比要注册的时间近则使用已有的
                if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) {
                    registrant = existingLease.getHolder();
                }
            } else {
                // 不存在该实例的租约信息,表示有一个新的客户要进行租约
                synchronized (lock) {
                    if (this.expectedNumberOfClientsSendingRenews > 0) {
						// 需要续约的客户数 + 1
                        this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews + 1;
                        // 计算每分钟续约的阈值
                        updateRenewsPerMinThreshold();
                    }
                }
            }
            // 创建新的租约对象,包含一些时间戳信息以及InstanceInfo本身
            Lease<InstanceInfo> lease = new Lease<InstanceInfo>(registrant, leaseDuration);
            // 如果已有租约信息,那么设置一下服务上线的时间
            if (existingLease != null) {
                lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());
            }
            // 放到appName的租约表里面
            gMap.put(registrant.getId(), lease);

            // 设置一些该实例的状态以及时间戳,省略...
            
        } finally {
            read.unlock();
        }
    }

	// 计算每分钟应该收到的续约阈值
    protected void updateRenewsPerMinThreshold() {
    	// 发送续约的客户端数 * 客户端每分钟发送续约的次数 * 阈值比例
        this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfClientsSendingRenews
                * (60.0 / serverConfig.getExpectedClientRenewalIntervalSeconds())
                * serverConfig.getRenewalPercentThreshold());
    }

PeerAwareInstanceRegistryImpl#openForTraffic:服务端上线&&开启剔除任务

    public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
        // 省略...
        // 修改server 状态为 UP,接受客户端注册
        logger.info("Changing status to UP");
        applicationInfoManager.setInstanceStatus(InstanceStatus.UP);
        // 调用父类 AbstarctInstanceRegistry.postInit()
        super.postInit();
    }
AbstarctInstanceRegistry#postInit:开启剔除任务
    protected void postInit() {
    	// 计数任务
        renewsLastMin.start();
        // 取消之前的剔除任务
        if (evictionTaskRef.get() != null) {
            evictionTaskRef.get().cancel();
        }
        // 开启新的剔除任务
        evictionTaskRef.set(new EvictionTask());
        evictionTimer.schedule(evictionTaskRef.get(),
        		// 剔除任务的时间周期:eureka.server.eviction-interval-timer-in-ms
        		// 默认是60s,延时60s执行,然后每60s执行一次
                serverConfig.getEvictionIntervalTimerInMs(),
                serverConfig.getEvictionIntervalTimerInMs());
    }

	class EvictionTask extends TimerTask {
        @Override
        public void run() {
            try {
            	// 计算一个补偿时间
                long compensationTimeMs = getCompensationTimeMs();
                evict(compensationTimeMs);
            } catch (Throwable e) {
                logger.error("Could not run the evict task", e);
            }
        }
		
		public void evict(long additionalLeaseMs) {
			// 不允许实例过期则直接返回
	        if (!isLeaseExpirationEnabled()) {
	            logger.debug("DS: lease expiration is currently disabled.");
	            return;
	        }
	
	        // 遍历注册表中所有实例,过滤出全部过期的实例
	        List<Lease<InstanceInfo>> expiredLeases = new ArrayList<>();
	        for (Entry<String, Map<String, Lease<InstanceInfo>>> groupEntry : registry.entrySet()) {
	            Map<String, Lease<InstanceInfo>> leaseMap = groupEntry.getValue();
	            if (leaseMap != null) {
	                for (Entry<String, Lease<InstanceInfo>> leaseEntry : leaseMap.entrySet()) {
	                    Lease<InstanceInfo> lease = leaseEntry.getValue();
	                    // 计算该租约是否过期
	                    if (lease.isExpired(additionalLeaseMs) && lease.getHolder() != null) {
	                        expiredLeases.add(lease);
	                    }
	                }
	            }
	        }
	
	        // 本地注册表大小
	        int registrySize = (int) getLocalRegistrySize();
	        // 注册表的最小阈值
	        // eureka.server.renewal-percent-threshold 默认 0.85
	        int registrySizeThreshold = (int) (registrySize * serverConfig.getRenewalPercentThreshold());
	        // 最多可剔除的实例数量
	        int evictionLimit = registrySize - registrySizeThreshold;
			// 已过期的实例数 和 最多可剔除的实例数,取较小者
	        int toEvict = Math.min(expiredLeases.size(), evictionLimit);
	        // 有可剔除的名额则进行剔除
	        if (toEvict > 0) {
	            Random random = new Random(System.currentTimeMillis());
	            for (int i = 0; i < toEvict; i++) {
	                // 随机挑选已过期的实例去剔除
	                int next = i + random.nextInt(expiredLeases.size() - i);
	                Collections.swap(expiredLeases, i, next);
	                Lease<InstanceInfo> lease = expiredLeases.get(i);
					// 过期实例属性
	                String appName = lease.getHolder().getAppName();
	                String id = lease.getHolder().getId();
	                // 过期计数
	                EXPIRED.increment();
	                logger.warn("DS: Registry: expired lease for {}/{}", appName, id);
	                // 下线服务
	                internalCancel(appName, id, false);
	            }
	        }
	    }
	}

PeerAwareInstanceRegistryImpl#isLeaseExpirationEnabled

    public boolean isLeaseExpirationEnabled() {
    	//  自我保护机制 eureka.server.enable-self-preservation 默认true
        if (!isSelfPreservationModeEnabled()) {
            // 关闭自我保护,返回 true,代表允许剔除
            return true;
        }
        // 开启自我保护,则需要计算是否应该剔除
        // 每分钟应收到的续约阈值 > 0 && 上一分钟收到的续约数 > 每分钟应收到的续约阈值
        return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
    }

Lease#isExpired

    public boolean isExpired(long additionalLeaseMs) {
    	// 过期时间 > 0 || 当前时间 > 上次续约时间 + 续约周期 + 补偿时间
        return (evictionTimestamp > 0 || 
        	System.currentTimeMillis() > (lastUpdateTimestamp + duration + additionalLeaseMs));
    }
AbstarctInstanceRegistry#internalCancel:服务下线

	private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
	private final Lock read = readWriteLock.readLock();
	
	protected boolean internalCancel(String appName, String id, boolean isReplication) {
		// 读锁
        read.lock();
        try {
            CANCEL.increment(isReplication);
            // 获取该appName的全部实例
            Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
            Lease<InstanceInfo> leaseToCancel = null;
            if (gMap != null) {
            	// 从注册表移除
                leaseToCancel = gMap.remove(id);
            }
            // 最近下线的实例队列
            recentCanceledQueue.add(new Pair<Long, String>(System.currentTimeMillis(), appName + "(" + id + ")"));
            InstanceStatus instanceStatus = overriddenInstanceStatusMap.remove(id);
            if (instanceStatus != null) {
                logger.debug("Removed instance id {} from the overridden map which has value {}", id, instanceStatus.name());
            }
            // 不存在则记录并返回
            if (leaseToCancel == null) {
                CANCEL_NOT_FOUND.increment(isReplication);
                logger.warn("DS: Registry: cancel failed because Lease is not registered for: {}/{}", appName, id);
                return false;
            } else {
            	// 记录下线时间
                leaseToCancel.cancel();
                InstanceInfo instanceInfo = leaseToCancel.getHolder();
                String vip = null;
                String svip = null;
                if (instanceInfo != null) {
                    instanceInfo.setActionType(ActionType.DELETED);
                    recentlyChangedQueue.add(new RecentlyChangedItem(leaseToCancel));
                    instanceInfo.setLastUpdatedTimestamp();
                    vip = instanceInfo.getVIPAddress();
                    svip = instanceInfo.getSecureVipAddress();
                }
                // 清除缓存
                invalidateCache(appName, vip, svip);
                logger.info("Cancelled instance {}/{} (replication={})", appName, id, isReplication);
            }
        } finally {
            read.unlock();
        }

        synchronized (lock) {
            if (this.expectedNumberOfClientsSendingRenews > 0) {
                // 需要续约的客户数 - 1
                this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews - 1;
                // 计算每分钟需要续约的阈值
                updateRenewsPerMinThreshold();
            }
        }

        return true;
    }

JerseyFilter: 配置EurekaServer的Rest接口

以上都是Server主动去获得注册表,去剔除服务。同样的,Server也可以被动的接收客户端发来的注册以及下线信息。
还是回到最初的配置类EurekaServerAutoConfiguration中,这个配置类注册了两个Bean

	// 包路径,分别在 eureka-client.jar 和 eureka-core.jar中
	private static final String[] EUREKA_PACKAGES = new String[] {
			"com.netflix.discovery", "com.netflix.eureka" };	
			
	@Bean
	public FilterRegistrationBean<?> jerseyFilterRegistration(
			javax.ws.rs.core.Application eurekaJerseyApp) {
		FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<Filter>();
		bean.setFilter(new ServletContainer(eurekaJerseyApp));
		bean.setOrder(Ordered.LOWEST_PRECEDENCE);
		bean.setUrlPatterns(
				// eureka/*
				Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/*"));

		return bean;
	}
	
	@Bean
	public javax.ws.rs.core.Application jerseyApplication(Environment environment,ResourceLoader resourceLoader) {
		// 扫描包路径
		ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(
				false, environment);
		// 过滤出带有 @Path 和 @Provider 注解的类
		// @Path 相当于 @RequestMapping
		// @Provider 相当于 @Controller
		provider.addIncludeFilter(new AnnotationTypeFilter(Path.class));
		provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class));
		// 开始扫描
		Set<Class<?>> classes = new HashSet<>();
		for (String basePackage : EUREKA_PACKAGES) {
			Set<BeanDefinition> beans = provider.findCandidateComponents(basePackage);
			for (BeanDefinition bd : beans) {
				Class<?> cls = ClassUtils.resolveClassName(bd.getBeanClassName(),
						resourceLoader.getClassLoader());
				classes.add(cls);
			}
		}
		DefaultResourceConfig rc = new DefaultResourceConfig(classes);
		// 省略部分代码
		return rc;
	}

从扫描出的类中,取出用于获取注册表的类ApplicationsResource来看一看。

ApplicationsResource:获取注册表

@Path("/{version}/apps")
@Produces({"application/xml", "application/json"})
public class ApplicationsResource {
	
	// 省略部分代码
	
    private final EurekaServerConfig serverConfig;
	private final PeerAwareInstanceRegistry registry;	
	private final ResponseCache responseCache;

    @Inject
    ApplicationsResource(EurekaServerContext eurekaServer) {
        this.serverConfig = eurekaServer.getServerConfig();
        this.registry = eurekaServer.getRegistry();
        this.responseCache = registry.getResponseCache();
    }
	
	// 具体单类服务的操作,交给 ApplicationResource 处理
    @Path("{appId}")
    public ApplicationResource getApplicationResource(@PathParam("version") String version,
    					@PathParam("appId") String appId) {
        CurrentRequestVersion.set(Version.toEnum(version));
        try {
            return new ApplicationResource(appId, serverConfig, registry);
        } finally {
            CurrentRequestVersion.remove();
        }
    }
    
	// 获取全量注册表
    @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) {
        // 省略部分代码                                  
		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;
	}

	// 获取增量注册表
    @Path("delta")
    @GET
    public Response getContainerDifferential(
            @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) {
		// 省略部分代码      
        Key cacheKey = new Key(Key.EntityType.Application,
                ResponseCacheImpl.ALL_APPS_DELTA,
                keyType, CurrentRequestVersion.get(), EurekaAccept.fromString(eurekaAccept), regions
        );
        final 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;
	}
}

ApplicationResource:单类服务信息

接收请求路径apps/{appId},这里的 appId 实际是应用名。

// apps/{appName}
@Produces({"application/xml", "application/json"})
public class ApplicationResource {

	// 省略部分代码
	
	private final String appName;
    private final EurekaServerConfig serverConfig;
    private final PeerAwareInstanceRegistry registry;
    private final ResponseCache responseCache;

    ApplicationResource(String appName,EurekaServerConfig serverConfig,PeerAwareInstanceRegistry registry) {
        this.appName = appName.toUpperCase();
        this.serverConfig = serverConfig;
        this.registry = registry;
        this.responseCache = registry.getResponseCache();
    }

	// GET请求获取单类服务信息
	@GET
    public Response getApplication(@PathParam("version") String version,
                                   @HeaderParam("Accept") final String acceptHeader,
                                   @HeaderParam(EurekaAccept.HTTP_X_EUREKA_ACCEPT) String eurekaAccept) {
		 // 省略部分代码
         Key cacheKey = new Key(Key.EntityType.Application,
                appName,keyType,CurrentRequestVersion.get(),EurekaAccept.fromString(eurekaAccept));

        String payLoad = responseCache.get(cacheKey);
        if (payLoad != null) {
            logger.debug("Found: {}", appName);
            return Response.ok(payLoad).build();
        } else {
            logger.debug("Not Found: {}", appName);
            return Response.status(Status.NOT_FOUND).build();
        }
	}

	// POST请求,这类服务的实例注册
    @POST
    @Consumes({"application/json", "application/xml"})
    public Response addInstance(InstanceInfo info,
                                @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
		// 省略部分代码
		registry.register(info, "true".equals(isReplication));
        return Response.status(204).build();
	}
	
	// 这类服务的某个具体实例信息,交给 InstanceResource 处理
    @Path("{id}")
    public InstanceResource getInstanceInfo(@PathParam("id") String id) {
        return new InstanceResource(this, id, serverConfig, registry);
    }
}

InstanceResource:单个实例信息

// apps/{appName}/{appId}
@Produces({"application/xml", "application/json"})
public class InstanceResource {

    private final PeerAwareInstanceRegistry registry;
    private final EurekaServerConfig serverConfig;
    private final String id;
    private final ApplicationResource app;

    InstanceResource(ApplicationResource app, String id, EurekaServerConfig serverConfig, PeerAwareInstanceRegistry registry) {
        this.app = app;
        this.id = id;
        this.serverConfig = serverConfig;
        this.registry = registry;
    }

	// GET请求,获取实例信息
    @GET
    public Response getInstanceInfo() {
        InstanceInfo appInfo = registry.getInstanceByAppAndId(app.getName(), id);
        if (appInfo != null) {
            return Response.ok(appInfo).build();
        } else {
            return Response.status(Status.NOT_FOUND).build();
        }
    }

	// PUT请求,心跳续约
    @PUT
    public Response renewLease(
            @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication,
            @QueryParam("overriddenstatus") String overriddenStatus,
            @QueryParam("status") String status,
            @QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) {
		// 简化代码
		// 更新 LeaseInfo 的一些时间戳
		return this.validateDirtyTimestamp(Long.valueOf(lastDirtyTimestamp), isFromReplicaNode);
	}
	
	// DELETE请求,实例下线
    @DELETE
    public Response cancelLease(@HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
        try {
            boolean isSuccess = registry.cancel(app.getName(), id,"true".equals(isReplication));
            if (isSuccess) {
                return Response.ok().build();
            } else {
                return Response.status(Status.NOT_FOUND).build();
            }
        } catch (Throwable e) {
            logger.error("Error (cancel): {} - {}", app.getName(), id, e);
            return Response.serverError().build();
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值