如果需要解決方法,請直接看 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
@EnableDiscoveryClient 注解如何實現服務注冊與發現:https://blog.csdn.net/z694644032/article/details/96706593