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

文章目录

    • @[toc]
  • 1.环境
  • 2.版本
  • 3.pom依赖
    • 3.1父工程的pom
    • 3.2子模块的pom
  • 4.问题
  • 5.根本原因
    • 5.1根本原因说明
    • 5.2总入口
    • 5.3servletWeb容器初始化
    • 5.4 nacos服务注册监听点
    • 5.5 dubbo启动服务注册监听点
  • 6.解决办法
    • 6.1降低springBoot版本为2.2.x
    • 6.2 修改源码
      • 6.2.1修改源码方式一
      • 6.2.2修改源码方式二
    • 6.3应用启动后,更新一下在注册中心的实例状态
      • 6.3.1方式一
      • 6.3.2方式二
  • 7.dubbo本地调用验证
    • 7.1 dubbo两个服务本地调用配置如下
  • 8.总结

1.环境

  springCloudAlibaba+dubbo+nacos的环境,环境搭建或升级请参看这三个的官方文档

2.版本

  JDK版本:1.8

  springBoot的版本:2.3.12.RELEASE

  spring-cloud.version版本:Hoxton.SR9

  spring-cloud-alibaba.version版本:2.2.6.RELEASE

  dubbo.version版本:2.2.6.RELEASE

  nacos服务端2.0.3、nacos客服端1.4.2

  注意:这个客户端还是要跟服务端的版本相匹配的,

  nacos2.x的客户端用的是GRPC调用,而nacos客户端1.4.x使用的是http的方式,这种还是兼容的,但是最好是版本匹配不容易出问题。

3.pom依赖

3.1父工程的pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.12.RELEASE</version><relativePath/></parent><groupId>com.xxx.xx</groupId><artifactId>xxx-xxx</artifactId><packaging>pom</packaging><version>1.0.0-SNAPSHOT</version><modules><module>xxx-api</module><module>xxx-service</module></modules><distributionManagement><repository><id>nexus-snapshots</id><name>Nexus snapshots</name><url>xxxxxxx</url></repository></distributionManagement><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><spring-cloud.version>Hoxton.SR9</spring-cloud.version><spring-cloud-alibaba.version>2.2.6.RELEASE</spring-cloud-alibaba.version><dubbo.version>2.2.6.RELEASE</dubbo.version><mybatisplus.version>3.5.1</mybatisplus.version></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies></project>

3.2子模块的pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://maven.apache.org/POM/4.0.0"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>xxx-xxxx</artifactId><groupId>com.xxx.xxx</groupId><version>1.0.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>xxxx-service</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-dubbo</artifactId><version>${dubbo.version}</version></dependency><dependency><groupId>com.xxx.xxxx</groupId><artifactId>xxxx-api</artifactId><version>1.2.0-SNAPSHOT</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.8</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

4.问题

org.apache.dubbo.rpc.RpcException: No provider available from registry localhost:9090 for service xxxx on consumer 192.168.20.92.1 use dubbo version 2.7.8, please check status of providers(disabled, not registered or in blacklist).
at org.apache.dubbo.registry.integration.RegistryDirectory.doList(RegistryDirectory.java:599)at org.apache.dubbo.rpc.cluster.directory.AbstractDirectory.list(AbstractDirectory.java:74)at org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker.list(AbstractClusterInvoker.java:292)at org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:257)at org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor.intercept(ClusterInterceptor.java:47)at org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster$InterceptorInvokerNode.invoke(AbstractCluster.java:92)at org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(MockClusterInvoker.java:88)at org.apache.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(InvokerInvocationHandler.java:74)

  调用服务提供者时,消费者的dubbo的服务目录 org.apache.dubbo.registry.integration.RegistryDirectoryforbidden 属性 为 true

图片

5.根本原因

5.1根本原因说明

  在springBoot2.3.x版本中nacos的服务注册是监听了ServletWebServerInitializedEvent事件,该事件是servletWeb容器初始化完成后会发这个事件,而dubbo的服务注册时机是在SpringBoot容器完成刷新的时候会发ContextRefreshedEvent这个事件,是nacos的服务注册时间早于dubbo的服务注册的时间,这种会导致nacos服务端在处理了服务提供者的注册请求后向订阅者下发了实例变更通知,而在这个过程中提供者自身的dubbo服务暴露有可能还没有完成,最直接的表现就是服务提供者的 com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepositoryallExportedURLs 属性中还没有对应的dubbo服务的URL。

  因为spring cloud alibaba + dubbo 中dubbo的服务是暴露在本地的com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository中的 allExportedURLs 属性中,不会传到注册中心服务端。所以最终暴露完成以后,nacos服务端无法感知到dubbo服务是否已准备妥当,也无法通知订阅者。这种情况下,提供者发起调用时通过泛化调用DubboMetadataService接口获取提供者暴露的服务时,从 allExportedURLs 中获取到的就是一个空的 List。然后消费者就会以为是没有提供者,于是在自己本地的dubbo服务目录 RegistryDirectory 中 把禁用属性 forbidden 的值更新为了 true

  在spring boot 2.2.xServletWebServerInitializedEvent事件的发布是在ContextRefreshedEvent事件之后,源码分析如下(以下是springBoot2.3.x的版本):

