如果需要解决方法,请直接看9. 如何在WAR包部署下成功注册nacos部分

1. @EnableDiscoveryClient的使用

在项目中需要使用nacos进行服务治理时,需要在启动类上添加该注解来实现服务的自动注册

/**
* Annotation to enable a DiscoveryClient implementation.
* @author Spencer Gibb
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient { /**
* If true, the ServiceRegistry will automatically register the local server.
* @return - {@code true} if you want to automatically register.
*/
boolean autoRegister() default true; }

autoRegister默认是注册的,引入EnableDiscoveryClientImportSelector.class

2. EnableDiscoveryClientImportSelector类的作用

首先将源码贴出来,该类继承SpringFactoryImportSelector(用来选择与泛型相关的配置,加载其相应实现)

/**
* @author Spencer Gibb
*/
@Order(Ordered.LOWEST_PRECEDENCE - 100)
public class EnableDiscoveryClientImportSelector
extends SpringFactoryImportSelector<EnableDiscoveryClient> { @Override
public String[] selectImports(AnnotationMetadata metadata) {
String[] imports = super.selectImports(metadata); AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(getAnnotationClass().getName(), true)); boolean autoRegister = attributes.getBoolean("autoRegister"); if (autoRegister) {
List<String> importsList = new ArrayList<>(Arrays.asList(imports));
importsList.add(
"org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
imports = importsList.toArray(new String[0]);
}
else {
Environment env = getEnvironment();
if (ConfigurableEnvironment.class.isInstance(env)) {
ConfigurableEnvironment configEnv = (ConfigurableEnvironment) env;
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("spring.cloud.service-registry.auto-registration.enabled", false);
MapPropertySource propertySource = new MapPropertySource(
"springCloudDiscoveryClient", map);
configEnv.getPropertySources().addLast(propertySource);
}
}
return imports;
} @Override
protected boolean isEnabled() {
return getEnvironment().getProperty("spring.cloud.discovery.enabled",
Boolean.class, Boolean.TRUE);
} @Override
protected boolean hasDefaultFactory() {
return true;
} }

1.在selectImports(AnnotationMetadata)方法中获取@EnableDiscoveryClient中的autoRegister参数,如过为True则将

org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration加入加载列表中。

