Nacos源码—6.Nacos升级gRPC分析一

大纲

1.Nacos 2.x版本的一些变化

2.客户端升级gRPC发起服务注册

3.服务端进行服务注册时的处理

4.客户端服务发现和服务端处理服务订阅的源码分析

1.Nacos 2.x版本的一些变化

变化一:客户端和服务端的交互方式由HTTP升级为gRPC

Nacos 1.x服务端会提供一系列的HTTP接口供客户端请求调用,Nacos 2.x服务端会定义一些列Handler处理类来处理客户端的gRPC请求。

Nacos 1.x进行服务注册时使用的是HTTP短连接,Nacos 2.x进行服务注册时使用的是RPC长连接。

变化二:注册中心的注册表由双重Map结构变为轻量的一个Map

Nacos 1.x版本中的注册中心是使用双重Map结构来存储注册表的,这样使得注册表还是比较重量级的,在并发高时也需要考虑并发冲突。

Nacos 2.x版本则把注册表轻量化了,服务端在处理服务注册时,只是简单地往一个Map记写入客户端连接的ID。

变化三:大量使用了事件驱动

Nacos 2.x版本的Nacos里,使用了非常多的事件驱动。比如服务注册、服务销毁、服务变更等都先通过通知中心来发布一个事件,然后通过处理事件来来处理后续的逻辑流程。

2.客户端升级gRPC发起服务注册

(1)客户端和服务端的版本选择

(2)Nacos客户端项目启动时自动触发服务实例注册

(3)Nacos客户端通过gRPC方式发起服务实例注册

(4)总结

(1)客户端和服务端的版本选择

<!-- 使用的是Nacos 1.4.1 -->
<!-- <properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    <spring-cloud-alibaba.version>2.2.5.RELEASE</spring-cloud-alibaba.version>
</properties> -->

<!-- 使用的是Nacos 2.1.0 -->
<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
    <spring-cloud-alibaba.version>2.2.8.RELEASE</spring-cloud-alibaba.version>
</properties>

(2)Nacos客户端项目启动时自动触发服务实例注册

spring-cloud-starter-alibaba-nacos-discovery依赖包会自动注册服务。查看这个依赖包中的spring.factories文件,会指定一些Configuration类。Spring Boot启动时会扫描spring.factories文件,然后创建里面的配置类。

在spring.pactories文件中,与注册相关的类就是:NacosServiceRegistryAutoConfiguration这个Nacos服务注册自动配置类。

Nacos服务注册自动配置类NacosServiceRegistryAutoConfiguration如下,该配置类创建了三个Bean:

第一个Bean:NacosServiceRegistry

这个Bean在创建时,会传入加载了yml配置文件内容的类NacosDiscoveryProperties。

第二个Bean:NacosRegistration

这个Bean在创建时,会传入加载了yml配置文件内容的类NacosDiscoveryProperties。

第三个Bean:NacosAutoServiceRegistration

这个Bean在创建时,会传入NacosServiceRegistry和NacosRegistration两个Bean,然后该Bean继承了AbstractAutoServiceRegistration抽象类。该抽象类实现了ApplicationListener接口,所以项目启动时便是利用了Spring的监听事件来实现自动注册服务的。因为在Spring容器启动的最后会执行finishRefresh()方法,然后会发布一个事件,该事件会触发调用onApplicationEvent()方法。

调用AbstractAutoServiceRegistration的onApplicationEvent()方法时,首先会调用AbstractAutoServiceRegistration的bind()方法,然后调用AbstractAutoServiceRegistration的start()方法,接着调用AbstractAutoServiceRegistration的register()方法发起注册,也就是调用this.serviceRegistry的register()方法完成服务注册的具体工作。

而AbstractAutoServiceRegistration的serviceRegistry属性,是在服务注册自动配置类NacosServiceRegistryAutoConfiguration,创建第三个Bean—NacosAutoServiceRegistration时,通过传入其创建的第一个Bean—NacosServiceRegistry进行赋值的。

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class, AutoServiceRegistrationAutoConfiguration.class, NacosDiscoveryAutoConfiguration.class })
public class NacosServiceRegistryAutoConfiguration {
    @Bean
    public NacosServiceRegistry nacosServiceRegistry(NacosServiceManager nacosServiceManager, NacosDiscoveryProperties nacosDiscoveryProperties) {
        return new NacosServiceRegistry(nacosServiceManager, nacosDiscoveryProperties);
    }

    @Bean
    @ConditionalOnBean(AutoServiceRegistrationProperties.class)
    public NacosRegistration nacosRegistration(ObjectProvider<List<NacosRegistrationCustomizer>> registrationCustomizers, NacosDiscoveryProperties nacosDiscoveryProperties, ApplicationContext context) {
        return new NacosRegistration(registrationCustomizers.getIfAvailable(), nacosDiscoveryProperties, context);
    }