5.2总入口

public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment);context = createApplicationContext();prepareContext(context, environment, listeners, applicationArguments, printedBanner);refreshContext(context);//这里就是总入口afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}return context;}

5.3servletWeb容器初始化

  AbstractApplicationContext#onRefresh

图片

  ServletWebServerApplicationContext#onRefresh

图片

  ServletWebServerApplicationContext#createWebServer

图片

  WebServerStartStopLifecycle#start

图片

5.4 nacos服务注册监听点

  nacos的服务注册的上层抽象定义是在spring-cloud-commons2.2.6.RELEASE的  AbstractAutoServiceRegistration#onApplicationEvent#bind

图片

	public void start() {if (!isEnabled()) {if (logger.isDebugEnabled()) {logger.debug("Discovery Lifecycle disabled. Not starting");}return;}// only initialize if nonSecurePort is greater than 0 and it isn't already running// because of containerPortInitializer belowif (!this.running.get()) {this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration()));register();//这里是注册的上层接口定义if (shouldRegisterManagement()) {registerManagement();}this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration()));this.running.compareAndSet(false, true);}}

  NacosServiceRegistry#register

public void register(Registration registration) {if (StringUtils.isEmpty(registration.getServiceId())) {log.warn("No service to register for nacos client...");} else {NamingService namingService = this.namingService();String serviceId = registration.getServiceId();String group = this.nacosDiscoveryProperties.getGroup();Instance instance = this.getNacosInstanceFromRegistration(registration);try {namingService.registerInstance(serviceId, group, instance);log.info("nacos registry, {} {} {}:{} register finished", new Object[]{group, serviceId, instance.getIp(), instance.getPort()});} catch (Exception var7) {if (this.nacosDiscoveryProperties.isFailFast()) {log.error("nacos registry, {} register failed...{},", new Object[]{serviceId, registration.toString(), var7});ReflectionUtils.rethrowRuntimeException(var7);} else {log.warn("Failfast is false. {} register failed...{},", new Object[]{serviceId, registration.toString(), var7});}}}}

5.5 dubbo启动服务注册监听点

  DubboBootstrapApplicationListener#onApplicationContextEvent#onContextRefreshedEvent#dubboBootstrap.start

图片

  DubboBootstrap#start

public DubboBootstrap start() {if (started.compareAndSet(false, true)) {ready.set(false);initialize();if (logger.isInfoEnabled()) {logger.info(NAME + " is starting...");}// 1. export Dubbo Services //暴露dubbo服务exportServices();// Not only provider registerif (!isOnlyRegisterProvider() || hasExportedServices()) {// 2. export MetadataService //暴露dubbo服务元数据服务exportMetadataService();//3. Register the local ServiceInstance if requiredregisterServiceInstance(); //注册dubbo服务实例}referServices();if (asyncExportingFutures.size() > 0) {new Thread(() -> {try {this.awaitFinish();} catch (Exception e) {logger.warn(NAME + " exportAsync occurred an exception.");}ready.set(true);if (logger.isInfoEnabled()) {logger.info(NAME + " is ready.");}}).start();} else {ready.set(true);if (logger.isInfoEnabled()) {logger.info(NAME + " is ready.");}}if (logger.isInfoEnabled()) {logger.info(NAME + " has started.");}}return this;}

6.解决办法

6.1降低springBoot版本为2.2.x

  该方法有可能不适配,需要去调整适配到相对应的版本上才可以的,具体可以去尝试下

6.2 修改源码

  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);@Autowiredprivate 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 DubboBootstrapStartCommandLineRunnerimplements CommandLineRunner, ApplicationEventPublisherAware {private ApplicationEventPublisher applicationEventPublisher;@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}@Overridepublic 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 DubboServiceRegistrationEventPublishingAspectimplements 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));}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}}

  下面的源码修改正式利用了上面这几个地方。

