这是我参与2022首次更文挑战的第25天,活动详情查看2022首次更文挑战
Spring Cloud项目下应用部署到tomcat(东方通)无法向Nacos中注册服务
使用外部容器启动项目需要做以下步骤
- 在项目的pom.xml文件下配置maven依赖
<!--以war包方式打包-->
<packaging>war</packaging>
<!--使用外部Tomcat启动-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
- 创建一个Initializer类继承SpringBootServletInitializer,并重写configure方法,并且这个类应与项目的Application在同一级目录下,如下图所示
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启动端口号