spring-cloud-alibaba-dubbo-issues1805修复


这个issue就是我这前写了那两篇文章的那个issue

Dubbo重启服务提供者或先启动服务消费者后启动服务提供者,消费者有时候会出现找不到服务的问题及解决

https://mp.weixin.qq.com/s/L-ejXtV9wk-hPYAc2e3jvQ

Duboo项目集成升级问题解决

https://mp.weixin.qq.com/s/-9t0tqnVX-A2fz4SNiPFdA


1.官方信息

Spring Cloud Dubbo组件去留问题讨论 #2398

https://github.com/alibaba/spring-cloud-alibaba/issues/2398

官方issue1805

https://github.com/alibaba/spring-cloud-alibaba/issues/1805

官方issue1805提交

https://github.com/alibaba/spring-cloud-alibaba/commit/fd8a6a6f6c262cbf8d20c049c84cd445651bd969


2.版本代码对比


Spring-Cloud-Start-dubbo-2.2.6-RELEASE的服务注册自动配置类如下:

/* * Copyright 2013-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
package com.alibaba.cloud.dubbo.autoconfigure;
import java.util.Collection;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.cloud.dubbo.autoconfigure.condition.MissingSpringCloudRegistryConfigPropertyCondition;import com.alibaba.cloud.dubbo.bootstrap.DubboBootstrapStartCommandLineRunner;import com.alibaba.cloud.dubbo.bootstrap.DubboBootstrapWrapper;import com.alibaba.cloud.dubbo.bootstrap.event.DubboBootstrapStartedEvent;import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;import com.alibaba.cloud.dubbo.registry.DubboServiceRegistrationEventPublishingAspect;import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreDeregisteredEvent;import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreRegisteredEvent;import com.ecwid.consul.v1.agent.model.NewService;import com.netflix.appinfo.InstanceInfo;import org.apache.dubbo.config.RegistryConfig;import org.apache.dubbo.config.bootstrap.DubboBootstrap;import org.apache.dubbo.config.spring.ServiceBean;import org.slf4j.Logger;import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;import org.springframework.beans.factory.ObjectProvider;import org.springframework.beans.factory.SmartInitializingSingleton;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.AutoConfigureAfter;import org.springframework.boot.autoconfigure.AutoConfigureOrder;import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.serviceregistry.Registration;import org.springframework.cloud.client.serviceregistry.ServiceRegistry;import org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties;import org.springframework.cloud.consul.serviceregistry.ConsulRegistration;import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration;import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration;import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaServiceRegistry;import org.springframework.context.SmartLifecycle;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Conditional;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;import org.springframework.context.event.EventListener;import org.springframework.util.StringUtils;
import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME;import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME;import static com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory.ADDRESS;import static com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory.PROTOCOL;import static org.springframework.util.ObjectUtils.isEmpty;
/** * Dubbo Service Registration Auto-{@link Configuration}. * * @author Mercy * @author theonefx */@Configuration(proxyBeanMethods = false)@Import({ DubboServiceRegistrationEventPublishingAspect.class, DubboBootstrapStartCommandLineRunner.class })@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)@AutoConfigureAfter(name = { EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME, CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME, "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration" }, value = { DubboMetadataAutoConfiguration.class })public class DubboServiceRegistrationAutoConfiguration {
/** * EurekaClientAutoConfiguration. */ public static final String EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration";
/** * ConsulAutoServiceRegistrationAutoConfiguration. */ public static final String CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.consul.serviceregistry.ConsulAutoServiceRegistrationAutoConfiguration";
/** * ConsulAutoRegistration. */ public static final String CONSUL_AUTO_SERVICE_AUTO_REGISTRATION_CLASS_NAME = "org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration";
/** * ZookeeperAutoServiceRegistrationAutoConfiguration. */ public static final String ZOOKEEPER_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.zookeeper.serviceregistry.ZookeeperAutoServiceRegistrationAutoConfiguration";
private static final Logger logger = LoggerFactory .getLogger(DubboServiceRegistrationAutoConfiguration.class);
@Autowired private DubboServiceMetadataRepository dubboServiceMetadataRepository;
@Bean @Conditional({ MissingSpringCloudRegistryConfigPropertyCondition.class }) public RegistryConfig defaultSpringCloudRegistryConfig() { return new RegistryConfig(ADDRESS, PROTOCOL); }
private Map, Set> registrations = new ConcurrentHashMap<>();
@EventListener(DubboBootstrapStartedEvent.class) public void onDubboBootstrapStarted(DubboBootstrapStartedEvent event) { if (!event.getSource().isReady()) { return; } registrations.forEach( (registry, registrations) -> registrations.forEach(registration -> { attachDubboMetadataServiceMetadata(registration); registry.register(registration); })); }
@EventListener(ServiceInstancePreRegisteredEvent.class) public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) { Registration registration = event.getSource(); if (!DubboBootstrap.getInstance().isReady() || !DubboBootstrap.getInstance().isStarted()) { ServiceRegistry registry = event.getRegistry(); synchronized (registry) { registrations.putIfAbsent(registry, new HashSet<>()); registrations.get(registry).add(registration); } } else { attachDubboMetadataServiceMetadata(registration); }
}
@EventListener(ServiceInstancePreDeregisteredEvent.class) public void onServiceInstancePreDeregistered( ServiceInstancePreDeregisteredEvent event) { ServiceRegistry registry = event.getRegistry(); registrations.remove(registry); }
private void attachDubboMetadataServiceMetadata(Registration registration) { if (registration == null) { return; } synchronized (registration) { Map metadata = registration.getMetadata(); attachDubboMetadataServiceMetadata(metadata); } }
private void attachDubboMetadataServiceMetadata(Map metadata) { Map serviceMetadata = dubboServiceMetadataRepository .getDubboMetadataServiceMetadata(); if (!isEmpty(serviceMetadata)) { metadata.putAll(serviceMetadata); } }
@Configuration(proxyBeanMethods = false) @ConditionalOnBean(name = EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME) class EurekaConfiguration implements SmartInitializingSingleton {
@Autowired private ObjectProvider> serviceBeans;
@EventListener(DubboBootstrapStartedEvent.class) public void onDubboBootstrapStarted(DubboBootstrapStartedEvent event) { DubboBootstrapWrapper wrapper = event.getSource(); if (!wrapper.isReady()) { return; } registrations.forEach( (registry, registrations) -> registrations.removeIf(registration -> { if (!(registration instanceof EurekaRegistration)) { return false; } EurekaRegistration eurekaRegistration = (EurekaRegistration) registration; InstanceInfo instanceInfo = eurekaRegistration .getApplicationInfoManager().getInfo();
EurekaInstanceConfigBean config = (EurekaInstanceConfigBean) eurekaRegistration .getInstanceConfig(); config.setInitialStatus(InstanceInfo.InstanceStatus.UP);
attachDubboMetadataServiceMetadata(instanceInfo.getMetadata()); eurekaRegistration.getApplicationInfoManager() .registerAppMetadata(instanceInfo.getMetadata()); eurekaRegistration.getApplicationInfoManager() .setInstanceStatus(InstanceInfo.InstanceStatus.UP); return true; })); }
@EventListener(ServiceInstancePreRegisteredEvent.class) public void onServiceInstancePreRegistered( ServiceInstancePreRegisteredEvent event) { Registration registration = event.getSource(); if (!(registration instanceof EurekaRegistration)) { return; }
if (DubboBootstrap.getInstance().isReady() && DubboBootstrap.getInstance().isStarted()) { EurekaRegistration eurekaRegistration = (EurekaRegistration) registration; InstanceInfo instanceInfo = eurekaRegistration.getApplicationInfoManager() .getInfo();
EurekaInstanceConfigBean config = (EurekaInstanceConfigBean) eurekaRegistration .getInstanceConfig(); config.setInitialStatus(InstanceInfo.InstanceStatus.UP);
attachDubboMetadataServiceMetadata(instanceInfo.getMetadata()); eurekaRegistration.getApplicationInfoManager() .registerAppMetadata(instanceInfo.getMetadata()); } else { EurekaRegistration eurekaRegistration = (EurekaRegistration) registration; EurekaInstanceConfigBean config = (EurekaInstanceConfigBean) eurekaRegistration .getInstanceConfig(); config.setInitialStatus(InstanceInfo.InstanceStatus.STARTING); } }
/** * {@link EurekaServiceRegistry} will register current {@link ServiceInstance * service instance} on {@link EurekaAutoServiceRegistration#start()} execution(in * {@link SmartLifecycle#start() start phase}), thus this method must * {@link ServiceBean#export() export} all {@link ServiceBean ServiceBeans} in * advance. */ @Override public void afterSingletonsInstantiated() { Collection serviceBeans = this.serviceBeans.getIfAvailable(); if (!isEmpty(serviceBeans)) { serviceBeans.forEach(ServiceBean::export); } }
}
@Configuration(proxyBeanMethods = false) @ConditionalOnBean(name = CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME) @AutoConfigureOrder class ConsulConfiguration {
@Autowired private ConsulDiscoveryProperties consulDiscoveryProperties;
@EventListener(DubboBootstrapStartedEvent.class) public void attachURLsIntoMetadataBeforeReRegist( DubboBootstrapStartedEvent event) { if (!event.getSource().isReady()) { return; } registrations.entrySet().removeIf(entry -> { Set registrations = entry.getValue(); registrations.removeIf(registration -> { Class registrationClass = AopUtils.getTargetClass(registration); String registrationClassName = registrationClass.getName(); return !CONSUL_AUTO_SERVICE_AUTO_REGISTRATION_CLASS_NAME .equalsIgnoreCase(registrationClassName); }); return registrations.isEmpty(); });
registrations.forEach( (registry, registrations) -> registrations.forEach(registration -> { ConsulRegistration consulRegistration = (ConsulRegistration) registration; attachURLsIntoMetadata(consulRegistration); })); }
@EventListener(ServiceInstancePreRegisteredEvent.class) public void onServiceInstancePreRegistered( ServiceInstancePreRegisteredEvent event) { Registration registration = event.getSource(); attachURLsIntoMetadata((ConsulRegistration) registration); }
private void attachURLsIntoMetadata(ConsulRegistration consulRegistration) { Map serviceMetadata = dubboServiceMetadataRepository .getDubboMetadataServiceMetadata(); if (isEmpty(serviceMetadata)) { return; } NewService newService = consulRegistration.getService(); if (consulDiscoveryProperties.isTagsAsMetadata()) { for (Map.Entry entry : serviceMetadata.entrySet()) { attAsTag(newService.getTags(), entry.getKey(), entry.getValue()); } } else { newService.getMeta().putAll(serviceMetadata); } }
private void attAsTag(List tags, String key, String value) { Iterator iter = tags.iterator(); while (iter.hasNext()) { String tag = iter.next(); String[] tmp = tag.split("="); if (StringUtils.pathEquals(tmp[0], key)) { iter.remove(); } } tags.add(key + "=" + value); }
}
}