6.2.1修改源码方式一

  DubboServiceRegistrationEventPublishingAspect该切面会拦截上面的NacosServiceRegistry#register方法执行前做一些处理

  将这类DubboServiceRegistrationEventPublishingAspect提出来修改

图片

package com.alibaba.cloud.dubbo.registry;
@Aspect
public class DubboServiceRegistrationEventPublishingAspectimplements 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;private static DubboBootstrap dubboBootstrap;//这里是新增的代码static {//这里是新增的代码dubboBootstrap = DubboBootstrap.getInstance();}@Before(value = REGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration")public void beforeRegister(ServiceRegistry registry, Registration registration) {dubboBootstrap.start();//这里是新增的代码applicationEventPublisher.publishEvent(new ServiceInstancePreRegisteredEvent(registry, registration));}@Before(value = DEREGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration")public void beforeDeregister(ServiceRegistry registry, Registration registration) {dubboBootstrap.stop();//这里是新增的代码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));}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}}

  将DubboBootstrapApplicationListener类提出来修改:
图片

  DubboBootstrapApplicationListener修改如下:

public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListenerimplements Ordered {/*** The bean name of {@link DubboBootstrapApplicationListener}** @since 2.7.6*/public static final String BEAN_NAME = "dubboBootstrapApplicationListener";private final DubboBootstrap dubboBootstrap;public DubboBootstrapApplicationListener() {this.dubboBootstrap = DubboBootstrap.getInstance();}@Overridepublic void onApplicationContextEvent(ApplicationContextEvent event) {if (event instanceof ContextRefreshedEvent) {//onContextRefreshedEvent((ContextRefreshedEvent) event); 注释这一行,这个逻辑已经放到上面的DubboServiceRegistrationEventPublishingAspect里面来触发了} else if (event instanceof ContextClosedEvent) {onContextClosedEvent((ContextClosedEvent) event);}}private void onContextRefreshedEvent(ContextRefreshedEvent event) {dubboBootstrap.start();}private void onContextClosedEvent(ContextClosedEvent event) {dubboBootstrap.stop();}@Overridepublic int getOrder() {return LOWEST_PRECEDENCE;}
}

6.2.2修改源码方式二

  将DubboBootstrapApplicationListener的监听父级事件放宽松,修改监听触发事件改为InstancePreRegisteredEvent事件,这个InstancePreRegisteredEvent是在nacos注册前会发这个时间的,上面nacos的注册点有这个代码的,可以去欣赏下的,需要重新如下几个类:

图片

  DubboBootstrapApplicationListener类修改入下:

public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListenerimplements Ordered {/*** The bean name of {@link DubboBootstrapApplicationListener}** @since 2.7.6*/public static final String BEAN_NAME = "dubboBootstrapApplicationListener";private final DubboBootstrap dubboBootstrap;public DubboBootstrapApplicationListener() {this.dubboBootstrap = DubboBootstrap.getInstance();}/*@Overridepublic void onApplicationContextEvent(ApplicationContextEvent event) {if (event instanceof ContextRefreshedEvent) {onContextRefreshedEvent((ContextRefreshedEvent) event);} else if (event instanceof ContextClosedEvent) {onContextClosedEvent((ContextClosedEvent) event);}}*/@Overridepublic void onApplicationContextEvent(ApplicationEvent event) {if (event instanceof InstancePreRegisteredEvent) {onContextRefreshedEvent((InstancePreRegisteredEvent) event);} else if (event instanceof ContextClosedEvent) {onContextClosedEvent((ContextClosedEvent) event);}}/*private void onContextRefreshedEvent(ContextRefreshedEvent event) {dubboBootstrap.start();}*/private void onContextRefreshedEvent(InstancePreRegisteredEvent event) {dubboBootstrap.start();}/*private void onContextClosedEvent(ContextClosedEvent event) {dubboBootstrap.stop();}*/private void onContextClosedEvent(ContextClosedEvent event) {dubboBootstrap.stop();}@Overridepublic int getOrder() {return LOWEST_PRECEDENCE;}
}

  DubboLifecycleComponentApplicationListener类修改如下:

public class DubboLifecycleComponentApplicationListener extends OneTimeExecutionApplicationContextEventListener {/*** The bean name of {@link DubboLifecycleComponentApplicationListener}** @since 2.7.6*/public static final String BEAN_NAME = "dubboLifecycleComponentApplicationListener";private List<Lifecycle> lifecycleComponents = emptyList();/* @Overrideprotected void onApplicationContextEvent(ApplicationContextEvent event) {if (event instanceof ContextRefreshedEvent) {onContextRefreshedEvent((ContextRefreshedEvent) event);} else if (event instanceof ContextClosedEvent) {onContextClosedEvent((ContextClosedEvent) event);}}*/@Overrideprotected void onApplicationContextEvent(ApplicationEvent event) {if (event instanceof InstancePreRegisteredEvent) {onContextRefreshedEvent((InstancePreRegisteredEvent) event);} else if (event instanceof ContextClosedEvent) {onContextClosedEvent((ContextClosedEvent) event);}}/*  protected void onContextRefreshedEvent(ContextRefreshedEvent event) {initLifecycleComponents(event);startLifecycleComponents();}*/protected void onContextRefreshedEvent(InstancePreRegisteredEvent event) {initLifecycleComponents(event);startLifecycleComponents();}protected void onContextClosedEvent(ContextClosedEvent event) {destroyLifecycleComponents();}/*private void initLifecycleComponents(ContextRefreshedEvent event) {ApplicationContext context = event.getApplicationContext();ClassLoader classLoader = context.getClassLoader();lifecycleComponents = new LinkedList<>();// load the Beans of Lifecycle from ApplicationContextloadLifecycleComponents(lifecycleComponents, context);}*/private void initLifecycleComponents(InstancePreRegisteredEvent event) {ApplicationContext context = SpringUtils.getApplicationContext();ClassLoader classLoader = context.getClassLoader();lifecycleComponents = new LinkedList<>();// load the Beans of Lifecycle from ApplicationContextloadLifecycleComponents(lifecycleComponents, context);}private void loadLifecycleComponents(List<Lifecycle> lifecycleComponents, ApplicationContext context) {lifecycleComponents.addAll(beansOfTypeIncludingAncestors(context, Lifecycle.class).values());}private void startLifecycleComponents() {lifecycleComponents.forEach(Lifecycle::start);}private void destroyLifecycleComponents() {lifecycleComponents.forEach(Lifecycle::destroy);}
}

  OneTimeExecutionApplicationContextEventListener类修改如下:

abstract class OneTimeExecutionApplicationContextEventListener implements ApplicationListener, ApplicationContextAware {private ApplicationContext applicationContext;/* public final void onApplicationEvent(ApplicationEvent event) {if (isOriginalEventSource(event) && event instanceof ApplicationContextEvent) {onApplicationContextEvent((ApplicationContextEvent) event);}}*/public final void onApplicationEvent(ApplicationEvent event) {if (isOriginalEventSource(event) && event instanceof InstancePreRegisteredEvent) {onApplicationContextEvent((ApplicationContextEvent) event);}}/*** The subclass overrides this method to handle {@link ApplicationContextEvent}** @param event {@link ApplicationContextEvent}*///protected abstract void onApplicationContextEvent(ApplicationContextEvent event);protected abstract void onApplicationContextEvent(ApplicationEvent event);/*** Is original {@link ApplicationContext} as the event source** @param event {@link ApplicationEvent}* @return*//*private boolean isOriginalEventSource(ApplicationEvent event) {return (applicationContext == null) // Current ApplicationListener is not a Spring Bean, just was added// into Spring's ConfigurableApplicationContext|| Objects.equals(applicationContext, event.getSource());}*/private boolean isOriginalEventSource(ApplicationEvent event) {return (applicationContext == null) // Current ApplicationListener is not a Spring Bean, just was added// into Spring's ConfigurableApplicationContext|| Objects.equals(applicationContext, event.getSource());}@Overridepublic final void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}public ApplicationContext getApplicationContext() {return applicationContext;}
}

  将dubbo服务启动注册监听点提前到nacos的服务注册点之前执行,会发送一个ServiceInstancePreRegisteredEvent事件会被dubbo自动装配监听后将dubbo服务注册的服务信息放到registrations中:

	@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);}}

  当dubbo服务启动后会发一个DubboBootstrapStartedEvent事件dubbo的自动自动装配监听到这个事件将自动装配中监听的ServiceInstancePreRegisteredEvent事件提前注册的dubbo服务注册实例在去遍历注册到nacos上:

