@EnableEurekaClient
@EnableDiscoveryClient -->EnableDiscoveryClientImportSelector
EnableDiscoveryClientImportSelector继承了SpringFactoryImportSelector
SpringFactoryImportSelector中selectImports中有下面一段话SpringFactoriesLoader进行META-INF/spring.factories中的configuration进行装载
// Find all possible auto configuration classes, filtering duplicates
List<String> factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader
.loadFactoryNames(this.annotationClass, this.beanClassLoader)));
在autoConfiguration中的其中一个NoopDiscoveryClientAutoConfiguration
有@Bean DiscoveryClient
@Bean
public DiscoveryClient discoveryClient() {
return new NoopDiscoveryClient(this.serviceInstance);
}
EurekaDiscoveryClient:Eureka 的 DiscoveryClient 实现类。
CompositeDiscoveryClient:用于排序可用客户端的发现客户端的顺序。
NoopDiscoveryClient:什么都不做的服务发现实现类,已经被废弃。
SimpleDiscoveryClient:简单的服务发现实现类 SimpleDiscoveryClient,具体的服务实例从 SimpleDiscoveryProperties 配置中获取。
EurekaDiscoveryClient中发现服务主要是this.eurekaClient,private final EurekaClient eurekaClient;
public List<ServiceInstance> getInstances(String serviceId) {
List<InstanceInfo> infos = this.eurekaClient.getInstancesByVipAddress(serviceId, false);
List<ServiceInstance> instances = new ArrayList();
Iterator var4 = infos.iterator();
while(var4.hasNext()) {
InstanceInfo info = (InstanceInfo)var4.next();
instances.add(new EurekaDiscoveryClient.EurekaServiceInstance(info));
}
return instances;
}
eurekaClient的实现类为DiscoveryClient
DiscoveryClient的构造方法中,进行几个线程池的初始化以及加入到scheduler中
@Inject
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args, Provider<BackupRegistry> backupRegistryProvider) {
。。。
try {
this.scheduler = Executors.newScheduledThreadPool(3, (new ThreadFactoryBuilder()).setNameFormat("DiscoveryClient-%d").setDaemon(true).build());
this.heartbeatExecutor = new ThreadPoolExecutor(1, this.clientConfig.getHeartbeatExecutorThreadPoolSize(), 0L, TimeUnit.SECONDS, new SynchronousQueue(), (new ThreadFactoryBuilder()).setNameFormat("DiscoveryClient-HeartbeatExecutor-%d").setDaemon(true).build());
this.cacheRefreshExecutor = new ThreadPoolExecutor(1, this.clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0L, TimeUnit.SECONDS, new SynchronousQueue(), (new ThreadFactoryBuilder()).setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d").setDaemon(true).build());
this.eurekaTransport = new DiscoveryClient.EurekaTransport(null);
this.scheduleServerEndpointTask(this.eurekaTransport, args);
Object azToRegionMapper;
if (this.clientConfig.shouldUseDnsForFetchingServiceUrls()) {
azToRegionMapper = new DNSBasedAzToRegionMapper(this.clientConfig);
} else {
azToRegionMapper = new PropertyBasedAzToRegionMapper(this.clientConfig);
}
if (null != this.remoteRegionsToFetch.get()) {
((AzToRegionMapper)azToRegionMapper).setRegionsToFetch(((String)this.remoteRegionsToFetch.get()).split(","));
}
this.instanceRegionChecker = new InstanceRegionChecker((AzToRegionMapper)azToRegionMapper, this.clientConfig.getRegion());
} catch (Throwable var8) {
throw new RuntimeException("Failed to initialize DiscoveryClient!", var8);
}
if (this.clientConfig.shouldFetchRegistry() && !this.fetchRegistry(false)) {
this.fetchRegistryFromBackup();
}
this.initScheduledTasks();
try {
Monitors.registerObject(this);
} catch (Throwable var7) {
logger.warn("Cannot register timers", var7);
}
DiscoveryManager.getInstance().setDiscoveryClient(this);
DiscoveryManager.getInstance().setEurekaClientConfig(config);
this.initTimestampMs = System.currentTimeMillis();
logger.info("Discovery Client initialized at timestamp {} with initial instances count: {}", this.initTimestampMs, this.getApplications().size());
}
}
在initScheduledTasks方法中this.instanceInfoReplicator.start(this.clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
查看InstanceInfoReplicator中的run方法,可以看到注册方法this.discoveryClient.register();
public void run() {
boolean var6 = false;
ScheduledFuture next;
label53: {
try {
var6 = true;
this.discoveryClient.refreshInstanceInfo();
Long dirtyTimestamp = this.instanceInfo.isDirtyWithTime();
if (dirtyTimestamp != null) {
this.discoveryClient.register();
this.instanceInfo.unsetIsDirty(dirtyTimestamp);
var6 = false;
} else {
var6 = false;
}
break label53;
} catch (Throwable var7) {
logger.warn("There was a problem with the instance info replicator", var7);
var6 = false;
} finally {
if (var6) {
ScheduledFuture next = this.scheduler.schedule(this, (long)this.replicationIntervalSeconds, TimeUnit.SECONDS);
this.scheduledPeriodicRef.set(next);
}
}
next = this.scheduler.schedule(this, (long)this.replicationIntervalSeconds, TimeUnit.SECONDS);
this.scheduledPeriodicRef.set(next);
return;
}
next = this.scheduler.schedule(this, (long)this.replicationIntervalSeconds, TimeUnit.SECONDS);
this.scheduledPeriodicRef.set(next);
}
再看注册方法,使用 http rest请求进行注册
client的心跳,续约
this.heartbeatExecutor = new ThreadPoolExecutor(1, this.clientConfig.getHeartbeatExecutorThreadPoolSize(), 0L, TimeUnit.SECONDS, new SynchronousQueue(), (new ThreadFactoryBuilder()).setNameFormat("DiscoveryClient-HeartbeatExecutor-%d").setDaemon(true).build());
在initScheduledTasks()方法中有启动定时任务
this.scheduler.schedule(new TimedSupervisorTask("heartbeat", this.scheduler, this.heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.HeartbeatThread(null)), (long)renewalIntervalInSecs, TimeUnit.SECONDS);
查看线程DiscoveryClient.HeartbeatThread中的run方法,可以看到renew()方法,进行续约
参考:https://mp.weixin.qq.com/s/47TUd96NMz67_PCDyvyInQ
下面是eureka的server端:
server有服务剔除(client没有心跳交互后,会尝试3次,90秒),下线服务
client会定时30秒从server端拉取注册信息,并缓存到本地,30秒重新拉取后会覆盖
server端的自我保护机制,server统计心跳15分钟内,失败是否大于85%,如果大于则进入自我保护机制,不再剔除client,同时接收注册跟查询,但是不再跟其他的节点同步注册信息,等待网路稳定后再同步。
server集群,不分主次,相互之间通过 Replicate 来同步数据,只需要配置一下同步节点的URL
application-eureka-server1.properties、application-eureka-server2.properties配置文件设置了不同的端口(server.port),重点是参数eureka.client.serviceUrl.defaultZone,分别配置对方的地址作为Eureka Client进行相互注册。
Eureka 提供了 Region 和 Zone 两个概念来进行分区,同一分区内进行优先调用。