Springboot War包部署下nacos無法注冊問題



如果需要解決方法,請直接看 9. 如何在WAR包部署下成功注冊nacos部分

1. @EnableDiscoveryClient的使用

在項目中需要使用nacos進行服務治理時,需要在啟動類上添加該注解來實現服務的自動注冊

/**
 * Annotation to enable a DiscoveryClient implementation.
 * @author Spencer Gibb
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {

	/**
	 * If true, the ServiceRegistry will automatically register the local server.
	 * @return - {@code true} if you want to automatically register.
	 */
	boolean autoRegister() default true;

}

autoRegister默認是注冊的,引入EnableDiscoveryClientImportSelector.class

2. EnableDiscoveryClientImportSelector類的作用

首先將源碼貼出來,該類繼承SpringFactoryImportSelector(用來選擇與泛型相關的配置,加載其相應實現)

/**
 * @author Spencer Gibb
 */
@Order(Ordered.LOWEST_PRECEDENCE - 100)
public class EnableDiscoveryClientImportSelector
		extends SpringFactoryImportSelector<EnableDiscoveryClient> {

	@Override
	public String[] selectImports(AnnotationMetadata metadata) {
		String[] imports = super.selectImports(metadata);

		AnnotationAttributes attributes = AnnotationAttributes.fromMap(
				metadata.getAnnotationAttributes(getAnnotationClass().getName(), true));

		boolean autoRegister = attributes.getBoolean("autoRegister");

		if (autoRegister) {
			List<String> importsList = new ArrayList<>(Arrays.asList(imports));
			importsList.add(
					"org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
			imports = importsList.toArray(new String[0]);
		}
		else {
			Environment env = getEnvironment();
			if (ConfigurableEnvironment.class.isInstance(env)) {
				ConfigurableEnvironment configEnv = (ConfigurableEnvironment) env;
				LinkedHashMap<String, Object> map = new LinkedHashMap<>();
				map.put("spring.cloud.service-registry.auto-registration.enabled", false);
				MapPropertySource propertySource = new MapPropertySource(
						"springCloudDiscoveryClient", map);
				configEnv.getPropertySources().addLast(propertySource);
			}
		}
		return imports;
	}

	@Override
	protected boolean isEnabled() {
		return getEnvironment().getProperty("spring.cloud.discovery.enabled",
				Boolean.class, Boolean.TRUE);
	}

	@Override
	protected boolean hasDefaultFactory() {
		return true;
	}

}

1.在selectImports(AnnotationMetadata)方法中獲取@EnableDiscoveryClient中的autoRegister參數,如過為True則將
org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration加入加載列表中。

2.當autoRegister=false時,設spring.cloud.service-registry.auto-registration.enabled=false,這樣跟注冊相關的類將不會自動裝配,因為自動注冊相關的類都有一個條件裝配@ConditionalOnProperty(value ="spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)。

3.AutoServiceRegistrationConfiguration

@Configuration
@EnableConfigurationProperties(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public class AutoServiceRegistrationConfiguration {

}

@ConditionOnProperty為條件裝配,只有spring.cloud.service-registry.auto-registration.enabled=true時才裝配。
該類更像是一個信號量,用來若其加載則會加載其后的那些注冊類

4.NacosDiscoveryAutoConfiguration

/**
 * @author xiaojing
 * @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
 */
@Configuration
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
		AutoServiceRegistrationAutoConfiguration.class })
public class NacosDiscoveryAutoConfiguration {

	@Bean
	public NacosServiceRegistry nacosServiceRegistry(
			NacosDiscoveryProperties nacosDiscoveryProperties) {
		return new NacosServiceRegistry(nacosDiscoveryProperties);
	}

	@Bean
	@ConditionalOnBean(AutoServiceRegistrationProperties.class)
	public NacosRegistration nacosRegistration(
			NacosDiscoveryProperties nacosDiscoveryProperties,
			ApplicationContext context) {
		return new NacosRegistration(nacosDiscoveryProperties, context);
	}

	@Bean
	@ConditionalOnBean(AutoServiceRegistrationProperties.class)
	public NacosAutoServiceRegistration nacosAutoServiceRegistration(
			NacosServiceRegistry registry,
			AutoServiceRegistrationProperties autoServiceRegistrationProperties,
			NacosRegistration registration) {
		return new NacosAutoServiceRegistration(registry,
				autoServiceRegistrationProperties, registration);
	}
}

該類的類加載條件為
@ConditionalOnNacosDiscoveryEnabled即配置中spring.cloud.nacos.discovery.enabled=true
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) 對應項目中是否使用到@EnableDiscoveryClient且其autoRegistry是否為true。