@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);}));}

   dubboBootstrap.start();的启动和注册只会被重复一次,方法里面使用的是CAS机制保证只启动注册一次,有兴趣的小伙伴可以去参看源码。

6.3应用启动后,更新一下在注册中心的实例状态

  在应用启动后,在 ApplicationRunner接口的run方法中,调用 springCloudAlibaba框架中的NacosServiceRegistry类的setStatus方法,更新一下在注册中心的实例状态,这两种方式的本质都是在应用启动都开了两个线程,当服务启动的时候会有一个线程不断的周期性的去上报务实例的相关信息,当服务停止的时候也是会上报状态给nacos服务端,然后nacos服务端收到服务上线(上线会关闭检测的线程池)或下线会发一个服务实例变更通知给所有的client,然后各个client会收到这个服务实例变更的通知,然后更新本地的服务缓存列表,这样不管是消费者先启动,服务提供者后启动,消费者都可以感知到服务的变更,然后正确的调用到所需的服务实例。

6.3.1方式一

import com.alibaba.cloud.nacos.registry.NacosRegistration;
import com.alibaba.cloud.nacos.registry.NacosServiceRegistry;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.common.lifecycle.Closeable;
import com.alibaba.nacos.common.utils.ThreadUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;@Component
public class NacosServiceInstanceUpAndDownOperator implements ApplicationRunner, Closeable {protected Logger logger = LoggerFactory.getLogger(this.getClass());/*** nacos服务实例上线*/private static final String OPERATOR_UP = "UP";/*** nacos服务实例下线*/private static final String OPERATOR_DOWN = "DOWN";@ResourceNacosServiceRegistry nacosServiceRegistry;@ResourceNacosRegistration nacosRegistration;private ScheduledExecutorService executorService;@PostConstructpublic void init() {int poolSize = 1;this.executorService = new ScheduledThreadPoolExecutor(poolSize, new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {Thread thread = new Thread(r);thread.setDaemon(true);thread.setName("NacosServiceInstanceUpAndDownOperator");return thread;}});}@Overridepublic void run(ApplicationArguments args) throws Exception {long delayDown = 5000L;  //下线任务延迟long delayUp = 10000L;   // 上线任务延迟this.executorService.schedule(new InstanceDownAndUpTask(nacosServiceRegistry, nacosRegistration, OPERATOR_DOWN), delayDown, TimeUnit.MILLISECONDS);this.executorService.schedule(new InstanceDownAndUpTask(nacosServiceRegistry, nacosRegistration, OPERATOR_UP), delayUp, TimeUnit.MILLISECONDS);}@Overridepublic void shutdown() throws NacosException {ThreadUtils.shutdownThreadPool(executorService, logger);}/*** 服务实例上下线任务*/class InstanceDownAndUpTask implements Runnable {private NacosServiceRegistry nacosServiceRegistry;private NacosRegistration nacosRegistration;//更新服务实例的状态 :UP 、DOWNprivate String nacosServiceInstanceOperator;InstanceDownAndUpTask(NacosServiceRegistry nacosServiceRegistry, NacosRegistration nacosRegistration, String nacosServiceInstanceOperator) {this.nacosServiceRegistry = nacosServiceRegistry;this.nacosRegistration = nacosRegistration;this.nacosServiceInstanceOperator = nacosServiceInstanceOperator;}@Overridepublic void run() {logger.info("===更新nacos服务实例的状态to:{}===start=", nacosServiceInstanceOperator);this.nacosServiceRegistry.setStatus(nacosRegistration, nacosServiceInstanceOperator);logger.info("===更新nacos服务实例的状态to:{}===end=", nacosServiceInstanceOperator);//上线后,关闭线程池if (NacosServiceInstanceUpAndDownOperator.OPERATOR_UP.equals(nacosServiceInstanceOperator)) {ThreadUtils.shutdownThreadPool(NacosServiceInstanceUpAndDownOperator.this.executorService, NacosServiceInstanceUpAndDownOperator.this.logger);}}}
}

