Eureka|Eureka 服务注册与发现

core:Java功能增强 —— 事件机制(事件与监听器) Eureka提供了服务注册与发现的功能,需要提供一个服务注册中心(我这里是在spring boot项目启动类上使用@EnableEurekaServer,再配置相关属性),然后在我们的应用服务(我这里是spring boot服务)启动类上使用@EnableDiscoveryClient启用服务注册发现功能。那么问题来了,我们的服务是怎么注册到注册中心的呢?
使用@EnableDiscoveryClient注解后启动服务,我们可以发现控制台打出了如下log:

Eureka|Eureka 服务注册与发现
文章图片
image.png
【Eureka|Eureka 服务注册与发现】从图中可以看出的信息有:1、是在EurekaDiscoveryClientConfiguration中将服务器注册到eureka的。2、DiscoveryClient类里面应该有许多服务器与eureka之间的通信操作,如心跳续约等。3、InstanceInfoReplicator应该是一个线程类。
既然EurekaDiscoveryClientConfiguration是最开始的地方,那就从它看起。
1、EurekaDiscoveryClientConfiguration 源码如下:

@Configuration//使用该注解来让Spring容器发现并注册里面的Bean @EnableConfigurationProperties @ConditionalOnClass({EurekaClientConfig.class}) @ConditionalOnProperty( value = https://www.it610.com/article/{"eureka.client.enabled"}, matchIfMissing = true ) public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Ordered {public void start() { if (this.port.get() != 0 && this.instanceConfig.getNonSecurePort() == 0) { this.instanceConfig.setNonSecurePort(this.port.get()); }if (!this.running.get() && this.instanceConfig.getNonSecurePort() > 0) { this.maybeInitializeClient(); if (log.isInfoEnabled()) { log.info("Registering application " + this.instanceConfig.getAppname() + " with eureka with status " + this.instanceConfig.getInitialStatus()); }this.applicationInfoManager.setInstanceStatus(this.instanceConfig.getInitialStatus()); if (this.healthCheckHandler != null) { this.eurekaClient.registerHealthCheck(this.healthCheckHandler); } //发布了一个注册事件,在哪里监听的?还是说是留给我们开发者用的? this.context.publishEvent(new InstanceRegisteredEvent(this, this.instanceConfig)); this.running.set(true); }}private void maybeInitializeClient() { this.applicationInfoManager.getInfo(); this.eurekaClient.getApplications(); }public void stop() { if (this.applicationInfoManager.getInfo() != null) { if (log.isInfoEnabled()) { log.info("Unregistering application " + this.instanceConfig.getAppname() + " with eureka with status DOWN"); }this.applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN); }this.running.set(false); }//监听,内置容器启动时开始注册 @EventListener({EmbeddedServletContainerInitializedEvent.class}) public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) { int localPort = event.getEmbeddedServletContainer().getPort(); if (this.port.get() == 0) { log.info("Updating port to " + localPort); this.port.compareAndSet(0, localPort); this.start(); }}//监听,应用关闭时关闭eureka客户端 @EventListener({ContextClosedEvent.class}) public void onApplicationEvent(ContextClosedEvent event) { this.stop(); this.eurekaClient.shutdown(); } }

按猜想的来说应该有一个Rest请求什么的将服务器信息发送到eureka服务器才对,好像上面的start()方法没有做什么事情。
我们关闭了eureka注册中心,重新启动服务,发现报了如下错:

Eureka|Eureka 服务注册与发现
文章图片
image.png
嗯,意料之中,肯定连不上啊,从这里可以看出,发请求是RedirectingEurekaHttpClient请求客户端做的,接着往下:

Eureka|Eureka 服务注册与发现
文章图片
image.png
就像一开始猜想的,DiscoveryClient里面有与eureka的通信操作,而InstanceInfoReplicator应该是一个线程类。进去看看: DiscoveryClient使用@Singleton修饰,里面有这么两个函数,可以看出是注册与续约:
boolean register() throws Throwable { logger.info("DiscoveryClient_" + this.appPathIdentifier + ": registering service..."); EurekaHttpResponse httpResponse; try { httpResponse = this.eurekaTransport.registrationClient.register(this.instanceInfo); } catch (Exception var3) { logger.warn("{} - registration failed {}", new Object[]{"DiscoveryClient_" + this.appPathIdentifier, var3.getMessage(), var3}); throw var3; }if (logger.isInfoEnabled()) { logger.info("{} - registration status: {}", "DiscoveryClient_" + this.appPathIdentifier, httpResponse.getStatusCode()); }return httpResponse.getStatusCode() == 204; }boolean renew() { try { EurekaHttpResponse httpResponse = this.eurekaTransport.registrationClient.sendHeartBeat(this.instanceInfo.getAppName(), this.instanceInfo.getId(), this.instanceInfo, (InstanceStatus)null); logger.debug("{} - Heartbeat status: {}", "DiscoveryClient_" + this.appPathIdentifier, httpResponse.getStatusCode()); if (httpResponse.getStatusCode() == 404) { this.REREGISTER_COUNTER.increment(); logger.info("{} - Re-registering apps/{}", "DiscoveryClient_" + this.appPathIdentifier, this.instanceInfo.getAppName()); return this.register(); } else { return httpResponse.getStatusCode() == 200; } } catch (Throwable var3) { logger.error("{} - was unable to send heartbeat!", "DiscoveryClient_" + this.appPathIdentifier, var3); return false; } }

这个register()就是InstanceInfoReplicator的run方法里调用的,而InstanceInfoReplicator是在DiscoveryClient的构造方法中实例化的。
在eureka服务器端则是将服务信息存放在一个双层Map里,第一层的Key是服务名,第二层的Key是实例名:
private final ConcurrentHashMap>> registry = new ConcurrentHashMap();

    推荐阅读