Eureka server使用的不是spring mvc的框架,而是使用Jersey。
Eureka server ,启动的流程,追本溯源,是在 DiscoveryClient里面,使用这个构造方法
@Inject
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
Provider<BackupRegistry> backupRegistryProvider)
在里面调用 scheduleServerEndpointTask(eurekaTransport, args); 方法
Eureka server ,里面的各种方法,最终是使用
C:UsersAdministrator.m2 epositorycom etflixeurekaeureka-client1.7.2eureka-client-1.7.2.jar!com etflixdiscoveryshared ransportjerseyAbstractJerseyEurekaHttpClient.class
进行http通信,包括cancel、register、sendHeartBeat、statusUpdate、deleteStatusOverride 等。
例如注册代码:
public EurekaHttpResponse<Void> register(InstanceInfo info) { String urlPath = "apps/" + info.getAppName(); ClientResponse response = null; try { Builder resourceBuilder = jerseyClient.resource(serviceUrl).path(urlPath).getRequestBuilder(); addExtraHeaders(resourceBuilder); response = resourceBuilder .header("Accept-Encoding", "gzip") .type(MediaType.APPLICATION_JSON_TYPE) .accept(MediaType.APPLICATION_JSON) .post(ClientResponse.class, info); //这里决定了是执行新增(put)、更新(post)、删除(delete)还是获取(get)。这是典型的restful风格 return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build(); } finally { if (logger.isDebugEnabled()) { logger.debug("Jersey HTTP POST {}/{} with instance {}; statusCode={}", serviceUrl, urlPath, info.getId(), response == null ? "N/A" : response.getStatus()); } if (response != null) { response.close(); } } }
里面有个包装类 Builder,通过Builder的post、delete、put、get 决定接下来的流程。
另外,要注意的是renew方法,调用的是sendHeartBeat 方法,也就是心跳。还有就是,renew是不需要加锁的,cancel、update、delete、scheduleRenewalThresholdUpdateTask(自我保护机制调用)都需要加锁。
自我保护机制,是eureka的一种机制,具体意思是,当15分钟(代码里体现的是15*60*1000)内,续约renew(心跳)的成功率低于默认的85%,则进入自我保护的模式,不再剔除节点。
eureka的节点,是保存在一个 ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> 中
当注册的时候,实质上是往这个map中add数据
当续约的时候,实质上是把InstanceInfo 的时间戳 lastUpdateTimestamp 替换
当注销的时候,实质上是把节点从map中共删除
当更新状态(sendHeartBeat、statusUpdate、deleteStatusOverride)时候,则是更新节点的状态(具体体现在代码里是:InstanceStatus)
另外,eureaka,无论是注册(register)、注销(cancel),还是更新(sendHeartBeat、statusUpdate、deleteStatusOverride),都有对应的计数器保存计数的
值得注意的是,续约的时候,如果有多个线程同时续约renew,则保留最新的时间戳的节点信息。同时,还有循环队列保存节点信息,具体体现的代码如下图
private final CircularQueue<Pair<Long, String>> recentRegisteredQueue; private final CircularQueue<Pair<Long, String>> recentCanceledQueue; private ConcurrentLinkedQueue<RecentlyChangedItem> recentlyChangedQueue = new ConcurrentLinkedQueue<RecentlyChangedItem>();
gMap.put(registrant.getId(), lease); synchronized (recentRegisteredQueue) { recentRegisteredQueue.add(new Pair<Long, String>( System.currentTimeMillis(), registrant.getAppName() + "(" + registrant.getId() + ")")); }