dubbo使用nacos为注册中心,先启动服务提供者,再启动消费者,调用服务提示找不到服务,改变启动顺序后就可以,怎么解决
当使用Dubbo框架并采用Nacos作为注册中心时,服务消费者需要通过服务名称进行远程调用。Dubbo会在Nacos中查找该服务提供者的地址,并通过网络传输协议建立远程连接。如果您确定服务提供者已经成功注册到Nacos,但消费者仍然找不到服务,您可以按照以下步骤进行检查:
确保服务提供者已经成功注册到Nacos。您可以在Nacos控制台查看服务列表或使用Nacos API查询服务。如果服务列表中没有您的服务提供者,可能是服务注册失败。
检查消费者端的Nacos配置。确保${nacos.server-address}中的变量名与实际定义的变量名相匹配。例如,如果您定义的变量名是nacos.server-addr,则配置文件中的引用应该是${nacos.server-addr}。
如果消费者和提供者的代码都已经写好,并且能够成功调用,但是在Nacos页面的服务列表中始终没有数据,这可能意味着配置文件中的地址引用错误或者消费者还没有成功注册到Nacos。
检查消费者的依赖配置。确保您已经添加了正确的Dubbo和Spring Boot Starter依赖。
如果以上步骤都无法解决问题,建议查看项目的日志文件,以获取更多关于服务调用失败的详细信息。
楼主你好,根据你出现服务找不到的情况,有可能是服务提供者还没有完成注册,消费者就开始访问了的原因造成的,你可以在Nacos中为服务提供者配置健康检查,这样在服务提供者注册完成之前,消费者是无法访问该服务的,但是可以在服务提供者配置文件中加入以下配置:
然后在Nacos控制台注册服务时,勾选“启用健康检查”选项。然后在服务提供者启动之前,消费者将无法访问该服务。
提供者的服务名称、接口名或分组名与消费者所使用的不一致。请确认消费者和服务提供者之间的协议和命名规则是否保持一致。
NACOS注册表中不存在相应的服务提供者。请确认服务提供者已经成功部署并已向NACOS注册表中发送了心跳信号。
网络问题:检查您的消费者和Nacos注册中心之间的网络连通性是否良好。如果可能的话,请确保Nacos注册中心的服务端口(默认为8848)在消费者上是开放的。
检查Nacos服务注册情况:
确保提供者已经成功注册到Nacos。
可以在Nacos的控制台界面查看服务列表,确认服务是否已经注册。
检查消费者配置:
确保消费者的application.properties或application.yml文件中正确配置了Nacos的地址和端口。
检查消费者的服务引用配置是否正确,包括服务接口名、版本号等。
网络问题:
确保消费者和提供者之间的网络连接是正常的。
检查防火墙或安全组设置,确保没有阻止Dubbo或Nacos的通信。
这是由于DUBBO的依赖注入机制导致的问题。
DUBBO通过观察Nacos注册表的变化,从注册表中获取可用的服务实例列表,并为消费者的Provider添加依赖关系。当启动顺序发生变化时,服务提供者的实例会在Consumer未完全初始化之前就被加载进Provider中,因此会导致无法正确解析服务实例,导致找不到服务的问题。
为了解决这个问题,您可以考虑以下几种方案:
这个问题通常是由于服务提供者和消费者之间的网络连接问题导致的。可以检查你的服务提供者和消费者是否可以正常启动,并且可以正常连接到 Nacos 注册中心。你可以检查你的日志文件,看是否有相关的错误信息。检查你的服务提供者和消费者是否配置了正确的 Nacos 注册中心的地址和端口,以及服务提供者和消费者的地址和端口。你可以检查你的配置文件,看是否有相关的错误配置。检查你的服务提供者和消费者是否配置了正确的网络连接配置,例如,是否开启了防火墙,是否限制了网络连接的IP地址等。你可以检查你的网络连接配置,看是否有相关的错误配置。检查你的服务提供者和消费者是否可以正常通信,你可以使用网络抓包工具,检查服务提供者和消费者之间的网络流量,看是否有相关的错误信息。检查你的服务提供者和消费者是否可以正常处理网络请求,你可以使用工具,如 jMeter,来测试服务提供者和消费者之间的网络请求,看是否有相关的错误信息。检查你的服务提供者和消费者是否可以正常处理异常,你可以检查你的日志文件,看是否有相关的错误信息。
检查注册中心是否已正确注册服务。在服务提供者启动后,需要等待一段时间(通常为1秒),以确保注册中心已正确注册服务。您可以使用Nacos提供的API来查看服务是否已正确注册。
检查服务的访问权限。确保服务提供者和消费者的访问权限相同,否则可能会导致无法找到服务。
检查服务名称是否正确。确保服务名称与消费者中注册的服务名称匹配。
检查服务的注册中心是否正确指定。确保服务提供者中正确指定了Nacos注册中心的地址。
消费者找不到服务可能有几个原因:
服务未注册:确保服务提供者已经成功将服务注册到Nacos。您可以在提供者的日志中检查注册是否成功。
注册中心配置错误:检查消费者和提供者对Nacos的配置是否正确。这包括Nacos服务器的地址、端口和其他相关配置。确保这些配置匹配提供者和消费者的网络环境。
订阅问题:在消费者端,您需要确保已经正确订阅了所需的服务。您可以在消费者的配置文件中检查订阅的组名和服务名是否正确。
这个问题可能是由于服务提供者和服务消费者之间的注册中心访问问题。在使用Dubbo和Nacos作为注册中心时,确保服务提供者能够正确地将服务注册到Nacos,并且服务消费者能够从Nacos中获取并订阅这些服务。
以下是一些可能的解决方法:
检查Nacos的配置:确认Nacos的配置正确,包括服务注册和发现的相关配置。确保Nacos服务器地址、端口和其他相关配置正确。
检查Dubbo和Nacos的版本兼容性:确认你使用的Dubbo和Nacos版本是相互兼容的。不同版本之间的兼容性问题可能会导致服务无法正常注册或发现。
检查网络连接:确认服务提供者和消费者之间的网络连接是正常的。如果网络存在问题,可能导致服务无法正常注册或发现。
检查服务提供者的注册问题:在启动服务提供者时,确保服务已经正确地注册到Nacos。可以查看Dubbo和Nacos的日志,确认服务是否成功注册。
检查服务消费者的订阅问题:在启动服务消费者时,确保服务已经正确地从Nacos中获取并订阅。可以查看Dubbo和Nacos的日志,确认服务是否成功订阅。
改变启动顺序的问题:如果在先启动服务提供者再启动消费者时出现找不到服务的问题,可以尝试在启动时同时启动服务提供者和消费者,或者在消费者启动时增加一些延时,以便服务提供者有时间将服务注册到Nacos。
更新Dubbo和Nacos的版本:如果你使用的是较旧的版本,可以尝试更新到最新版本,以获得更好的兼容性和性能。
如果以上方法都不能解决问题,建议查阅Dubbo和Nacos的官方文档或者向社区寻求更具体的帮助。
确保服务提供者在启动时已经注册到 Nacos。您可以检查服务提供者的日志,查找类似以下关键字的日志信息:
registerCopyCopy
这表示服务提供者正在注册到 Nacos。
@Service
@LoadBalanced
public class MyServiceImpl implements MyService {
// ...
}CopyCopy
当你使用Nacos作为注册中心时,服务提供者和消费者都需要先启动,才能正确地注册到Nacos服务器并完成服务发现的过程。这是因为Nacos在启动时会初始化注册表,而服务发现就是在这个基础上完成的。
因此,当你的服务提供者和消费者都在运行时,你需要确保它们都已正确地连接到了Nacos服务器,并且已成功注册了服务。你可以使用Nacos的命令行工具或Web UI来检查注册表,并确认服务提供者和消费者的状况。
如果服务提供者没有先启动,那么服务可能还没有完成注册就试图去查找服务,这时就会出现找不到服务的情况。反之亦然,如果消费者没有先启动,那么服务也可能会找不到消费者,进而导致调用失败。
当使用Dubbo和Nacos作为注册中心时,出现找不到服务的提示可能有几个原因。以下是一些可能的解决方法:
检查服务提供者是否已正确注册到Nacos:确保服务提供者已成功注册到Nacos注册中心,并且消费者可以访问到服务提供者的服务。您可以检查Nacos的监控面板或日志来确认服务提供者是否已注册。
检查Nacos服务的命名和分组:确保服务提供者和消费者使用的服务名称和分组是一致的。在Dubbo中,您需要在服务提供者和消费者端的配置文件中指定正确的服务名称和分组。
检查消费者的订阅设置:在Dubbo中,消费者需要订阅服务提供者所注册的服务。确保消费者的订阅设置正确,包括服务名称、分组、版本等信息。
确认网络通信是否正常:如果服务提供者和消费者在不同的网络环境或机器上运行,需要确认它们之间的网络通信是否正常。您可以尝试使用ping或telnet等工具来测试网络连接。
检查Dubbo和Nacos的版本兼容性:确保您使用的Dubbo和Nacos版本是相互兼容的。不同版本之间可能存在一些差异,需要确认它们是否能够正常工作。
如果您确认上述问题都已解决,但仍然无法找到服务,可以尝试以下方法:
清理Nacos的缓存:有时候Nacos缓存中可能存在过时或错误的服务信息,清理缓存后重新注册服务可能会解决问题。
重启Dubbo和Nacos服务:有时候重启Dubbo和Nacos服务可以清除一些临时的问题,让服务重新启动并重新注册。
检查日志和监控信息:查看Dubbo和Nacos的日志和监控信息,可能会发现更详细的错误提示或异常信息,帮助您定位问题所在。
如果以上方法仍然无法解决问题,建议您检查相关的配置和代码,或者向Dubbo或Nacos的技术支持团队寻求帮助。
这个问题可能是由于Nacos注册中心和Dubbo服务提供者的启动速度不同步引起的。当服务提供者启动时,它会立即尝试向Nacos注册自己,而此时Nacos可能还没有完全准备好接受注册请求。如果服务提供者不能成功注册到Nacos,则消费者将无法发现它。
为了解决这个问题,你可以调整服务提供者的启动策略,使其在启动时等待一段时间,直到Nacos准备好接收注册请求为止。你可以通过以下两种方式之一来实现这一点:
META-INF/spring/dubbo/service_provider.xml
或META-INF/spring/dubbo/consumer.xml
),添加以下内容:<dubbo:registry address="nacos://127.0.0.1:8848?client=nacos-discovery-client&server-bean-name=nacos-discovery-server" timeout="10000"/>
这里的关键是添加了一个新的参数timeout
,它的单位是毫秒,表示服务提供者在启动时将等待多长时间,直到它能够成功连接到Nacos注册中心。
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("nacos://127.0.0.1:8848");
registryConfig.setTimeout(10000);
// ...
ApplicationModel.getEnvironment().getRegistries().add(registryConfig);
这里的setTimeout
方法的作用与上面XML配置中的timeout
参数相同。
修改源码DubboServiceRegistrationAutoConfiguration是dubbo的子动装配类。
package com.alibaba.cloud.dubbo.autoconfigure;
.........
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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @author <a href="mailto:chenxilzx1@gmail.com">theonefx</a>
*/
@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);
}
}
......................................
}
DubboBootstrapStartCommandLineRunner在dubbo服务启动注册完成会发一个DubboBootstrapStartedEvent事件
@Component
public class DubboBootstrapStartCommandLineRunner
implements CommandLineRunner, ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
@Override
public void run(String... args) {
applicationEventPublisher.publishEvent(
new DubboBootstrapStartedEvent(DubboBootstrapWrapper.getInstance()));
}
}
该事件会被DubboServiceRegistrationAutoConfiguration监听到启动后触发dubbo服务注册
@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);
}));
}
ServiceInstancePreRegisteredEvent事件是服务实例预注册时间,它的触发是在 DubboServiceRegistrationEventPublishingAspect切面类里面,
DubboServiceRegistrationEventPublishingAspect该切面会拦截上面的NacosServiceRegistry#register方法执行前做一些处理。
@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框架启动nacos自动服务注册的时点比启动dubbo服务注册的时点早。前者的启动时点是监听到WebServerInitializedEvent
事件时(org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration#bind(WebServerInitializedEvent event)
),后者的启动时点是监听到ContextRefreshedEvent
事件时(org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener#onContextRefreshedEvent(ContextRefreshedEvent event)
)。
在spring boot 2.2.x
中ServletWebServerInitializedEvent
事件的发布是在ContextRefreshedEvent
事件之后,如图:
但在 spring boot 2.3.x
中改在了ContextRefreshedEvent
事件前,如图:
nacos服务端在处理了服务提供者的注册请求后向订阅者下发了实例变更通知,而在这个过程中提供者自身的dubbo服务暴露有可能还没有完成,最直接的表现就是服务提供者的 com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository
的 allExportedURLs
属性中还没有对应的dubbo服务的URL。
在第3条的问题重现里面,当程序跑到断点的时候,通过jprofiler
查看此时的堆栈信息,可以看到allExportedURLs
属性中没有期望的值。
因为spring cloud alibaba + dubbo
中dubbo的服务是暴露在本地的com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository
中的 allExportedURLs
属性中,不会传到注册中心服务端。所以最终暴露完成以后,nacos服务端无法感知到dubbo服务是否已准备妥当,也无法通知订阅者。这种情况下,提供者发起调用时通过泛化调用DubboMetadataService
接口获取提供者暴露的服务时,从 allExportedURLs
中获取到的就是一个空的 List<Url>
。然后消费者就会以为是没有提供者,于是在自己本地的dubbo服务目录 RegistryDirectory
中 把禁用属性 forbidden
的值更新为了 true
。
springCloudAlibaba+dubbo+nacos环境下,重启服务提供者或先启动服务消费者后启动服务提供者的情况下,消费者有时候会出现找不到服务的问题及解决方案
https://blog.csdn.net/tianzeyong/article/details/109513113
问题的直接原因
调用服务提供者时,消费者的dubbo的服务目录 org.apache.dubbo.registry.integration.RegistryDirectory
的 forbidden
属性 为 true
,如下图:
问题的根本原因
问题的根本原因是spring cloud alibaba框架启动nacos自动服务注册的时点比启动dubbo服务注册的时点早。前者的启动时点是监听到WebServerInitializedEvent
事件时(org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration#bind(WebServerInitializedEvent event)
),后者的启动时点是监听到ContextRefreshedEvent
事件时(org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener#onContextRefreshedEvent(ContextRefreshedEvent event)
)。
在spring boot 2.2.x
中ServletWebServerInitializedEvent
事件的发布是在ContextRefreshedEvent
事件之后,如图:
应用端解决方案
1、添加一个切面,切点为 `spring cloud` 的服务注册入口,然后在 `nacos` 服务注册之前先启动 `dubbo`,暴露dubbo服务:
@Before("execution( org.springframework.cloud.client.serviceregistry.ServiceRegistry.register()) && args(registration)")
public void beforeRegister(Registration registration) {
DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance();
dubboBootstrap.start();
}
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。