加載順序為@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class,AutoServiceRegistrationAutoConfiguration.class })
意味着若AutoServiceRegistrationConfiguration.class,AutoServiceRegistrationAutoConfiguration.class加載失敗則不進行該類的加載。

同時,在該類中創建NacosServiceRegistry,NacosRegistration,NacosAutoServiceRegistration三個類,后續的注冊調用主要依賴於這三個類進行。

5. NacosServiceRegistry

該類主要實現org.springframework.cloud.client.serviceregistry.ServiceRegistry中的方法
如void register(R registration);方法和void deregister(R registration);方法。其中register中通過層層調用,通過api方式請求nacos的服務。
這里的R泛型的策略是

ServiceRegistry接口

/**
 * Contract to register and deregister instances with a Service Registry.
 *
 */
public interface ServiceRegistry<R extends Registration> {

	/**
	 * Registers the registration. A registration typically has information about an
	 * instance, such as its hostname and port.
	 * @param registration registration meta data
	 */
	void register(R registration);

	/**
	 * Deregisters the registration.
	 * @param registration registration meta data
	 */
	void deregister(R registration);

	/**
	 * Closes the ServiceRegistry. This is a lifecycle method.
	 */
	void close();

	/**
	 * Sets the status of the registration. The status values are determined by the
	 * individual implementations.
	 * @param registration The registration to update.
	 * @param status The status to set.
	 * @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
	 */
	void setStatus(R registration, String status);

	/**
	 * Gets the status of a particular registration.
	 * @param registration The registration to query.
	 * @param <T> The type of the status.
	 * @return The status of the registration.
	 * @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
	 */
	<T> T getStatus(R registration);

}

6. NacosRegistration

該類主要用來做注冊信息的管理實現了Registration, ServiceInstance接口

7. NacosAutoServiceRegistration

該類主要用來調用NacosServiceRegistry中的registry方法進行注冊

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;
	}

}

該類中的register()方法最終會調用super.register();

AbstractAutoServiceRegistration類

public abstract class AbstractAutoServiceRegistration<R extends Registration>
		implements AutoServiceRegistration, ApplicationContextAware,
		ApplicationListener<WebServerInitializedEvent> {

	private static final Log logger = LogFactory
			.getLog(AbstractAutoServiceRegistration.class);

	private final ServiceRegistry<R> serviceRegistry;

	private boolean autoStartup = true;

	private AtomicBoolean running = new AtomicBoolean(false);

	private int order = 0;

	private ApplicationContext context;

	private Environment environment;

	private AtomicInteger port = new AtomicInteger(0);

	private AutoServiceRegistrationProperties properties;

	@Deprecated
	protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry) {
		this.serviceRegistry = serviceRegistry;
	}

	protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry,
			AutoServiceRegistrationProperties properties) {
		this.serviceRegistry = serviceRegistry;
		this.properties = properties;
	}

	protected ApplicationContext getContext() {
		return this.context;
	}

	@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();
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.context = applicationContext;
		this.environment = this.context.getEnvironment();
	}

	@Deprecated
	protected Environment getEnvironment() {
		return this.environment;
	}

	@Deprecated
	protected AtomicInteger getPort() {
		return this.port;
	}

	public boolean isAutoStartup() {
		return this.autoStartup;
	}

	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);
		}

	}


	public void stop() {
		if (this.getRunning().compareAndSet(true, false) && isEnabled()) {
			deregister();
			if (shouldRegisterManagement()) {
				deregisterManagement();
			}
			this.serviceRegistry.close();
		}
	}

}