6.3.2方式二

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.registry.NacosRegistration;
import com.alibaba.cloud.nacos.registry.NacosServiceRegistry;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.common.lifecycle.Closeable;
import com.alibaba.nacos.common.utils.ThreadUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Properties;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;@Component
public class NacosUpDown implements ApplicationRunner, Closeable {private static final Logger logger = LoggerFactory.getLogger(NacosUpDown.class);/*** nacos服务实例上线*/private static final String OPERATOR_UP = "UP";/*** nacos服务实例下线*/private static final String OPERATOR_DOWN = "DOWN";@ResourceNacosServiceRegistry nacosServiceRegistry;@ResourceNacosRegistration nacosRegistration;@Resourceprivate NacosServiceManager nacosServiceManager;@Resourceprivate NacosDiscoveryProperties nacosDiscoveryProperties;private ScheduledExecutorService executorService;@PostConstructpublic void init() {int poolSize = 1;this.executorService = new ScheduledThreadPoolExecutor(poolSize, r -> {Thread thread = new Thread(r);thread.setDaemon(true);thread.setName("NacosUpAndDown");return thread;});}@Overridepublic void run(ApplicationArguments args){//下线任务延迟long delayDown = 15000L;// 上线任务延迟long delayUp = 21000L;this.executorService.schedule(new InstanceDownAndUpTask(nacosServiceRegistry, nacosRegistration, OPERATOR_DOWN), delayDown, TimeUnit.MILLISECONDS);this.executorService.schedule(new InstanceDownAndUpTask(nacosServiceRegistry, nacosRegistration, OPERATOR_UP), delayUp, TimeUnit.MILLISECONDS);}@Overridepublic void shutdown() {ThreadUtils.shutdownThreadPool(executorService, logger);}/*** 服务实例上下线任务*/class InstanceDownAndUpTask implements Runnable {private final NacosServiceRegistry nacosServiceRegistry;private final NacosRegistration nacosRegistration;//更新服务实例的状态 :UP 、DOWNprivate final String nacosServiceInstanceOperator;InstanceDownAndUpTask(NacosServiceRegistry nacosServiceRegistry, NacosRegistration nacosRegistration, String nacosServiceInstanceOperator) {this.nacosServiceRegistry = nacosServiceRegistry;this.nacosRegistration = nacosRegistration;this.nacosServiceInstanceOperator = nacosServiceInstanceOperator;}@Overridepublic void run() {logger.info("===更新nacos服务实例的状态to:{}===start=", nacosServiceInstanceOperator);setStatus(nacosRegistration, nacosServiceInstanceOperator);logger.info("===更新nacos服务实例的状态to:{}===end=", nacosServiceInstanceOperator);//上线后,关闭线程池if (NacosUpDown.OPERATOR_UP.equals(nacosServiceInstanceOperator)) {ThreadUtils.shutdownThreadPool(NacosUpDown.this.executorService, logger);}}}public void setStatus(Registration registration, String status) {if (!status.equalsIgnoreCase(OPERATOR_UP) && !status.equalsIgnoreCase(OPERATOR_DOWN)) {} else {String serviceId = registration.getServiceId();Instance instance = this.getNacosInstanceFromRegistration(registration);if (status.equalsIgnoreCase(OPERATOR_DOWN)) {instance.setEnabled(false);} else {instance.setEnabled(true);}try {Properties nacosProperties = this.nacosDiscoveryProperties.getNacosProperties();this.nacosServiceManager.getNamingMaintainService(nacosProperties).updateInstance(serviceId,nacosProperties.getProperty("group"), instance);} catch (Exception var6) {throw new RuntimeException("update nacos instance status fail", var6);}}}private Instance getNacosInstanceFromRegistration(Registration registration) {Instance instance = new Instance();instance.setIp(registration.getHost());instance.setPort(registration.getPort());instance.setWeight(this.nacosDiscoveryProperties.getWeight());instance.setClusterName(this.nacosDiscoveryProperties.getClusterName());instance.setEnabled(this.nacosDiscoveryProperties.isInstanceEnabled());instance.setMetadata(registration.getMetadata());instance.setEphemeral(this.nacosDiscoveryProperties.isEphemeral());return instance;}
}

