赞
踩
使用Nacos作为注册中心的Spring Boot项目,以war包形式部署到服务器上,启动项目发现该服务无法在Nacos中注册。
SpringCloud 项目打 war 包部署时,也就是使用外部 Tomcat 部署,其启动命令、端口等是由外部容器 Tomcat 配置的,而 Nacos 或者其他服务注册方式需要当前项目的端口号用于注册微服务。
以 Nacos 为例,其自动注册微服务的类是 NacosAutoServiceRegistration,我们看一下它的源码:
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 中的方法获取的:
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 就不能监听到容器启动事件了,也就不会尝试向服务注册中心注册当前这个微服务,那么注册就失败了,并且也就没有异常信息了。
自定义获取获取外部容器端口的方法, 然后监听应用启动事件,当应用被启动时,获取外部容器启动的端口号,然后将这个 port 设置到 NacosAutoServiceReigistration 中。
具体操作流程:
Spring Boot提供了ApplicationRunner接口,是在应用起好之后执行一些初始化动作。通过这个接口我们可以实现启动项目后注册服务。使用这种方法,需要在配置文件中配置端口号,如果一个应用部署很多端口,每个应用都要配置,很不方便。故可获取外部tomcat自动设置端口。经测试,方法可行。
在启动类同级目录,复制下面代码即可(不需要修改,直接构建war即可)
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以上版本
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。