一、简介
Eureka是Netflix提供的一个用于作为注册中心的组件。提供服务注册、服务发现等功能。
二、结构图
Eureka可以做集群部署,提供高可用性。如下是它的结构图。图片来源:https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance
图中包含两个组件:Eureka Client、Eureka Server。服务提供者跟服务调用方都是作为Eureka Client向Eureka Server注册。
- Application Service向Eureka Server进行注册
- Applocation Client向Eureka Server获取服务注册信息
- Applocation Client调用 Application Service
三、功能介绍
1、服务注册
服务提供者在启动的时候,会将自己服务信息,包括IP、port等提交给Eureka Server进行注册工作。在eureka-client.1.9.2.jar里面,有一个com.netflix.discovery.DiscoveryClient类,其中有一个register(),从代码中可以看出通过http向Eureka Server进行注册操作。
在eureka-core.1.9.2.jar里面,在com.netflix.eureka.resources.ApplicationResource里面提供了addInstance供客户端进行注册调用。com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl里有提供register,在服务注册成功之后会及时的同步到别的server节点。
public void register(InstanceInfo info, boolean isReplication) { int leaseDuration = 90; if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) { leaseDuration = info.getLeaseInfo().getDurationInSecs(); } super.register(info, leaseDuration, isReplication); this.replicateToPeers(PeerAwareInstanceRegistryImpl.Action.Register, info.getAppName(), info.getId(), info, (InstanceStatus)null, isReplication); }
2、服务发现
服务消费者在启动的时候,会根据已经配置的Eureka Server信息,去拉取已经注册的服务信息。在eureka-client.1.9.2.jar里面,有一个com.netflix.discovery.DiscoveryClient类,在其初始化的过程中调用了initScheduledTasks(),用来向服务端拉取服务信息
@Inject DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args, Provider<BackupRegistry> backupRegistryProvider) { ...省略... logger.info("Initializing Eureka in region {}", this.clientConfig.getRegion()); if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()) { ...省略... } else { ...省略... this.initScheduledTasks(); ...省略... }
在这里通过定时器拉取服务注册信息。
private void initScheduledTasks() { ...省略... if (this.clientConfig.shouldFetchRegistry()) { renewalIntervalInSecs = this.clientConfig.getRegistryFetchIntervalSeconds(); expBackOffBound = this.clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); this.scheduler.schedule(new TimedSupervisorTask("cacheRefresh", this.scheduler, this.cacheRefreshExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.CacheRefreshThread()), (long)renewalIntervalInSecs, TimeUnit.SECONDS); } ...省略... }
runnable里面会通过HTTP进行服务拉取。
private void getAndStoreFullRegistry() throws Throwable { long currentUpdateGeneration = this.fetchRegistryGeneration.get(); logger.info("Getting all instance registry info from the eureka server"); Applications apps = null; EurekaHttpResponse<Applications> httpResponse = this.clientConfig.getRegistryRefreshSingleVipAddress() == null ? this.eurekaTransport.queryClient.getApplications((String[])this.remoteRegionsRef.get()) : this.eurekaTransport.queryClient.getVip(this.clientConfig.getRegistryRefreshSingleVipAddress(), (String[])this.remoteRegionsRef.get()); if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) { apps = (Applications)httpResponse.getEntity(); } logger.info("The response status is {}", httpResponse.getStatusCode()); if (apps == null) { logger.error("The application is null for some reason. Not storing this information"); } else if (this.fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1L)) { this.localRegionApps.set(this.filterAndShuffle(apps)); logger.debug("Got full registry with apps hashcode {}", apps.getAppsHashCode()); } else { logger.warn("Not updating applications as another thread is updating it already"); } }
3、服务续约
服务提供者在启动的时候,会将自己服务信息,包括IP、port等提交给Eureka Server进行注册工作。在eureka-client.1.9.2.jar里面,有一个com.netflix.discovery.DiscoveryClient类,其中有一个renew(),通过HTTP向Eureka Server进行续约。
boolean renew() { try { EurekaHttpResponse<InstanceInfo> httpResponse = this.eurekaTransport.registrationClient.sendHeartBeat(this.instanceInfo.getAppName(), this.instanceInfo.getId(), this.instanceInfo, (InstanceStatus)null); logger.debug("DiscoveryClient_{} - Heartbeat status: {}", this.appPathIdentifier, httpResponse.getStatusCode()); if (httpResponse.getStatusCode() == 404) { this.REREGISTER_COUNTER.increment(); logger.info("DiscoveryClient_{} - Re-registering apps/{}", this.appPathIdentifier, this.instanceInfo.getAppName()); long timestamp = this.instanceInfo.setIsDirtyWithTime(); boolean success = this.register(); if (success) { this.instanceInfo.unsetIsDirty(timestamp); } return success; } else { return httpResponse.getStatusCode() == 200; } } catch (Throwable var5) { logger.error("DiscoveryClient_{} - was unable to send heartbeat!", this.appPathIdentifier, var5); return false; } }在eureka-core.1.9.2.jar里面,在com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl里有提供renew
public boolean renew(String appName, String id, boolean isReplication) { if (super.renew(appName, id, isReplication)) { this.replicateToPeers(PeerAwareInstanceRegistryImpl.Action.Heartbeat, appName, id, (InstanceInfo)null, (InstanceStatus)null, isReplication); return true; } else { return false; } }
4、时间参数
Eureka有2个时间参数。30、90。每30s发送一次续约请求,如果90s没有收到续约请求,则会将某一个服务实例剔除。