該類繼承了一個springboot的監聽ApplicationListener器用來監聽WebServerInitializedEvent該事件。
當該事件完成后將調用onApplicationEvent()方法,並調用bind(WebServerInitializedEvent)方法。
在bind(WebServerInitializedEvent)方法中調用start()方法,並在start()方法中調用register()方法。

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;
}

protected void register() {
	this.serviceRegistry.register(getRegistration());
}

其中ServiceRegistry其實現類就是NacosServiceRegistry

8.WebServerInitializedEvent

該事件將在應用上下文完成刷新並准備好之后發布。主要用來獲取正在運行的服務器本地端口。

public abstract class WebServerInitializedEvent extends ApplicationEvent {

	protected WebServerInitializedEvent(WebServer webServer) {
		super(webServer);
	}

	/**
	 * Access the {@link WebServer}.
	 * @return the embedded web server
	   獲取嵌入式的web服務器(即SpringBoot中內置的tomcat)
	 */
	public WebServer getWebServer() {
		return getSource();
	}

	/**
	 * Access the application context that the server was created in. Sometimes it is
	 * prudent to check that this matches expectations (like being equal to the current
	 * context) before acting on the server itself.
	 * @return the applicationContext that the server was created from
	   獲取服務創建時的上下文。有時會謹慎的檢查配置是否復合預期(如 正在作用的是否是當前上下文)
	 */
	public abstract WebServerApplicationContext getApplicationContext();

	/**
	 * Access the source of the event (an {@link WebServer}).
	 * @return the embedded web server
	 訪問事件源
	 */
	@Override
	public WebServer getSource() {
		return (WebServer) super.getSource();
	}

}

可以看到該類主要是獲取Springboot內置的tomcat端口。當使用外置tomcat時,該事件並不能獲取到對應的服務信息。所以也無法在nacos中注冊成功。

9. 如何在WAR包部署下成功注冊nacos

實現ApplicationRunner接口,在應用啟動成功后完成某些初始化工作。


@Component
public class NacosConfig implements ApplicationRunner {
 
    @Autowired(required = false)
    private NacosAutoServiceRegistration registration;
 
    @Value("${baseConfig.nacos.port}")
    Integer port;
 
    @Override
    public void run(ApplicationArguments args) {
        if (registration != null && port != null) {
            Integer tomcatPort = port;
            try {
                tomcatPort = new Integer(getTomcatPort());
            } catch (Exception e) {
                e.printStackTrace();
            }
 
            registration.setPort(tomcatPort);
            registration.start();
        }
    }
 
	/**
	*	獲取外部tomcat端口
	*/
    public String getTomcatPort() throws Exception {
        MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
        Set<ObjectName> objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"), Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")));
        String port = objectNames.iterator().next().getKeyProperty("port");
 
        return port;
    }
}

實現一個監聽

@Component
public class NacosListener implements ApplicationListener<ApplicationReadyEvent> {


    @Autowired
    private NacosRegistration registration;

    @Autowired
    private NacosAutoServiceRegistration nacosAutoServiceRegistration;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        String property = event.getApplicationContext().getEnvironment().getProperty("server.port");
        registration.setPort(Integer.valueOf(property));
        nacosAutoServiceRegistration.start();
    }
}
@EventListener(ApplicationReadyEvent.class)
public void onWebServerReady(ApplicationReadyEvent event) {
    String property = event.getApplicationContext().getEnvironment().getProperty("server.port");
    registration.setPort(Integer.valueOf(property));
    nacosAutoServiceRegistration.start();
}

參考地址:

實現ApplicationRunner接口:https://my.oschina.net/yuhuashang/blog/3086167

源碼:127.0.0.1

springboot事件: https://docs.spring.io/spring-boot/docs/2.1.11.BUILD-SNAPSHOT/reference/html/boot-features-spring-application.html#boot-features-application-events-and-listeners

@EnableDiscoveryClient 注解如何實現服務注冊與發現:https://blog.csdn.net/z694644032/article/details/96706593


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM