本文共 6877 字,大约阅读时间需要 22 分钟。
@EnableEurekaClient
@EnableDiscoveryClient -->EnableDiscoveryClientImportSelector
EnableDiscoveryClientImportSelector继承了SpringFactoryImportSelector
SpringFactoryImportSelector中selectImports中有下面一段话SpringFactoriesLoader进行META-INF/spring.factories中的configuration进行装载
// Find all possible auto configuration classes, filtering duplicates Listfactories = 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 ListgetInstances(String serviceId) { List infos = this.eurekaClient.getInstancesByVipAddress(serviceId, false); List 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, ProviderbackupRegistryProvider) { 。。。 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()方法,进行续约
参考:
下面是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 两个概念来进行分区,同一分区内进行优先调用。
参考地址:
转载地址:http://lxadi.baihongyu.com/