DubboServiceRegistrationEventPublishingAspect类:

/* * Copyright 2013-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
package com.alibaba.cloud.dubbo.registry;
import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreDeregisteredEvent;import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreRegisteredEvent;import com.alibaba.cloud.dubbo.registry.event.ServiceInstanceRegisteredEvent;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;
import org.springframework.cloud.client.serviceregistry.Registration;import org.springframework.cloud.client.serviceregistry.ServiceRegistry;import org.springframework.context.ApplicationEventPublisher;import org.springframework.context.ApplicationEventPublisherAware;
/** * Dubbo Service Registration Event-Publishing Aspect. * * @author Mercy * @see ServiceInstancePreRegisteredEvent * @see ServiceInstanceRegisteredEvent * @see ServiceInstancePreDeregisteredEvent */@Aspectpublic class DubboServiceRegistrationEventPublishingAspect implements ApplicationEventPublisherAware {
/** * The pointcut expression for {@link ServiceRegistry#register(Registration)}. */ public static final String REGISTER_POINTCUT_EXPRESSION = "execution(* org.springframework.cloud.client.serviceregistry.ServiceRegistry.register(*)) && target(registry) && args(registration)";
/** * The pointcut expression for {@link ServiceRegistry#deregister(Registration)}. */ public static final String DEREGISTER_POINTCUT_EXPRESSION = "execution(* org.springframework.cloud.client.serviceregistry.ServiceRegistry.deregister(*)) && target(registry) && args(registration)";
private ApplicationEventPublisher applicationEventPublisher;
@Before(value = REGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void beforeRegister(ServiceRegistry registry, Registration registration) { applicationEventPublisher.publishEvent( new ServiceInstancePreRegisteredEvent(registry, registration)); }
@Before(value = DEREGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void beforeDeregister(ServiceRegistry registry, Registration registration) { applicationEventPublisher.publishEvent( new ServiceInstancePreDeregisteredEvent(registry, registration)); }
@After(value = REGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void afterRegister(ServiceRegistry registry, Registration registration) { applicationEventPublisher .publishEvent(new ServiceInstanceRegisteredEvent(registration)); }
@Override public void setApplicationEventPublisher( ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; }
}

spring-cloud-alibaba-2022.0.0.0

https://github.com/alibaba/spring-cloud-alibaba/tree/fd8a6a6f6c262cbf8d20c049c84cd445651bd969

Spring-Cloud-Start-dubbo-2.2.7-RELEASE的服务注册自动配置类如下:

ApplicationContextEvent是一个抽象类:在容器启动、刷新、关闭和停止的时候都会发送这个事件。

/* * Copyright 2013-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
package com.alibaba.cloud.dubbo.autoconfigure;
import java.util.Collection;import java.util.HashSet;import java.util.List;import java.util.Map;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.cloud.dubbo.autoconfigure.condition.MissingSpringCloudRegistryConfigPropertyCondition;import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;import com.alibaba.cloud.dubbo.registry.DubboServiceRegistrationEventPublishingAspect;import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreDeregisteredEvent;import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreRegisteredEvent;import com.ecwid.consul.v1.agent.model.NewService;import com.netflix.appinfo.InstanceInfo;import org.apache.dubbo.config.RegistryConfig;import org.apache.dubbo.config.spring.ServiceBean;import org.slf4j.Logger;import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;import org.springframework.beans.factory.ObjectProvider;import org.springframework.beans.factory.SmartInitializingSingleton;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.AutoConfigureAfter;import org.springframework.boot.autoconfigure.AutoConfigureOrder;import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.serviceregistry.Registration;import org.springframework.cloud.client.serviceregistry.ServiceRegistry;import org.springframework.cloud.consul.serviceregistry.ConsulRegistration;import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration;import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration;import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaServiceRegistry;import org.springframework.context.SmartLifecycle;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Conditional;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;import org.springframework.context.event.ApplicationContextEvent;import org.springframework.context.event.EventListener;import org.springframework.core.Ordered;import org.springframework.core.annotation.Order;
import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME;import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME;import static com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory.ADDRESS;import static com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory.PROTOCOL;import static org.springframework.util.ObjectUtils.isEmpty;
/** * Dubbo Service Registration Auto-{@link Configuration}. * * @author Mercy */@Configuration(proxyBeanMethods = false)@Import({ DubboServiceRegistrationEventPublishingAspect.class })@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)@AutoConfigureAfter(name = { EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME, CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME, "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration" }, value = { DubboMetadataAutoConfiguration.class })public class DubboServiceRegistrationAutoConfiguration {
/** * EurekaClientAutoConfiguration. */ public static final String EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration";
/** * ConsulAutoServiceRegistrationAutoConfiguration. */ public static final String CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.consul.serviceregistry.ConsulAutoServiceRegistrationAutoConfiguration";
/** * ConsulAutoRegistration. */ public static final String CONSUL_AUTO_SERVICE_AUTO_REGISTRATION_CLASS_NAME = "org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration";
/** * ZookeeperAutoServiceRegistrationAutoConfiguration. */ public static final String ZOOKEEPER_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.zookeeper.serviceregistry.ZookeeperAutoServiceRegistrationAutoConfiguration";
private static final Logger logger = LoggerFactory .getLogger(DubboServiceRegistrationAutoConfiguration.class);
@Autowired private DubboServiceMetadataRepository dubboServiceMetadataRepository;
@Bean @Conditional({ MissingSpringCloudRegistryConfigPropertyCondition.class }) public RegistryConfig defaultSpringCloudRegistryConfig() { return new RegistryConfig(ADDRESS, PROTOCOL); }
private Map, Set> registrations = new ConcurrentHashMap<>();
// 监听ApplicationContextEvent这个事件重新注册dubbo的服务 @Order @EventListener(ApplicationContextEvent.class) public void attachDubboMetadataAndRegistAgain(ApplicationContextEvent event) { registrations.forEach( (registry, registrations) -> registrations.forEach(registration -> { attachDubboMetadataServiceMetadata(registration); registry.register(registration); })); } //dubbo的aspect切拦截nacos服务注册前会发送这个事件,监听到该事件后需要将注册到nacos的服务加入到registrations @EventListener(ServiceInstancePreRegisteredEvent.class) public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) { Registration registration = event.getSource(); ServiceRegistry registry = event.getRegistry(); // 这里加了一个jvm的锁 synchronized (registry) { registrations.putIfAbsent(registry, new HashSet<>()); registrations.get(registry).add(registration); } } //dubbo的aspect切面拦截服务取消注册前会发送这个时间,监听到该事件后需要将服务注册信息从registrations中移除 @EventListener(ServiceInstancePreDeregisteredEvent.class) public void onServiceInstancePreDeregistered( ServiceInstancePreDeregisteredEvent event) { ServiceRegistry registry = event.getRegistry(); registrations.remove(registry); }
private void attachDubboMetadataServiceMetadata(Registration registration) { if (registration == null) { return; } synchronized (registration) { Map metadata = registration.getMetadata(); attachDubboMetadataServiceMetadata(metadata); } } //这里是dubbo服务元数据,会加入到一个map集合中,然后会走服务变更,通知服务订阅者重新拉取最新的注册的服务信息(猜测)。 private void attachDubboMetadataServiceMetadata(Map metadata) { Map serviceMetadata = dubboServiceMetadataRepository .getDubboMetadataServiceMetadata(); if (!isEmpty(serviceMetadata)) { metadata.putAll(serviceMetadata); } }
@Configuration(proxyBeanMethods = false) @ConditionalOnBean(name = EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME) class EurekaConfiguration implements SmartInitializingSingleton {
@Autowired private ObjectProvider> serviceBeans;
@Order(Ordered.LOWEST_PRECEDENCE - 1) @EventListener(ApplicationContextEvent.class) public void onServiceInstancePreRegistered(ApplicationContextEvent event) { registrations.forEach((registry, registrations)-> registrations.forEach(registration -> { EurekaRegistration eurekaRegistration = (EurekaRegistration) registration; InstanceInfo instanceInfo = eurekaRegistration.getApplicationInfoManager().getInfo(); attachDubboMetadataServiceMetadata(instanceInfo.getMetadata()); })); }
/** * {@link EurekaServiceRegistry} will register current {@link ServiceInstance * service instance} on {@link EurekaAutoServiceRegistration#start()} execution(in * {@link SmartLifecycle#start() start phase}), thus this method must * {@link ServiceBean#export() export} all {@link ServiceBean ServiceBeans} in * advance. */ @Override public void afterSingletonsInstantiated() { Collection serviceBeans = this.serviceBeans.getIfAvailable(); if (!isEmpty(serviceBeans)) { serviceBeans.forEach(ServiceBean::export); } }
}
@Configuration(proxyBeanMethods = false) @ConditionalOnBean(name = CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME) @AutoConfigureOrder class ConsulConfiguration {
@Order(Ordered.LOWEST_PRECEDENCE - 1) @EventListener(ApplicationContextEvent.class) public void attachURLsIntoMetadataBeforeReRegist(ApplicationContextEvent event) { registrations.entrySet().removeIf(entry -> { Set registrations = entry.getValue(); registrations.removeIf(registration -> { Class registrationClass = AopUtils.getTargetClass(registration); String registrationClassName = registrationClass.getName(); return !CONSUL_AUTO_SERVICE_AUTO_REGISTRATION_CLASS_NAME .equalsIgnoreCase(registrationClassName); }); return registrations.isEmpty(); });
registrations.forEach( (registry, registrations) -> registrations.forEach(registration -> { ConsulRegistration consulRegistration = (ConsulRegistration) registration; attachURLsIntoMetadata(consulRegistration); })); }
private void attachURLsIntoMetadata(ConsulRegistration consulRegistration) { NewService newService = consulRegistration.getService(); Map serviceMetadata = dubboServiceMetadataRepository .getDubboMetadataServiceMetadata(); if (!isEmpty(serviceMetadata)) { List tags = newService.getTags(); for (Map.Entry entry : serviceMetadata.entrySet()) { tags.add(entry.getKey() + "=" + entry.getValue()); } } }
}
}

说明:

将2022.0.0.0的start的服务注册自动装配类扣出来,放到项目中,然后注释EurekaConfiguration和ConsulConfiguration的自动装配即可,如果要用可以把先关的包引入进来,这个版本的start和2.2.7版本的这个start相差不大,可以替换使用

之前通过升级解决这个问题,估计是巧合吧Duboo项目集成升级问题解决,百思不得其解,然后我就去git的官网上看了下找到了这个issue,然后官方是在spring-cloud-alibaba-2022.0.0.0这个版本才修复有提交代码记录的,由于这个版本所需要的版本如下:

我们项目中的springBoot的版本是2.3.12.RELEASE,所以都不想升级版本依赖,所以想了这个办法,直接将这个类抠出来用下就可以了。

3.修改尝试

package com.alibaba.cloud.dubbo.autoconfigure;
/* * Copyright 2013-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
import com.alibaba.cloud.dubbo.autoconfigure.condition.MissingSpringCloudRegistryConfigPropertyCondition;import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;import com.alibaba.cloud.dubbo.registry.DubboServiceRegistrationEventPublishingAspect;import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreDeregisteredEvent;import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreRegisteredEvent;import org.apache.dubbo.config.RegistryConfig;import org.apache.dubbo.config.spring.ServiceBean;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.AutoConfigureAfter;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.serviceregistry.Registration;import org.springframework.cloud.client.serviceregistry.ServiceRegistry;import org.springframework.context.SmartLifecycle;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Conditional;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;import org.springframework.context.event.ApplicationContextEvent;import org.springframework.context.event.EventListener;import org.springframework.core.annotation.Order;
import java.util.HashSet;import java.util.Map;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;
import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME;import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME;import static com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory.ADDRESS;import static com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory.PROTOCOL;import static org.springframework.util.ObjectUtils.isEmpty;
/** * Dubbo Service Registration Auto-{@link Configuration}. * * @author Mercy */@Configuration(proxyBeanMethods = false)@Import({DubboServiceRegistrationEventPublishingAspect.class})@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)@AutoConfigureAfter(name = {EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME, CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME, "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration"}, value = {DubboMetadataAutoConfiguration.class})public class DubboServiceRegistrationAutoConfiguration {
/** * EurekaClientAutoConfiguration. */ public static final String EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration";
/** * ConsulAutoServiceRegistrationAutoConfiguration. */ public static final String CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.consul.serviceregistry.ConsulAutoServiceRegistrationAutoConfiguration";
/** * ConsulAutoRegistration. */ public static final String CONSUL_AUTO_SERVICE_AUTO_REGISTRATION_CLASS_NAME = "org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration";
/** * ZookeeperAutoServiceRegistrationAutoConfiguration. */ public static final String ZOOKEEPER_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.zookeeper.serviceregistry.ZookeeperAutoServiceRegistrationAutoConfiguration";
private static final Logger logger = LoggerFactory.getLogger(DubboServiceRegistrationAutoConfiguration.class);
@Autowired private DubboServiceMetadataRepository dubboServiceMetadataRepository;
@Bean @Conditional({MissingSpringCloudRegistryConfigPropertyCondition.class}) public RegistryConfig defaultSpringCloudRegistryConfig() { return new RegistryConfig(ADDRESS, PROTOCOL); }
private Map<ServiceRegistry<Registration>, Set<Registration>> registrations = new ConcurrentHashMap<>();
@Order @EventListener(ApplicationContextEvent.class) public void attachDubboMetadataAndRegistAgain(ApplicationContextEvent event) { registrations.forEach((registry, registrations) -> registrations.forEach(registration -> { attachDubboMetadataServiceMetadata(registration); registry.register(registration); })); }
@EventListener(ServiceInstancePreRegisteredEvent.class) public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) { Registration registration = event.getSource(); ServiceRegistry<Registration> registry = event.getRegistry(); synchronized (registry) { registrations.putIfAbsent(registry, new HashSet<>()); registrations.get(registry).add(registration); } }
@EventListener(ServiceInstancePreDeregisteredEvent.class) public void onServiceInstancePreDeregistered(ServiceInstancePreDeregisteredEvent event) { ServiceRegistry<Registration> registry = event.getRegistry(); registrations.remove(registry); }
private void attachDubboMetadataServiceMetadata(Registration registration) { if (registration == null) { return; } synchronized (registration) { Map<String, String> metadata = registration.getMetadata(); attachDubboMetadataServiceMetadata(metadata); } }
private void attachDubboMetadataServiceMetadata(Map<String, String> metadata) { Map<String, String> serviceMetadata = dubboServiceMetadataRepository.getDubboMetadataServiceMetadata(); if (!isEmpty(serviceMetadata)) { metadata.putAll(serviceMetadata); } }
/*@Configuration(proxyBeanMethods = false) @ConditionalOnBean(name = EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME) class EurekaConfiguration implements SmartInitializingSingleton {
@Autowired private ObjectProvider> serviceBeans;
@Order(Ordered.LOWEST_PRECEDENCE - 1) @EventListener(ApplicationContextEvent.class) public void onServiceInstancePreRegistered(ApplicationContextEvent event) { registrations.forEach((registry, registrations) -> registrations.forEach(registration -> { EurekaRegistration eurekaRegistration = (EurekaRegistration) registration; InstanceInfo instanceInfo = eurekaRegistration.getApplicationInfoManager().getInfo(); attachDubboMetadataServiceMetadata(instanceInfo.getMetadata()); })); }
*//** * {@link EurekaServiceRegistry} will register current {@link ServiceInstance * service instance} on {@link EurekaAutoServiceRegistration#start()} execution(in * {@link SmartLifecycle#start() start phase}), thus this method must * {@link ServiceBean#export() export} all {@link ServiceBean ServiceBeans} in * advance. *//* @Override public void afterSingletonsInstantiated() { Collection serviceBeans = this.serviceBeans.getIfAvailable(); if (!isEmpty(serviceBeans)) { serviceBeans.forEach(ServiceBean::export); } }
}*/
/*@Configuration(proxyBeanMethods = false) @ConditionalOnBean(name = CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME) @AutoConfigureOrder class ConsulConfiguration {
@Order(Ordered.LOWEST_PRECEDENCE - 1) @EventListener(ApplicationContextEvent.class) public void attachURLsIntoMetadataBeforeReRegist(ApplicationContextEvent event) { registrations.entrySet().removeIf(entry -> { Set registrations = entry.getValue(); registrations.removeIf(registration -> { Class registrationClass = AopUtils.getTargetClass(registration); String registrationClassName = registrationClass.getName(); return !CONSUL_AUTO_SERVICE_AUTO_REGISTRATION_CLASS_NAME .equalsIgnoreCase(registrationClassName); }); return registrations.isEmpty(); });
registrations.forEach( (registry, registrations) -> registrations.forEach(registration -> { ConsulRegistration consulRegistration = (ConsulRegistration) registration; attachURLsIntoMetadata(consulRegistration); })); }
private void attachURLsIntoMetadata(ConsulRegistration consulRegistration) { NewService newService = consulRegistration.getService(); Map serviceMetadata = dubboServiceMetadataRepository .getDubboMetadataServiceMetadata(); if (!isEmpty(serviceMetadata)) { List tags = newService.getTags(); for (Map.Entry entry : serviceMetadata.entrySet()) { tags.add(entry.getKey() + "=" + entry.getValue()); } } }
}*/
}

