应用启动无法向Nacos中注册服务

679 阅读2分钟

这是我参与2022首次更文挑战的第25天,活动详情查看2022首次更文挑战

Spring Cloud项目下应用部署到tomcat(东方通)无法向Nacos中注册服务

使用外部容器启动项目需要做以下步骤

  1. 在项目的pom.xml文件下配置maven依赖
   <!--以war包方式打包-->
   <packaging>war</packaging>
 
 <!--使用外部Tomcat启动-->
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
  </dependency>
  1. 创建一个Initializer类继承SpringBootServletInitializer,并重写configure方法,并且这个类应与项目的Application在同一级目录下,如下图所示

image-20220210174948822.png

public class ServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder springApplicationBuilder){
        return springApplicationBuilder.sources(TrsTeachingManagementApplication.class);
    }
}

问题

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

查找原因

查看Nacos源码,从Nacos的注册类查看,查找后发现Nacos注册类NacosAutoServiceRegistration继承了Spring Cloud中AbstractAutoServiceRegistration,而在AbstractAutoServiceRegistration中绑定了一个监听事件,监听内置容器启动完成事件,监听到获取容器端口后向注册中心注册。

public class NacosAutoServiceRegistration extends AbstractAutoServiceRegistration<Registration> {}

@Deprecated
    public void bind(WebServerInitializedEvent event) {
        ApplicationContext context = event.getApplicationContext();
        if (!(context instanceof ConfigurableWebServerApplicationContext) || !"management".equals(((ConfigurableWebServerApplicationContext)context).getServerNamespace())) {
            this.port.compareAndSet(0, event.getWebServer().getPort());
            this.start();
        }
    }

因为使用外部容器部署时,无法监听对应事件,所以项目启动后向注册中心失败。

解决方案

Spring Boot提供了ApplicationRunner接口,在应用启动后指定一些初始化操作,可通过这个接口实现向注册中心注册服务,使用这种方式,需要在配置文件中配置好端口号,有一种情况是一个应用以不同端口号部署启动,那每个应用都需要配置,这种方式不是很便利,换成获取外部Tomcat端口来自动设置端口并向注册中心注册。

@Component
public class NacosConfig implements ApplicationRunner {

    @Autowired(required = false)
    private NacosAutoServiceRegistration nacosAutoServiceRegistration;
    @Value("${server.port}")
    private Integer port;

    @Override
    public void run(ApplicationArguments args) throws Exception {
            if(nacosAutoServiceRegistration != null & port != null){
                Integer tomcatPort = port;
                tomcatPort = Integer.valueOf(getTomcatPort());
                nacosAutoServiceRegistration.setPort(port);
                nacosAutoServiceRegistration.start();
            }

    }

    public String getTomcatPort() throws MalformedObjectNameException {
        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;
    }
}

扫描Tomcat的server.xml文件下得到Bean定义获取tomcat启动端口号

image-20220210163448658.png