  这个问题可以升级版本看看,在高匹配版本上官方有没有把这个bug修复了。

7.dubbo本地调用验证

7.1 dubbo两个服务本地调用配置如下

  服务提供者yml配置:

server:address: 192.168.20.2 # 本机ipport: 8081
dubbo:provider:host: 192.168.20.2
spring:cloud:nacos:config:server-addr: ${nacos.addr}group: ${nacos.group}namespace: ${nacos.ns}file-extension: yamldiscovery:server-addr: ${nacos.addr}namespace: ${nacos.ns}ip: 192.168.20.2 # 服务提供者注册指定ip注册nacos:addr: xxxx:8848group: xxxxxns: xxxxx

  服务消费者yml配置和消费者的配置基本大同小异,都要加上上面那几个本地的ip配置

  服务提供者的nacos的公共配置:

server:port: 12188
dubbo:provider:filter: -validationconsumer:check: falsecloud:subscribed-services: ''scan:base-packages: com.dy.member.service.dubboprotocol:name: dubboport: -1 # 这里设置为-1就会导致每次服务提供者重启后,服务提供者的服务端口会变,这里在本地两个服务相互调用的时候需要注意registry:address: spring-cloud://localhost # nacos://xxxx:8848 这种方式是直接注册到nacos上不注册到本地的目录中,每次都去nacos上拉取最新的,就不至于会等实例变更通知后,客户端没有及时去拉取nacos上的服务实例信息缓存到本地,在从本地调用application:version: 1.0.0

