core:Java功能增强 —— 事件机制(事件与监听器)
Eureka提供了服务注册与发现的功能,需要提供一个服务注册中心(我这里是在spring boot项目启动类上使用@EnableEurekaServer,再配置相关属性),然后在我们的应用服务(我这里是spring boot服务)启动类上使用@EnableDiscoveryClient启用服务注册发现功能。那么问题来了,我们的服务是怎么注册到注册中心的呢?
使用@EnableDiscoveryClient注解后启动服务,我们可以发现控制台打出了如下log:
从图中可以看出的信息有:1、是在EurekaDiscoveryClientConfiguration中将服务器注册到eureka的。2、DiscoveryClient类里面应该有许多服务器与eureka之间的通信操作,如心跳续约等。3、InstanceInfoReplicator应该是一个线程类。
既然EurekaDiscoveryClientConfiguration是最开始的地方,那就从它看起。
1、EurekaDiscoveryClientConfiguration
源码如下:
@Configuration //使用该注解来让Spring容器发现并注册里面的Bean
@EnableConfigurationProperties
@ConditionalOnClass({EurekaClientConfig.class})
@ConditionalOnProperty(
value = {"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注册中心,重新启动服务,发现报了如下错:
嗯,意料之中,肯定连不上啊,从这里可以看出,发请求是RedirectingEurekaHttpClient请求客户端做的,接着往下:
就像一开始猜想的,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();