SpringCloud Alibaba Nacos服務註冊原理

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 事件,所以流程如下:

  1. spring 容器初始化完成,發佈 WebServerInitializedEvent 事件
  2. 回調監聽WebServerInitializedEvent 事件方法 AbstractAutoServiceRegistration#onApplicationEvent(WebServerInitializedEvent event)
  3. 調用start() 方法執行註冊操作,並在註冊前後分別發佈註冊前置、後置事件
  4. 調用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);
		}
	}

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章