  服务消费者调用服务提供者的dubbo接口代码姿势如下:

@RestController
@RequestMapping("xxx")
@Slf4j
public class xxxxController {@DubboReference(version = "${dubbo.application.version}",url = "dubbo://192.168.20.2:20880")UserClient userClient; //这里是服务提供这的dubbo服务,需要设置url为本地注册到nacos的服务的url信息,该信息可以从nacos的服务那里查看,如果测试环境,不配置这个url,可以将测试环境上的其它这个服务提供者注册上去的服务下线,只保留你自己本地启动注册上去的服务,这种就可以不用加这个url了,因为nacos上只有你的你本地的服务,就可以直接调用到这个服务的,若果加了这个url,需要去nacos上查看服务提供者本地注册上去的dubbo服务的地址和端口@PostMapping("getUserInfo")public RestResponse<?> getUserInfo() {Long uid = customThreadLocal.getCustomInfo().getMemberId();MemberVOV2 memberVOV2 = userClient.queryMemberByUid(uid);}
}

  nacos服务提供者查看服务信息和下线服务提供者的非本地的服务

图片

  本机验证,先把服务提供者的nacos的测试环境的其它服务提供实例下线,然后启动服务消费者,然后启动服务提供者后,在使用postman调用服务消费的getUserInfo接口发现是可以立马调用到接口的,这个问题由于是一个偶现的问题,所以还得去测试环境或者生产环境具体的验证的,所以采用那种方式就至关重要了,如果改源码的方式,风险会有点大的,因为你不知道不这种改会不会还有其它的bug问题出现的,所以推荐6.3应用启动后,更新一下在注册中心的实例状态的这种方式,如果要使用改源码的方法是是可以的,6.2和6.3一起使用,双保险的,改源码的方式经过上面的源码分析,评估是可以行的,不会有太大的影响,但是还是得小心谨慎哦,充分验证后在决定使用啥方式来搞。

8.总结

