Spring Boot 构建war 部署到tomcat下无法在Nacos中注册服务

Spring Boot 构建war 部署到tomcat下无法在Nacos中注册服务

  • [1. 问题](#1. 问题)
  • [2. 分析](#2. 分析)
  • [3. 解决方案](#3. 解决方案)
  • 参考

1. 问题

使用Nacos作为注册中心的Spring Boot项目,以war包形式部署到服务器上,启动项目发现该服务无法在Nacos中注册。

2. 分析

SpringCloud 项目打 war 包部署时,也就是使用外部 Tomcat 部署,其启动命令、端口等是由外部容器 Tomcat 配置的,而 Nacos 或者其他服务注册方式需要当前项目的端口号用于注册微服务。

以 Nacos 为例,其自动注册微服务的类是 NacosAutoServiceRegistration,我们看一下它的源码:

java 复制代码
public class NacosAutoServiceRegistration extends AbstractAutoServiceRegistration<Registration> {

    private NacosRegistration registration;

    @Deprecated
    public void setPort(int port) {
        this.getPort().set(port);
    }

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

我们看到 NacosAutoServiceRegistration 使用了 this.registration.setPort(this.getPort().get()); 来设置端口号。

而端口号是从其父类 AbstractAutoServiceRegistration 中的方法获取的:

java 复制代码
public abstract class AbstractAutoServiceRegistration<R extends Registration>
		implements AutoServiceRegistration, ApplicationContextAware,
		ApplicationListener<WebServerInitializedEvent> {

	private AtomicInteger port = new AtomicInteger(0);


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

这段代码监听了内置容器启动完成事件,监听获取到容器端口后,向注册中心注册服务。

因此,当使用外部容器时,如此处的 Tomcat 来部署项目,AbstractAutoServiceRegistration 就不能监听到容器启动事件了,也就不会尝试向服务注册中心注册当前这个微服务,那么注册就失败了,并且也就没有异常信息了。

3. 解决方案

自定义获取获取外部容器端口的方法, 然后监听应用启动事件,当应用被启动时,获取外部容器启动的端口号,然后将这个 port 设置到 NacosAutoServiceReigistration 中。

具体操作流程:

Spring Boot提供了ApplicationRunner接口,是在应用起好之后执行一些初始化动作。通过这个接口我们可以实现启动项目后注册服务。使用这种方法,需要在配置文件中配置端口号,如果一个应用部署很多端口,每个应用都要配置,很不方便。故可获取外部tomcat自动设置端口。经测试,方法可行。

在启动类同级目录,复制下面代码即可(不需要修改,直接构建war即可)

java 复制代码
import java.lang.management.ManagementFactory;
import java.util.Set;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.Query;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

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

import lombok.extern.slf4j.Slf4j;

/**
 * 项目打包war情况下部署外部tomcat,需该方式注册nacos
 */
@Component
@Slf4j
public class NacosRegisterOnWar implements ApplicationRunner {

    @Autowired
    private NacosRegistration registration;

    @Autowired
    private NacosAutoServiceRegistration nacosAutoServiceRegistration;

    @Value("${server.port}")
    Integer port;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        if (registration != null && port != null) {
            Integer tomcatPort = port;
            try {
                tomcatPort = new Integer(getTomcatPort());
            } catch (Exception e) {
                log.warn("获取外部Tomcat端口异常:", e);
            }
            registration.setPort(tomcatPort);
            nacosAutoServiceRegistration.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;
    }
}

提示:

在部署项目要注意版本问题,如Spring Boot 2.0.6应该部署在tomcat8以上版本

参考

Spring Boot 构建war 部署到tomcat下无法在Nacos中注册服务

相关推荐
字节跳动数据库7 小时前
文章分享——相似函数处理方法
人工智能·后端·程序员
云技纵横7 小时前
@Transactional 失效的 7 种场景:第 5 种最难排查
后端
用户6757049885028 小时前
你知道 Go 结构体和结构体指针调用的区别吗?一文带你彻底搞懂!
后端·go
程序员cxuan8 小时前
读懂 Claude Code 架构分析系列,第一篇,开始!
人工智能·后端·架构
用户6757049885028 小时前
面试官问“装饰器模式”,这样回答薪资多要 3000!
后端
tntxia8 小时前
Geo Scene域名修改引起的一些问题
后端
用户298698530148 小时前
Java 实现 Word 文档加密与权限解除
java·后端
vanuan9 小时前
给你的A2A-Agent加把锁-认证鉴权实战指南
后端
Yeats_Liao9 小时前
14:Servlet中的页面跳转-Java Web
java·后端·架构