    @Bean
    @ConditionalOnBean(AutoServiceRegistrationProperties.class)
    public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry registry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
        return new NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);
    }
}

public class NacosAutoServiceRegistration extends AbstractAutoServiceRegistration<Registration> {
    ...
    private NacosRegistration registration;

    public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry,
            AutoServiceRegistrationProperties autoServiceRegistrationProperties,
            NacosRegistration registration) {
        super(serviceRegistry, autoServiceRegistrationProperties);
        this.registration = registration;
    }
    ...
}

public abstract class AbstractAutoServiceRegistration<R extends Registration>
        implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> {
    ...
    private final ServiceRegistry<R> serviceRegistry;
    private AutoServiceRegistrationProperties properties;

    protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry, AutoServiceRegistrationProperties properties) {
        this.serviceRegistry = serviceRegistry;
        this.properties = properties;
    }
    ...

    @Override
    @SuppressWarnings("deprecation")
    public void onApplicationEvent(WebServerInitializedEvent event) {
        bind(event);
    }

    public void bind(WebServerInitializedEvent event) {
        ApplicationContext context = event.getApplicationContext();
        if (context instanceof ConfigurableWebServerApplicationContext) {
            if ("management".equals(((ConfigurableWebServerApplicationContext) context).getServerNamespace())) {
                return;
            }
        }
        this.port.compareAndSet(0, event.getWebServer().getPort());
        this.start();
    }

    public void start() {
        if (!isEnabled()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Discovery Lifecycle disabled. Not starting");
            }
            return;
        }
        if (!this.running.get()) {
            this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration()));
            //发起注册
            register();
            if (shouldRegisterManagement()) {
                registerManagement();
            }
            this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration()));
            this.running.compareAndSet(false, true);
        }
    }

    protected void register() {
        //调用创建NacosAutoServiceRegistration时传入的NacosServiceRegistry实例的register()方法
        this.serviceRegistry.register(getRegistration());
    }
    ...
}

public class NacosServiceRegistry implements ServiceRegistry<Registration> {
    private final NacosDiscoveryProperties nacosDiscoveryProperties;

