最近把一個 SpringBoot 項(xiàng)目接入了微服務(wù),使用 SpringBoot 2.1.9.RELASE 和 SpringCloud Greewich.SR3 。本地測試都正常,但是上線后千诬,出現(xiàn)了一個這樣一個情況:
日志未出現(xiàn) Nacos Registery
類似的語句,也沒有任何異常信息,就像是壓根就沒有配置微服務(wù)一樣丁侄。
檢查了配置文件,正常
本地測試朝巫,正常注冊
后來找組長幫忙看了一下鸿摇,發(fā)現(xiàn)是 war 包部署的問題:
SpringCloud 項(xiàng)目打 war 包部署時(shí),也就是使用外部 Tomcat 部署劈猿,其啟動命令拙吉、端口等是由外部容器 Tomcat 配置的,而 Nacos 或者其他服務(wù)注冊方式需要當(dāng)前項(xiàng)目的端口號用于注冊微服務(wù)揪荣。
以 Nacos 為例筷黔,其自動注冊微服務(wù)的類是 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()); 來設(shè)置端口號仗颈。
而端口號是從其父類 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();
}
這段代碼監(jiān)聽了內(nèi)置容器啟動完成事件佛舱,監(jiān)聽獲取到容器端口后,向注冊中心注冊服務(wù)挨决。
因此请祖,當(dāng)使用外部容器時(shí),如此處的 Tomcat 來部署項(xiàng)目凰棉,AbstractAutoServiceRegistration 就不能監(jiān)聽到容器啟動事件了损拢,也就不會嘗試向服務(wù)注冊中心注冊當(dāng)前這個微服務(wù),那么注冊就失敗了撒犀,并且也就沒有異常信息了福压。
解決辦法是自定義獲取獲取外部容器端口的方法, 然后監(jiān)聽?wèi)?yīng)用啟動事件或舞,當(dāng)應(yīng)用被啟動時(shí)荆姆,獲取外部容器啟動的端口號,然后將這個 port 設(shè)置到 NacosAutoServiceReigistration 中映凳。
下面是完整的解決方法:
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.cloud.alibaba.nacos.registry.NacosAutoServiceRegistration;
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;
@Component
public class NacosConfig implements ApplicationRunner {
@Autowired(required = false)
private NacosAutoServiceRegistration registration;
@Value("${server.port}")
Integer port;
@Override
public void run(ApplicationArguments args) {
if (registration != null && port != null) {
Integer tomcatPort = port;
try {
tomcatPort = new Integer(getTomcatPort());
} catch (Exception e) {
e.printStackTrace();
}
registration.setPort(tomcatPort);
registration.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;
}
}
交流學(xué)習(xí)
個人微信:guanlanju6688
個人網(wǎng)站:http://www.eknown.cn
GitHub:https://github.com/laolunsi
公眾號:猿生物語胆筒,"分享技術(shù),也感悟人生",歡迎關(guān)注仆救!