现象
Spring boot 或者 Spring cloud 项目,如果用默认的打包jar, 使用内嵌的tomcat 启动,可以正常注册到Nacos 注册中心。
如果打包为war,部署到外部tomcat, 调用正常,消费者可以调用到 生产者服务,但是Nacos 后台的服务列表里没有显示。很诡异。。。
分析
查看源码,需从nacos的注册类找起,查找后发现,nacos注册类NacosAutoServiceRegistration继承了Spring Cloud中AbstractAutoServiceRegistration, 而在AbstractAutoServiceRegistration中绑定了一个监听事件,监听内置容器启动完成事件,监听到获取容器端口后向注册中心注册。
@EventListener({WebServerInitializedEvent.class})
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();
}
}
而使用外部容器时,不能监听到事件,所以自动注册失败。
解决方法
使用ApplicationRunner在服务启动完成后,加载注册。
import com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration;
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 javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.Query;
import java.lang.management.ManagementFactory;
import java.util.Set;
/**
* @author liushengbing
* @version 1.0.0
* @ClassName NacosRegisterOnWar.java
* @Description 项目打包war情况下部署外部tomcat,需要该方式注册nacos
* @createTime 2021年07月08日 16:36
*/
@Component
public class NacosRegisterOnWar implements ApplicationRunner {
@Autowired(required = false)
private NacosAutoServiceRegistration registration;
@Value("${server.port}")
Integer port;
/**
* Callback used to run the bean.
*
* @param args incoming application arguments
* @throws Exception on error
*/
@Override
public void run(ApplicationArguments args) throws Exception {
if(registration != null && port != null){
Integer tomcatPort = port;
try{
tomcatPort = new Integer(getTomcatPort());
registration.setPort(tomcatPort);
registration.start();
}catch(Exception e){
System.out.println("使用内置tomcat启动,不需要单独注册nacos服务");
}
}
}
/**
* @Author liushengbing
* @Description 获取外部tomcat端口
* @Date 2021/7/8 16:43
* @Param
* @return
*/
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;
}
}