  这个问题也是一个同事问我的,当时我也很懵,后面经过一番查阅源码分析后总结得出以上的方法,希望对大家有所帮助,请一键三连,么么哒!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/103674.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习笔记之优化算法(十六)梯度下降法在强凸函数上的收敛性证明

机器学习笔记之优化算法——梯度下降法在强凸函数上的收敛性证明 引言回顾&#xff1a;凸函数与强凸函数梯度下降法&#xff1a;凸函数上的收敛性分析 关于白老爹定理的一些新的认识梯度下降法在强凸函数上的收敛性收敛性定理介绍结论分析证明过程 引言 本节将介绍&#xff1a…

探索PDF校对:为何这是现代数字文档的关键步骤

在今日的数字化浪潮中&#xff0c;文档的创建与分享从未如此频繁。尤其是PDF&#xff0c;作为一个普遍接受的标准文件格式&#xff0c;其在企业、学术和日常生活中的应用已经无处不在。但随之而来的挑战是如何确保文档的准确性和专业性。让我们深入探索PDF校对的重要性以及它为…

Linux 定时任务 crontab 用法学习整理

一、linux版本 lsb_release -a 二、crontab 用法学习 2.1&#xff0c;crontab 简介 linux中crontab命令用于设置周期性被执行的指令&#xff0c;该命令从标准输入设备读取指令&#xff0c;并将其存放于“crontab”文件中&#xff0c;以供之后读取和执行。cron 系统调度进程。…

SQL注入之万能用户名

文章目录 分析代码原理实现 分析代码 在安装的cms数据库目录C:\phpStudy\WWW\cms\admin下找到login.action.php文件&#xff0c;查看第20行&#xff0c;发现如下php代码&#xff1a; $user_row $db->getOneRow("select userid from cms_users where username "…

消息队列——RabbitMQ(一)

MQ的相关概念 什么事mq MQ(message queue)&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO 先入先出&#xff0c;只不过队列中存放的内容是 message 而已&#xff0c;还是一种跨进程的通信机制&#xff0c;用于上下游传递消息。在互联网架构中&#xff…

【unity数据持久化】XML数据管理器知识点

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

如何更高效的写出更健全的代码,一篇文章教会你如何拥有一个良好的代码风格

前言&#xff1a;在平常的写代码的过程中&#xff0c;或多或少的遇到很多奇怪的 bug &#xff0c;尤其是一些大的程序&#xff0c;明明上一部分都是好好的&#xff0c;写下一块的时候突然多几百个 bug 的情况&#xff0c;然后这一块写完了后编译的时候直接傻眼了&#xff0c;看…

缓存穿透、缓存击穿和缓存雪崩

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱发博客的嗯哼&#xff0c;爱好Java的小菜鸟 &#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&#x1f44d;一下博主哦 &#x1f4dd;社区论坛&#xff1a;希望大家能加入社区共同进步…

拼多多app商品详情接口 获取pdd商品主图价格销量库存信息

拼多多是中国一家知名的电商平台&#xff0c;以"社交团购新零售"的商业模式闻名&#xff0c;通过手机app和微信小程序等渠道提供商品销售和购物体验。平台上的商品种类丰富多样&#xff0c;涵盖了服装、家居、美妆、食品、数码电子等各个领域。 拼多多的商业模式主要…

Windows运行Spark所需的Hadoop安装

解压文件 复制bin目录 找到winutils-master文件hadoop对应的bin目录版本 全部复制替换掉hadoop的bin目录文件 复制hadoop.dll文件 将bin目录下的hadoop.dll文件复制到System32目录下 配置环境变量 修改hadoop-env.cmd配置文件 注意jdk装在非C盘则完全没问题&#xff0c;如果装在…

springboot+docker实现微服务的小例子

【任务】&#xff1a; 创建一个服务A&#xff1a;service_hello 创建一个服务B&#xff1a;service_name service_name负责提供一个api接口返回一个name字符串。 service_hello负责从这个接口获取name字符串&#xff0c;然后进行一个字符串拼接&#xff0c;在后面加一个hello&…

Module not found: Error: Can‘t resolve ‘vue-pdf‘ in ‘xxx‘

使用命令npm run serve时vue项目报错&#xff1a; Module not found: Error: Cant resolve vue-pdf in xxx 解决方案&#xff1a; 运行命令&#xff1a; npm install vue-pdf --save --legacy-peer-deps 即可解决。 再次顺利执行npm run serve

C语言暑假刷题冲刺篇——day4

目录 一、选择题 二、编程题 &#x1f388;个人主页&#xff1a;库库的里昂 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏✨收录专栏&#xff1a;C语言每日一练 ✨其他专栏&#xff1a;代码小游戏C语言初阶&#x1f91d;希望作者的文章能对你…

更改计算机睡眠时间

控制面板–>系统和安全–>电源选项下的更改计算机睡眠时间 如果关闭显示器时间小于使计算机进入睡眠状态时间&#xff0c;时间先到达关闭显示器时间&#xff0c;显示器关闭&#xff0c;这时电脑还在正常工作状态。如果此时敲击键盘显示器出现画面&#xff0c;无需输入密…

MySQL 主从配置

环境 centos6.7 虚拟机两台 主&#xff1a;192.168.23.160 从&#xff1a;192.168.23.163 准备 在两台机器上分别安装mysql5.6.23&#xff0c;安装完成后利用临时密码登录mysql数据修改root的密码&#xff1b;将my.cnf配置文件放至/etc/my.cnf&#xff0c;重启mysql服务进…

Spark Standalone环境搭建及测试

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 篇一&#xff1a;Linux系统下配置java环境 篇二&#xff1a;hadoop伪分布式搭建&#xff08;超详细&#xff09; 篇三&#xff1a;hadoop完全分布式集群搭建&#xff08;超详细&#xf…

解决生僻字,中兴新支点操作系统通过GB 18030-2022《中文编码字符集》认证

您认识上图中的这个字吗&#xff1f; 上面一个“鸟”&#xff0c;下面一个“甲”&#xff0c;这个字读“nia&#xff08;四声&#xff09;”。它是云南丽江傈僳族中一支氏族的姓氏。这个氏族以鸟为图腾。因信息系统中无法输入显示“nia”字&#xff0c;氏族里近700人不得不妥协…

python并发编程

一、程序提速的方法 二、python对并发编程的支持 多线程&#xff1a;threading&#xff0c;利用CPU和IO可以同时执行的原理&#xff0c;让CPU不会干巴巴等待IO完成&#xff1b;多进程&#xff1a;multiprocess&#xff0c;利用多核CPU的能力&#xff0c;真正的并行执行任务&am…

某多多商品平台数据采集

某多多商品平台数据采集 声明逆向目标寻找加密位置代码分析补环境补充内容声明 本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者 无关,若有侵权,请私信我立即删除! 逆向目标 Anti-Content参数 寻找加密位置 先在控制台全局搜…

idea 新建servlet 访问提示404 WebServlet注解找不到包 报错

检查访问路径是否设置正确 如果设置为name “/testServlet”&#xff0c;则会404 WebServlet注解报错找不到包 检查是否引入了tomcat依赖包