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中注册服务

相关推荐
superman超哥10 小时前
Rust 内存泄漏检测与防范:超越所有权的内存管理挑战
开发语言·后端·rust·内存管理·rust内存泄漏
悟空码字10 小时前
SpringBoot整合FFmpeg,打造你的专属视频处理工厂
java·spring boot·后端
独自归家的兔10 小时前
Spring Boot 版本怎么选?2/3/4 深度对比 + 迁移避坑指南(含 Java 8→21 适配要点)
java·spring boot·后端
superman超哥10 小时前
Rust 移动语义(Move Semantics)的工作原理:零成本所有权转移的深度解析
开发语言·后端·rust·工作原理·深度解析·rust移动语义·move semantics
_loehuang_11 小时前
【mole】Mole User 开源用户中心:一站式身份认证与权限管理解决方案
spring boot·nacos·开源·用户中心·mole
superman超哥11 小时前
Rust 所有权转移在函数调用中的表现:编译期保证的零成本抽象
开发语言·后端·rust·函数调用·零成本抽象·rust所有权转移
源代码•宸11 小时前
goframe框架签到系统项目开发(实现总积分和积分明细接口、补签日期校验)
后端·golang·postman·web·dao·goframe·补签
无限进步_11 小时前
【C语言】堆(Heap)的数据结构与实现:从构建到应用
c语言·数据结构·c++·后端·其他·算法·visual studio
初次攀爬者11 小时前
基于知识库的知策智能体
后端·ai编程
喵叔哟11 小时前
16.项目架构设计
后端·docker·容器·.net