之前那两篇文章大致的思路上大致方向是对的,Spring-Cloud-Start-dubbo-2.2.6-RELEASE和Spring-Cloud-Start-dubbo-2.2.7-RELEASE的springBoot的基本流程是一样的,nacos的注册是在在servlet容器启动后进行,dubbo的BootStrap.start()触发是在容器刷新后触发,由于2.3.x的springBoot的版本的容器刷新事件是在nacos的servlet容器启动之后(之前的思路是将dubbo.start()触发提前到nacos注册服务之前,这种是行不通的,因为Spring-Cloud-Start-dubbo的服务注册的start的流程上就有问题,提前这个顺序只会搞出更多的问题),所以就有有dubbo服务还没有注册完,所以就没有触发更新和通知操作

spring-context-5.2.15.RELEASE

@Override  public void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {      // Prepare this context for refreshing.      prepareRefresh();
// Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory);
try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory);
// Initialize message source for this context. initMessageSource();
// Initialize event multicaster for this context. initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses. //这里是servelet容器启动后会触发通知nacos注册 onRefresh();
// Check for listener beans and register them. registerListeners();
// Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event. //这里是触发dubbo启动; finishRefresh(); }
catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); }
// Destroy already created singletons to avoid dangling resources. destroyBeans();
// Reset 'active' flag. cancelRefresh(ex);
// Propagate exception to caller. throw ex; }
finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }

finishRefresh();

protected void finishRefresh() {    // Clear context-level resource caches (such as ASM metadata from scanning).    clearResourceCaches();
// Initialize lifecycle processor for this context. initLifecycleProcessor();
// Propagate refresh to lifecycle processor first. getLifecycleProcessor().onRefresh(); // 容器刷新时间,触发dubbStrap.start(); // Publish the final event. publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active. LiveBeansView.registerApplicationContext(this); }

start中的服务元数据注册管理:

private void attachDubboMetadataServiceMetadata(Map<String, String> metadata) {        Map<String, String> serviceMetadata = dubboServiceMetadataRepository.getDubboMetadataServiceMetadata();        if (!isEmpty(serviceMetadata)) {            metadata.putAll(serviceMetadata);        }    }

getDubboMetadataServiceMetadata方法

public Map<String, String> getDubboMetadataServiceMetadata() {
List dubboMetadataServiceURLs = dubboMetadataServiceExporter.export();
// remove the exported URLs of DubboMetadataService removeDubboMetadataServiceURLs(dubboMetadataServiceURLs);
Map<String, String> metadata = newHashMap();
addDubboMetadataServiceURLsMetadata(metadata, dubboMetadataServiceURLs); addDubboProtocolsPortMetadata(metadata); addRevision(metadata);
return Collections.unmodifiableMap(metadata); }

addDubboProtocolsPortMetadata方法

private void addDubboProtocolsPortMetadata(Map<String, String> metadata) {
allExportedURLs.values().stream().flatMap(v -> v.stream()).forEach(url -> { String protocol = url.getProtocol(); String propertyName = getDubboProtocolPropertyName(protocol); String propertyValue = valueOf(url.getPort()); metadata.put(propertyName, propertyValue); }); }

通过去源码里面找allExportedURLs的逻辑,大致猜测可以知道,当服务注册、变更(刷新)或失败等会有一些触发服务变更通知订阅者的逻辑,这样在只要是服务有变动都可以感知到,然后nacos通知到订阅者,你订阅的服务有变动了,你需要拉取下最新的服务然后更新下本地的服务列表,这个就是我大致的逻辑猜测。

private final MultiValueMap<String, URL> allExportedURLs = new LinkedMultiValueMap<>();


4.验证

在公司测试环境依赖使用的是之前升级后的依赖,然后使用上面那个start部署测试环境后,服务提供者部署重启,服务消费者不用重启,然后访问应用,服务消费者可以正常的访问到服务提供者的服务。


5.总结

希望我的分享对你有帮助,有兴趣的可以去看下dubbo的相关源码和具体的逻辑是怎样的,上面的是我的一些猜测(合理的猜想 + 验证),感觉是这么一个感觉,请一键三连,么么么哒!


请使用浏览器的分享功能分享到微信等