2.当autoRegister=false时,设spring.cloud.service-registry.auto-registration.enabled=false,这样跟注册相关的类将不会自动装配,因为自动注册相关的类都有一个条件装配@ConditionalOnProperty(value ="spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)。

3.AutoServiceRegistrationConfiguration

@Configuration
@EnableConfigurationProperties(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public class AutoServiceRegistrationConfiguration { }

@ConditionOnProperty为条件装配,只有spring.cloud.service-registry.auto-registration.enabled=true时才装配。

该类更像是一个信号量,用来若其加载则会加载其后的那些注册类

4.NacosDiscoveryAutoConfiguration

/**
* @author xiaojing
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Configuration
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
AutoServiceRegistrationAutoConfiguration.class })
public class NacosDiscoveryAutoConfiguration { @Bean
public NacosServiceRegistry nacosServiceRegistry(
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosDiscoveryProperties);
} @Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosRegistration nacosRegistration(
NacosDiscoveryProperties nacosDiscoveryProperties,
ApplicationContext context) {
return new NacosRegistration(nacosDiscoveryProperties, context);
} @Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
NacosServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);
}
}

该类的类加载条件为

@ConditionalOnNacosDiscoveryEnabled即配置中spring.cloud.nacos.discovery.enabled=true

@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) 对应项目中是否使用到@EnableDiscoveryClient且其autoRegistry是否为true。

加载顺序为@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class,AutoServiceRegistrationAutoConfiguration.class })

意味着若AutoServiceRegistrationConfiguration.class,AutoServiceRegistrationAutoConfiguration.class加载失败则不进行该类的加载。

同时,在该类中创建NacosServiceRegistry,NacosRegistration,NacosAutoServiceRegistration三个类,后续的注册调用主要依赖于这三个类进行。

5. NacosServiceRegistry

该类主要实现org.springframework.cloud.client.serviceregistry.ServiceRegistry中的方法

如void register(R registration);方法和void deregister(R registration);方法。其中register中通过层层调用,通过api方式请求nacos的服务。

这里的R泛型的策略是

ServiceRegistry接口

/**
* Contract to register and deregister instances with a Service Registry.
*
*/
public interface ServiceRegistry<R extends Registration> { /**
* Registers the registration. A registration typically has information about an
* instance, such as its hostname and port.
* @param registration registration meta data
*/
void register(R registration); /**
* Deregisters the registration.
* @param registration registration meta data
*/
void deregister(R registration); /**
* Closes the ServiceRegistry. This is a lifecycle method.
*/
void close(); /**
* Sets the status of the registration. The status values are determined by the
* individual implementations.
* @param registration The registration to update.
* @param status The status to set.
* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
*/
void setStatus(R registration, String status); /**
* Gets the status of a particular registration.
* @param registration The registration to query.
* @param <T> The type of the status.
* @return The status of the registration.
* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
*/
<T> T getStatus(R registration); }

6. NacosRegistration

该类主要用来做注册信息的管理实现了Registration, ServiceInstance接口

7. NacosAutoServiceRegistration

该类主要用来调用NacosServiceRegistry中的registry方法进行注册

public class NacosAutoServiceRegistration
extends AbstractAutoServiceRegistration<Registration> {
private static final Logger log = LoggerFactory
.getLogger(NacosAutoServiceRegistration.class); private NacosRegistration registration; public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
super(serviceRegistry, autoServiceRegistrationProperties);
this.registration = registration;
} @Deprecated
public void setPort(int port) {
getPort().set(port);
} @Override
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;
} @Override
protected NacosRegistration getManagementRegistration() {
return null;
} @Override
protected void register() {
if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
log.debug("Registration disabled.");
return;
}
if (this.registration.getPort() < 0) {
this.registration.setPort(getPort().get());
}
super.register();
} @Override
protected void registerManagement() {
if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
return;
}
super.registerManagement(); } @Override
protected Object getConfiguration() {
return this.registration.getNacosDiscoveryProperties();
} @Override
protected boolean isEnabled() {
return this.registration.getNacosDiscoveryProperties().isRegisterEnabled();
} @Override
@SuppressWarnings("deprecation")
protected String getAppName() {
String appName = registration.getNacosDiscoveryProperties().getService();
return StringUtils.isEmpty(appName) ? super.getAppName() : appName;
} }

该类中的register()方法最终会调用super.register();

AbstractAutoServiceRegistration类

public abstract class AbstractAutoServiceRegistration<R extends Registration>
implements AutoServiceRegistration, ApplicationContextAware,
ApplicationListener<WebServerInitializedEvent> { private static final Log logger = LogFactory
.getLog(AbstractAutoServiceRegistration.class); private final ServiceRegistry<R> serviceRegistry; private boolean autoStartup = true; private AtomicBoolean running = new AtomicBoolean(false); private int order = 0; private ApplicationContext context; private Environment environment; private AtomicInteger port = new AtomicInteger(0); private AutoServiceRegistrationProperties properties; @Deprecated
protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry) {
this.serviceRegistry = serviceRegistry;
} protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry,
AutoServiceRegistrationProperties properties) {
this.serviceRegistry = serviceRegistry;
this.properties = properties;
} protected ApplicationContext getContext() {
return this.context;
} @Override
@SuppressWarnings("deprecation")
public void onApplicationEvent(WebServerInitializedEvent event) {
bind(event);
} @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();
} @Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.context = applicationContext;
this.environment = this.context.getEnvironment();
} @Deprecated
protected Environment getEnvironment() {
return this.environment;
} @Deprecated
protected AtomicInteger getPort() {
return this.port;
} public boolean isAutoStartup() {
return this.autoStartup;
} public void start() {
if (!isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
return;
} // only initialize if nonSecurePort is greater than 0 and it isn't already running
// because of containerPortInitializer below
if (!this.running.get()) {
this.context.publishEvent(
new InstancePreRegisteredEvent(this, getRegistration()));
register();
if (shouldRegisterManagement()) {
registerManagement();
}
this.context.publishEvent(
new InstanceRegisteredEvent<>(this, getConfiguration()));
this.running.compareAndSet(false, true);
} } public void stop() {
if (this.getRunning().compareAndSet(true, false) && isEnabled()) {
deregister();
if (shouldRegisterManagement()) {
deregisterManagement();
}
this.serviceRegistry.close();
}
} }

