nacos主要有两部分的功能:配置中心和注册中心,之前几篇文章讲了nacos作为配置中心的实现原理,这篇文章主要介绍下spring cloud如何集成nacos实现服务注册。
spring cloud服务注册具体逻辑是通过AbstractAutoServiceRegistration类实现的,它会监听WebServerInitializedEvent事件,当server初始化完成后,会自动注册服务到注册中心。spring cloud还停供了ServiceRegistry和Registration接口类,ServiceRegistry接口负责实现具体的注册逻辑,Registration接口是定义了注册的实例节点信息,包括实例ip、端口、服务id等信息。我们如果要自己实现服务注册功能,只需实现ServiceRegistry和Registration接口方法,并继承AbstractAutoServiceRegistration类实现里面的abstract方法即可。
先看一下AbstractAutoServiceRegistration的启动注册逻辑代码:
//监听WebServerInitializedEvent事件
@Override
@SuppressWarnings("deprecation")
public void onApplicationEvent(WebServerInitializedEvent event) {
bind(event);
}
@Deprecated
public void bind(WebServerInitializedEvent event) {
ApplicationContext context = event.getApplicationContext();
if (context instanceof ConfigurableWebServerApplicationContext) {
// 过滤掉management server
if ("management".equals(((ConfigurableWebServerApplicationContext) context)
.getServerNamespace())) {
return;
}
}
//保存服务的端口号
this.port.compareAndSet(0, event.getWebServer().getPort());
//启动服务注册
this.start();
}
public void start() {
if (!isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
return;
}
// only initialize if nonSecurePort is greater than 0 and it isn't already running
// because of containerPortInitializer below
if (!this.running.get()) {
//发布预注册事件
this.context.publishEvent(
new InstancePreRegisteredEvent(this, getRegistration()));
//实际注册方法
register();
//如果management不为空,也注册下
if (shouldRegisterManagement()) {
registerManagement();
}
//发布注册完成事件
this.context.publishEvent(
new InstanceRegisteredEvent<>(this, getConfiguration()));
//标记为已注册成功
this.running.compareAndSet(false, true);
}
}
protected void register() {
//调用注册方法,serviceRegistry是在构造函数中传进来的
this.serviceRegistry.register(getRegistration());
}
//子类只需实现此方法,把实例对象信息传递进来,即可实现服务注册功能
protected abstract R getRegistration();
nacos集成到spring cloud实现服务注册功能的逻辑跟上面讲到的方法是一样的,先是NacosAutoServiceRegistration继承了AbstractAutoServiceRegistration,然后NacosServiceRegistry和NacosRegistration分别实现了ServiceRegistry和Registration接口的方法。另外,NacosServiceRegistryAutoConfiguration类会把上述三个类自动实例化。这样,nacos就实现了应用启动服务时自动进行服务注册。
先看NacosAutoServiceRegistration的构造函数,它分别注入了NacosServiceRegistry和NacosRegistration类。
public NacosAutoServiceRegistration(ServiceRegistry serviceRegistry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
super(serviceRegistry, autoServiceRegistrationProperties);
this.registration = registration;
}
NacosAutoServiceRegistration的getRegistration方法就是返回的注入进来的NacosRegistration类:
@Override
protected NacosRegistration getRegistration() {
//初始化实例的端口信息
if (this.registration.getPort() < 0 && this.getPort().get() > 0) {
this.registration.setPort(this.getPort().get());
}
Assert.isTrue(this.registration.getPort() > 0, "service.port has not been set");
//返回NacosRegistration对象
return this.registration;
}
再看一下NacosRegistration的实例信息,基本上都是从配置信息类里直接取的:
@Override
public String getServiceId() {
//服务名
return nacosDiscoveryProperties.getService();
}
@Override
public String getHost() {
//实例ip
return nacosDiscoveryProperties.getIp();
}
@Override
public int getPort() {
//端口
return nacosDiscoveryProperties.getPort();
}
@Override
public boolean isSecure() {
//是否https
return nacosDiscoveryProperties.isSecure();
}
@Override
public URI getUri() {
return DefaultServiceInstance.getUri(this);
}
@Override
public Map getMetadata() {
//获取元数据
return nacosDiscoveryProperties.getMetadata();
}
NacosServiceRegistry是直接调用nacos服务端的服务注册和取消注册接口来实现的:
@Override
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
return;
}
NamingService namingService = namingService();
String serviceId = registration.getServiceId();
String group = nacosDiscoveryProperties.getGroup();
//把NacosRegistration转化为nacos自有的Instance对象
Instance instance = getNacosInstanceFromRegistration(registration);
try {
//调用nacos jdk的注册方法
namingService.registerInstance(serviceId, group, instance);
log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
instance.getIp(), instance.getPort());
}
catch (Exception e) {
if (nacosDiscoveryProperties.isFailFast()) {
log.error("nacos registry, {} register failed...{},", serviceId,
registration.toString(), e);
rethrowRuntimeException(e);
}
else {
log.warn("Failfast is false. {} register failed...{},", serviceId,
registration.toString(), e);
}
}
}
@Override
public void deregister(Registration registration) {
log.info("De-registering from Nacos Server now...");
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No dom to de-register for nacos client...");
return;
}
NamingService namingService = namingService();
String serviceId = registration.getServiceId();
String group = nacosDiscoveryProperties.getGroup();
try {
//调用nacos JDK的取消注册方法
namingService.deregisterInstance(serviceId, group, registration.getHost(),
registration.getPort(), nacosDiscoveryProperties.getClusterName());
}
catch (Exception e) {
log.error("ERR_NACOS_DEREGISTER, de-register failed...{},",
registration.toString(), e);
}
log.info("De-registration finished.");
}