Spring Cloud Nacos 服務註冊發現解析
一、服務實例註冊原理
1、AbstractAutoServiceRegistration
AbstractAutoServiceRegistration
是spring-cloud-commons包提供的服務註冊流程模版類,不同的註冊中心需要繼承該類來控制相應的註冊流程,及提供相應的註冊實例數據
1.1、AbstractAutoServiceRegistration
源碼概覽
public abstract class AbstractAutoServiceRegistration<R extends Registration>
implements AutoServiceRegistration, ApplicationContextAware,
ApplicationListener<WebServerInitializedEvent> {
...
private AtomicInteger port = new AtomicInteger(0);
private final ServiceRegistry<R> serviceRegistry;
@Deprecated
protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry,
AutoServiceRegistrationProperties properties) {
this.serviceRegistry = serviceRegistry;
this.properties = properties;
}
/**
* 監聽web server初始化完成事件,回調方法
* WebServerInitializedEvent事件由 AbstractApplicationContext#finishRefresh() 的具體實現方法發佈出來,
* 此處監聽方法被回調
*/
@Override
@SuppressWarnings("deprecation")
public void onApplicationEvent(WebServerInitializedEvent event) {
bind(event);
}
@Deprecated
public void bind(WebServerInitializedEvent event) {
ApplicationContext context = event.getApplicationContext();
if (context instanceof ConfigurableWebServerApplicationContext) {
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();
if (shouldRegisterManagement()) {
registerManagement();
}
// 發佈註冊後置事件(提供擴展)
this.context.publishEvent(
new InstanceRegisteredEvent<>(this, getConfiguration()));
this.running.compareAndSet(false, true);
}
}
/**
* Register the local service with the {@link ServiceRegistry}.
*/
protected void register() {
this.serviceRegistry.register(getRegistration());
}
...
}
1.2、AbstractAutoServiceRegistration
源碼說明
我們可以看到,抽象類AbstractAutoServiceRegistration
監聽了WebServerInitializedEvent
事件,所以流程如下:
- spring 容器初始化完成,發佈
WebServerInitializedEvent
事件 - 回調監聽
WebServerInitializedEvent
事件方法AbstractAutoServiceRegistration#onApplicationEvent(WebServerInitializedEvent event)
- 調用
start()
方法執行註冊操作,並在註冊前後分別發佈註冊前置、後置事件 - 調用
register()
方法進行服務實例註冊
由於
AbstractAutoServiceRegistration
是一個spring cloud 提供的抽象註冊模版類,所以Nacos註冊中心提供了NacosAutoServiceRegistration
的具體實現
2、NacosAutoServiceRegistration
解析
主要功能:
提供服務註冊實例的基礎數據(應用名稱、ip 、端口、元數據等信息),以及控制註冊流程
public class NacosAutoServiceRegistration
extends AbstractAutoServiceRegistration<Registration> {
private static final Logger log = LoggerFactory
.getLogger(NacosAutoServiceRegistration.class);
private NacosRegistration registration;
public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
super(serviceRegistry, autoServiceRegistrationProperties);
this.registration = registration;
}
@Deprecated
public void setPort(int port) {
getPort().set(port);
}
@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");
return this.registration;
}
@Override
protected NacosRegistration getManagementRegistration() {
return null;
}
@Override
protected void register() {
if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
log.debug("Registration disabled.");
return;
}
if (this.registration.getPort() < 0) {
this.registration.setPort(getPort().get());
}
super.register();
}
@Override
protected void registerManagement() {
if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
return;
}
super.registerManagement();
}
@Override
protected Object getConfiguration() {
return this.registration.getNacosDiscoveryProperties();
}
@Override
protected boolean isEnabled() {
return this.registration.getNacosDiscoveryProperties().isRegisterEnabled();
}
@Override
@SuppressWarnings("deprecation")
protected String getAppName() {
String appName = registration.getNacosDiscoveryProperties().getService();
return StringUtils.isEmpty(appName) ? super.getAppName() : appName;
}
}
3、NacosServiceRegistry 服務實例註冊類
NacosServiceRegistry
通過實現spring cloud commons公用註冊接口ServiceRegistry
, 來提供把服務實例註冊到Nacos註冊中心的能力,通過上面講到的 AbstractAutoServiceRegistration#register()
方法來觸發註冊
public class NacosServiceRegistry implements ServiceRegistry<Registration> {
private static final Logger log = LoggerFactory.getLogger(NacosServiceRegistry.class);
private final NacosDiscoveryProperties nacosDiscoveryProperties;
private final NamingService namingService;
public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.namingService = nacosDiscoveryProperties.namingServiceInstance();
}
@Override
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
return;
}
// 通過服務實例基礎數據拿到 serviceId
String serviceId = registration.getServiceId();
// 通過配置拿到註冊分組
String group = nacosDiscoveryProperties.getGroup();
Instance instance = getNacosInstanceFromRegistration(registration);
try {
// 通過nacos提供的命名服務api namingService來註冊到nacos註冊中心
namingService.registerInstance(serviceId, group, instance);
log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
instance.getIp(), instance.getPort());
}
catch (Exception e) {
log.error("nacos registry, {} register failed...{},", serviceId,
registration.toString(), e);
}
}
}