该类继承了一个springboot的监听ApplicationListener器用来监听WebServerInitializedEvent该事件。

当该事件完成后将调用onApplicationEvent()方法,并调用bind(WebServerInitializedEvent)方法。

在bind(WebServerInitializedEvent)方法中调用start()方法,并在start()方法中调用register()方法。

private final ServiceRegistry<R> serviceRegistry;

@Deprecated
protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry) {
this.serviceRegistry = serviceRegistry;
} protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry,
AutoServiceRegistrationProperties properties) {
this.serviceRegistry = serviceRegistry;
this.properties = properties;
} protected void register() {
this.serviceRegistry.register(getRegistration());
}

其中ServiceRegistry其实现类就是NacosServiceRegistry

8.WebServerInitializedEvent

该事件将在应用上下文完成刷新并准备好之后发布。主要用来获取正在运行的服务器本地端口。

public abstract class WebServerInitializedEvent extends ApplicationEvent {

	protected WebServerInitializedEvent(WebServer webServer) {
super(webServer);
} /**
* Access the {@link WebServer}.
* @return the embedded web server
获取嵌入式的web服务器(即SpringBoot中内置的tomcat)
*/
public WebServer getWebServer() {
return getSource();
} /**
* Access the application context that the server was created in. Sometimes it is
* prudent to check that this matches expectations (like being equal to the current
* context) before acting on the server itself.
* @return the applicationContext that the server was created from
获取服务创建时的上下文。有时会谨慎的检查配置是否复合预期(如 正在作用的是否是当前上下文)
*/
public abstract WebServerApplicationContext getApplicationContext(); /**
* Access the source of the event (an {@link WebServer}).
* @return the embedded web server
访问事件源
*/
@Override
public WebServer getSource() {
return (WebServer) super.getSource();
} }

可以看到该类主要是获取Springboot内置的tomcat端口。当使用外置tomcat时,该事件并不能获取到对应的服务信息。所以也无法在nacos中注册成功。

9. 如何在WAR包部署下成功注册nacos

实现ApplicationRunner接口,在应用启动成功后完成某些初始化工作。


@Component
public class NacosConfig implements ApplicationRunner { @Autowired(required = false)
private NacosAutoServiceRegistration registration; @Value("${baseConfig.nacos.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;
}
}

实现一个监听

@Component
public class NacosListener implements ApplicationListener<ApplicationReadyEvent> { @Autowired
private NacosRegistration registration; @Autowired
private NacosAutoServiceRegistration nacosAutoServiceRegistration; @Override
public void onApplicationEvent(ApplicationReadyEvent event) {
String property = event.getApplicationContext().getEnvironment().getProperty("server.port");
registration.setPort(Integer.valueOf(property));
nacosAutoServiceRegistration.start();
}
}
@EventListener(ApplicationReadyEvent.class)
public void onWebServerReady(ApplicationReadyEvent event) {
String property = event.getApplicationContext().getEnvironment().getProperty("server.port");
registration.setPort(Integer.valueOf(property));
nacosAutoServiceRegistration.start();
}

参考地址:

实现ApplicationRunner接口:https://my.oschina.net/yuhuashang/blog/3086167

源码:127.0.0.1

springboot事件: https://docs.spring.io/spring-boot/docs/2.1.11.BUILD-SNAPSHOT/reference/html/boot-features-spring-application.html#boot-features-application-events-and-listeners

@EnableDiscoveryClient 注解如何实现服务注册与发现:https://blog.csdn.net/z694644032/article/details/96706593

