SpringBoot部署到外部Tomcat無法註冊到Nacos服務端

事情經過

近期做一個項目投標演示(POC)環境支持,需要集成Nacos服務端。考慮到現有項目中已經有了Nacos相關依賴,那還不簡單?新建個服務端,配置幾下重啓不就搞定了嗎?然而事情遠沒有想得這麼簡單。同樣的代碼在我本地IDE裏運行就能註冊成功,在演示環境 Tomcat+War 部署就不行了。

經過遠程Debug代碼,發現Nacos客戶端的線程都有啓動,卻沒有註冊成功。

思路

想到可能與Tomcat部署模式有關係,就去查了官方issueStackOverFlow

The event is published as part of Spring Boot starting the embedded Tomcat instance. If you're deploying to an external container, there's no embedded container to start and, therefore, no event is published. – Andy Wilkinson

大致是說只有當 Spring Boot 啓動內嵌 Tomcat 成功後,纔會發佈 WebServerInitializedEvent 事件。而Nacos客戶端在等這個事件出現纔會向服務端註冊自己。又因部署在外部Tomcat中就不會初始化內嵌Tomcat,也就沒觸發這個事件。

所以解決方法就是將Nacos等事件的部分代碼調用下,讓他們啓動註冊。

Nacos的自動註冊類是 NacosAutoServiceRegistration,它繼承Spring Cloud的AbstractAutoServiceRegistration,在AbstractAutoServiceRegistration等的 bind(WebServerInitializedEvent)方法監聽事件,設置端口號並啓動註冊。這裏邊 this.port 是從事件中獲取的,需要我們自行獲取。

設置port的位置可見,是從org.springframework.cloud.client.serviceregistry.Registration中取到的,給它設置一下就可以了。

解決辦法

我寫了一個完整的配置類放到了該ISSUE下邊,這裏直接貼在下邊。

import java.lang.management.ManagementFactory;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.Query;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

import com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration;
import com.alibaba.cloud.nacos.registry.NacosRegistration;

@Configuration
public class NacosWarDeployConfig {
    private static final Logger logger = LoggerFactory.getLogger(NacosWarDeployConfig.class);

    @Autowired
    private Environment env;
    @Autowired
    private NacosRegistration registration;
    @Autowired
    private NacosAutoServiceRegistration nacosAutoServiceRegistration;

    @PostConstruct
    public void nacosServerRegister() {
        if (registration != null) {
            registration.setPort(getTomcatPort());
            nacosAutoServiceRegistration.start();
        }
    }

    public int getTomcatPort() {
        try {
            return getProvideTomcatPort();
        } catch (Exception e) {
            logger.warn("obtain provide tomcat port failed, fallback to embeded tomcat port.");
        }
        return getEmbeddedTomcatPort();
    }

    private int getProvideTomcatPort() throws MalformedObjectNameException, NullPointerException {
        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 Integer.valueOf(port);
    }

    private int getEmbeddedTomcatPort() {
        return env.getProperty("server.port", Integer.class, 8080);
    }

}

經過我這一波操作問題終於解決了。我是Hellxz,不在進坑就在爬坑的路上。

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