spring-cloud-alibaba-dubbo-issues1805修复

spring-cloud-alibaba-dubbo-issues1805修复

文章目录

    • @[toc]
  • 1.官方信息
  • 2.版本代码对比
  • 3.修改尝试
  • 4.验证
  • 5.总结

这个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-alibaba-dubbo-issues1805修复_第1张图片

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

spring-cloud-alibaba-dubbo-issues1805修复_第2张图片

/*
 * 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<ServiceRegistry<Registration>, Set<Registration>> 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<Registration> 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<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<Collection<ServiceBean>> 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<ServiceBean> 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<Registration> 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<String, String> serviceMetadata = dubboServiceMetadataRepository
					.getDubboMetadataServiceMetadata();
			if (isEmpty(serviceMetadata)) {
				return;
			}
			NewService newService = consulRegistration.getService();
			if (consulDiscoveryProperties.isTagsAsMetadata()) {
				for (Map.Entry<String, String> entry : serviceMetadata.entrySet()) {
					attAsTag(newService.getTags(), entry.getKey(), entry.getValue());
				}
			}
			else {
				newService.getMeta().putAll(serviceMetadata);
			}
		}

		private void attAsTag(List<String> tags, String key, String value) {
			Iterator<String> 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
 */
@Aspect
public 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是一个抽象类:在容器启动、刷新、关闭和停止的时候都会发送这个事件。

spring-cloud-alibaba-dubbo-issues1805修复_第3张图片

/*
 * 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<ServiceRegistry<Registration>, Set<Registration>> 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<Registration> 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<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);
		}
	}
    //这里是dubbo服务元数据,会加入到一个map集合中,然后会走服务变更,通知服务订阅者重新拉取最新的注册的服务信息(猜测)。
	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<Collection<ServiceBean>> 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<ServiceBean> 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<Registration> 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<String, String> serviceMetadata = dubboServiceMetadataRepository
					.getDubboMetadataServiceMetadata();
			if (!isEmpty(serviceMetadata)) {
				List<String> tags = newService.getTags();
				for (Map.Entry<String, String> entry : serviceMetadata.entrySet()) {
					tags.add(entry.getKey() + "=" + entry.getValue());
				}
			}
		}

	}

}

说明:

spring-cloud-alibaba-dubbo-issues1805修复_第4张图片

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

spring-cloud-alibaba-dubbo-issues1805修复_第5张图片

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

spring-cloud-alibaba-dubbo-issues1805修复_第6张图片

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

3.修改尝试

spring-cloud-alibaba-dubbo-issues1805修复_第7张图片

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<ServiceBean> 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<URL> 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 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的相关源码和具体的逻辑是怎样的,上面的是我的一些猜测(合理的猜想 + 验证),感觉是这么一个感觉,请一键三连,么么么哒!

你可能感兴趣的:(dubbo)