Springboot War包部署下nacos无法注册问题的更多相关文章

  1. SpringBoot war包部署到Tomcat服务器

    (1)pom.xml文件修改<packaging>war</packaging>,默认是jar包,<build>节点中增加<finalName>spri ...

  2. spring-boot war包部署(二)

    环境 jdk 8 tomcat 8.5 sts 4.4.2 maven 3.6.1 背景 有时候,服务器已经有了,我们必须要使用 war 包进行部署,所以需要 spring boot 支持打包和部署成 ...

  3. SpringBoot之打成war包部署到Tomcat

    正常情况下SpringBoot项目是以jar包的形式,正常情况下SpringBoot项目是以jar包的形式,并且SpringBoot是内嵌Tomcat服务器,所以每次重新启动都是用的新的Tomcat服 ...

  4. SpringBoot项目war包部署

    服务部署 记录原因 将本地SpringBoot项目通过war包部署到虚拟机中,验证服务器部署. 使用war包是为了方便替换配置文件等. 工具 对象 版本 Spring Boot 2.4.0 VMwar ...

  5. springboot 学习之路 5(打成war包部署tomcat)

    目录:[持续更新.....] spring 部分常用注解 spring boot 学习之路1(简单入门) spring boot 学习之路2(注解介绍) spring boot 学习之路3( 集成my ...

  6. Windows下war包部署到Linux下Tomcat出现的问题

    最近,将Windows下开发的war包部署到Linux下的Tomcat时报了一个错误:tomcat error in opening zip file.按理说,如果正常,当把war包复制到webapp ...

  7. 【Spring Boot项目】Win7+JDK8+Tomcat8环境下的War包部署

    一.pom.xml及启动类修改 pom.xml Step1:指定打包类型 <!-- 打包类型 jar 或 war --> <packaging>war</packagin ...

  8. springboot项目war包部署及出现的问题Failed to bind properties under 'mybatis.configuration.mapped-statements[0].

    1.修改pom文件 修改打包方式 为war: 添加tomcat使用范围,provided的意思即在发布的时候有外部提供,内置的tomcat就不会打包进去 <groupId>com.scho ...

  9. Web项目打成war包部署Tomcat时运行startup.bat直接闪退部署失败解决方案

    即上篇通过将web项目打成war包部署到Tomcat服务器,解决mysql问题后,又出现了新问题,真是一波三折,所以将解决过程分享给大家,希望能帮助到小伙伴们~ 将打好的war包拷贝到Tomcat的w ...

随机推荐

  1. Postman初探

    缘起 今天要测试一个新接口,返回值应该是现有6个接口返回值中data.CountNum之和.麻烦处有: 1.用户角色不同,接口返回值也有不同.因此要用到的接口很多. 2.要对所有接口的返回值求和,再与 ...

  2. 实验查看PHP本地的Session信息

    通过Nginx调度器负载后端两台Web服务器,实现以下目标: - 部署Nginx为前台调度服务器 - 调度算法设置为轮询 - 后端为两台LNMP服务器 - 部署测试页面,查看PHP本地的Session ...

  3. split 分割文件

    1.命令功能 split将文件分割成多个碎片文件. 2.语法格式 split  option  input  prefix split  选项    输入文件名   输出文件名前缀 参数说明 参数 参 ...

  4. [Luogu2015]二叉苹果树(树形dp)

    [Luogu2015] 二叉苹果树 题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. ...

  5. vue的class和style的绑定

    <div class="input-search" :class="{input-search-focus : iscur == 1}"> 在原本有 ...

  6. js实时计算价格

    //通过数量,单价的输入,实时显示总价 $("#number,#price").on("input",function(e){ $("#totalPr ...

  7. luogu P3411 序列变换

    链接 P3411 序列变换 如果要最小化答案,那么就最大化不移动的数. 那么不移动的子序列一定是最后答案的一段连续区间,而移动的数我们是一定有办法把他们还原的. 设\(f_{i}\)表示\(i\)点的 ...

  8. css 响应式(媒介查询)

    1.CSS 来实现响应式 CSS实现响应式网站的布局要用到的就是CSS中的媒体查询接下来来简单介绍一下: @media 类型 and (条件1) and (条件二){css样式} <link r ...

  9. django之数据模型类的字段分析

    一:表一的字段分析 class Sheep_Area(models.Model):# models.AutoField()自增列,要显示自定义的自增列,必须定义primary=True# area_i ...

  10. B2C自营商城的订单设计方案

    B2C自营商城的订单设计方案 2018年06月01日 17:19:00 lkx94 阅读数 1640   去年我们的美妆社区APP,上线了自有商城.之后经过多次版本迭代,商城系统的模块已经基本健全,值 ...