    public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
        this.nacosDiscoveryProperties = nacosDiscoveryProperties;
    }

    @Override
    public void register(Registration registration) {
        if (StringUtils.isEmpty(registration.getServiceId())) {
            log.warn("No service to register for nacos client...");
            return;
        }
        NamingService namingService = namingService();
        //获取服务ID、分组
        String serviceId = registration.getServiceId();
        String group = nacosDiscoveryProperties.getGroup();
        //创建Instance对象
        Instanc
<think>我们正在处理个关于Nacos客户端日志配置的问题。用户报告的错误是: "Unable to locate appender ASYNC-REMOTE for logger config com.alibaba.nacos.shaded.io.grpc" 这个错误通常发生在Nacos客户端尝试使用名为"ASYNC-REMOTE"的appender记录日志时,但在当前的日志配置中找不到该appender。 ### 问题分析 1. **日志框架配置问题**:Nacos客户端内部使用了SLF4J作为日志门面,实际日志实现可能是Logback、Log4j等。错误信息表明,在日志配置中,没有为名为"com.alibaba.nacos.shaded.io.grpc"的logger配置个叫做"ASYNC-REMOTE"的appender。 2. **Nacos客户端的日志配置**:Nacos客户端在启动时会尝试加载默认的日志配置,但用户可能自定义了日志配置,覆盖了Nacos的默认配置。 3. **依赖冲突**:如果项目中存在多个日志框架的依赖,可能会导致配置混乱。 ### 解决方案 #### 1. 检查日志配置文件 确保日志配置文件(如logback.xml、log4j2.xml等)中定义了名为"ASYNC-REMOTE"的appender,并且该appender被关联到logger"com.alibaba.nacos.shaded.io.grpc"。 例如,在Logback中,配置可能如下: ```xml <appender name="ASYNC-REMOTE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 配置略 --> </appender> <logger name="com.alibaba.nacos.shaded.io.grpc" level="INFO"> <appender-ref ref="ASYNC-REMOTE"/> </logger> ``` #### 2. 检查默认日志配置 如果用户没有提供自定义日志配置,Nacos客户端会使用默认配置。但某些情况下,默认配置可能被覆盖。可以尝试在项目中添加个简单的日志配置文件(如logback.xml)并配置所需的appender。 #### 3. 排除日志依赖冲突 检查项目的依赖树,确保没有引入多个日志框架的冲突版本。可以使用Maven的依赖分析工具: ```bash mvn dependency:tree ``` 或Gradle的依赖分析: ```bash gradle dependencies ``` 然后排除不必要的日志依赖。 例如,在Maven中排除冲突的日志依赖: ```xml <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> </exclusions> ``` #### 4. 使用Nacos默认日志配置 如果问题仍然存在,可以尝试使用Nacos自带的默认日志配置。在classpath下添加Nacos的默认日志配置文件(例如,从Nacos客户端jar包中提取logback.xml文件并放入项目的resources目录下)。但请注意,这可能会覆盖你的自定义配置。 ### 补充说明 - 如果问题仅在特定环境(如生产环境)出现,请检查该环境下的日志配置是否正确部署。 - 确保日志配置文件的名称和位置正确,并且被正确加载。 ### 示例配置(Logback) 以下是个简单的Logback配置示例,定义了个控制台输出和个文件输出的appender,并将文件输出appender命名为"ASYNC-REMOTE"(注意:实际中"ASYNC-REMOTE"可能是个异步appender,这里仅作示例): ```xml <configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <appender name="ASYNC-REMOTE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>nacos.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>nacos.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <maxFileSize>100MB</maxFileSize> <maxHistory>30</maxHistory> <totalSizeCap>1GB</totalSizeCap> </rollingPolicy> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <logger name="com.alibaba.nacos.shaded.io.grpc" level="INFO"> <appender-ref ref="ASYNC-REMOTE"/> </logger> <root level="INFO"> <appender-ref ref="CONSOLE"/> </root> </configuration> ``` 请根据实际需求调整。 ### 相关问题 1. 如何为Nacos客户端配置日志级别? 2. 如何解决SLF4J绑定多个日志框架的问题? 3. Nacos客户端在Spring Cloud项目中日志配置的优先级是怎样的?</think>### Nacos客户端日志配置问题分析 您遇到的错误信息 `Nacos client ERROR Unable to locate appender ASYNC-REMOTE for logger config com.alibaba.nacos.shaded.io.grpc` 表明Nacos客户端在尝试使用名为"ASYNC-REMOTE"的日志追加器(appender)时失败。以下是系统性的解决方案: --- ### 问题原因分析 1. **日志配置缺失** Nacos客户端内部使用`com.alibaba.nacos.shaded.io.grpc`包下的GRPC组件,该组件默认配置了名为`ASYNC-REMOTE`的appender。当您的项目日志配置中缺少此appender定义时,会触发此错误[^1]。 2. **日志框架冲突** 项目可能存在多个日志框架(如Logback、Log4j2、SLF4J)的依赖冲突,导致配置加载异常。 3. **配置覆盖问题** 自定义日志配置文件(如`logback.xml`)未正确继承Nacos默认配置,或错误覆盖了关键配置项。 --- ### 解决方案 #### 方案1:添加缺失的appender配置(推荐) 在您的日志配置文件(如`logback.xml`)中明确定义`ASYNC-REMOTE` appender: ```xml <!-- logback.xml 示例 --> <configuration> <!-- 1. 定义ASYNC-REMOTE appender --> <appender name="ASYNC-REMOTE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logs/nacos-remote.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>logs/nacos-remote.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <maxFileSize>100MB</maxFileSize> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <!-- 2. 关联到Nacos GRPC日志器 --> <logger name="com.alibaba.nacos.shaded.io.grpc" level="INFO"> <appender-ref ref="ASYNC-REMOTE"/> <!-- 关键配置 --> </logger> </configuration> ``` #### 方案2:排除冲突日志依赖 检查并排除重复的日志框架依赖(Maven示例): ```xml <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <exclusions> <!-- 排除冲突的日志绑定 --> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> ``` #### 方案3:启用Nacos默认配置 在`application.properties`中强制使用Nacos内置日志配置: ```properties # 强制加载Nacos默认日志配置 nacos.logging.config=classpath:nacos-logback.xml ``` --- ### 验证步骤 1. **检查配置生效** 启动时添加JVM参数验证配置加载: ```bash -Dlogging.config=classpath:logback.xml -Dnacos.logging.default.config.enabled=true ``` 2. **查看日志输出** 检查`logs/nacos-remote.log`是否生成,并包含GRPC相关日志。 3. **依赖树分析** 使用命令检查冲突: ```bash mvn dependency:tree | grep -E 'log4j|logback|slf4j' ``` --- ### 技术原理 Nacos客户端通过`nacos-client-logging`模块初始化日志系统,其默认配置要求存在`ASYNC-REMOTE` appender用于GRPC通信日志[^2]。当自定义配置覆盖默认配置但未保留此appender时,会导致组件级日志记录失败。 --- ### 相关问题 1. 如何为Nacos客户端配置不同的日志级别? 2. Spring Boot项目中如何统管理Nacos与其他组件的日志配置? 3. Nacos客户端日志配置不生效的常见排查方法有哪些? [^1]: Nacos官方文档 - 日志配置说明 [^2]: Nacos GitHub源码 - nacos-client-logging模块
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值