1. Nacos 客户端自动注册流程
Spring Boot 在启动时,会扫描所有的 spring.factories 文件,从而加载 spring.factories 文件定义的配置类。
而 Nacos 客户端的配置类为 NacosServiceRegistryAutoConfiguration,NacosServiceRegistryAutoConfiguration 一共定义了三个 Bean 对象,分别是:
- NacosServiceRegistry:用于将微服务实例注册到 Nacos 服务注册中心。
- NacosRegistration:是一个信息封装类,用于表示一个已经注册到 Nacos 的服务实例。
- NacosAutoServiceRegistration:真正在 Spring Cloud 应用启动时,自动将服务实例注册到 Nacos 服务注册中心的类。
NacosServiceRegistryAutoConfiguration 的执行条件
NacosServiceRegistryAutoConfiguration 的类定义如下:
@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 {}
//====================================
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@ConditionalOnProperty(value = "spring.cloud.nacos.discovery.enabled",
matchIfMissing = true)
public @interface ConditionalOnNacosDiscoveryEnabled {
}
从 NacosServiceRegistryAutoConfiguration 类标记的注解可知,要加载 NacosServiceRegistryAutoConfiguration 定义的 Bean 需要满足以下条件:
spring.cloud.nacos.discovery.enabled设置为true,如果该值未被设置,则默认为truespring.cloud.service-registry.auto-registration.enabled设置为true,如果该值未被设置,则默认为true- 在
AutoServiceRegistrationConfiguration、AutoServiceRegistrationAutoConfiguration和NacosDiscoveryAutoConfiguration这三个类被配置类加载完成后。
NacosServiceRegistry
NacosServiceRegistry Bean 的配置
@Bean
public NacosServiceRegistry nacosServiceRegistry(
NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosServiceManager, nacosDiscoveryProperties);
}
从 Bean 定义方法可以看出,创建 NacosServiceRegistry 需要 NacosServiceManager 和 NacosDiscoveryProperties Bean
NacosServiceManager主要负责与 Nacos 服务端进行交互以实现对服务的操作和管理。NacosDiscoveryProperties则会读取 application 配置文件中加载定义的spring.cloud.nacos.discovery属性,并根本这些属性设置为其字段。
NacosServiceRegistry 内部最重要的是 register 的方法
@Override
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
return;
}
NamingService namingService = namingService();
String serviceId = registration.getServiceId();
String group = nacosDiscoveryProperties.getGroup();
Instance instance = getNacosInstanceFromRegistration(registration);
try {
namingService.registerInstance(serviceId, group, instance);
log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
instance.getIp(), instance.getPort());
}
catch (Exception e) {
if (nacosDiscoveryProperties.isFailFast()) {
log.error("nacos registry, {} register failed...{},", serviceId,
registration.toString(), e);
rethrowRuntimeException(e);
}
else {
log.warn("Failfast is false. {} register failed...{},", serviceId,
registration.toString(), e);
}
}
}
- register 是真正发起服务注册的方法
- 调用 register 方法需要
Registration作为参数。Registration是一个接口,主要用于表示服务实例的注册信息,它只有一个实现类,即NacosRegistration。NacosRegistration也就是NacosServiceRegistryAutoConfiguration定义的第二个 Bean 对象。
NacosRegistration
NacosRegistration 类是主要用于表示一个已注册到 Nacos 服务注册中心的服务实例的信息。通过这个类可以获取服务实例相关的信息。如服务 ID,服务端口,服务元数据等。
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosRegistration nacosRegistration(
ObjectProvider<List<NacosRegistrationCustomizer>> registrationCustomizers,
NacosDiscoveryProperties nacosDiscoveryProperties,
ApplicationContext context) {
return new NacosRegistration(registrationCustomizers.getIfAvailable(),
nacosDiscoveryProperties, context);
}
- 创建 NacosRegistration 的 Bean 的条件是 Spring 容器中已存在
AutoServiceRegistrationProperties类型的 Bean,AutoServiceRegistrationProperties是存储spring.cloud.service-registry.auto-registration属性的对象。 - 创建 NacosRegistration 需要
List<NacosRegistrationCustomizer>,NacosDiscoveryProperties和ApplicationContext作为参数。NacosRegistrationCustomizer是一个接口,用于自定义NacosRegistration对象,即对注册到 Nacos 服务注册中心的服务实例信息进行定制化操作。NacosDiscoveryProperties则是存储加载定义的spring.cloud.nacos.discovery属性的对象。ApplicationContext则是 Spring 容器。
NacosAutoServiceRegistration
从 NacosAutoServiceRegistration 的 Auto 可知,这个 Bean 便是发起服务自动注册的类。
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
NacosServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);
}
- 创建
NacosAutoServiceRegistration的 Bean 的条件是 Spring 容器中已存在AutoServiceRegistrationProperties类型的 Bean。AutoServiceRegistrationProperties是存储spring.cloud.service-registry.auto-registration属性的对象。 - 创建 NacosAutoServiceRegistration 需要
NacosServiceRegistry,AutoServiceRegistrationProperties和NacosRegistration作为参数。NacosServiceRegistry和NacosRegistration便是在NacosServiceRegistryAutoConfiguration定义的前两个对象。
NacosAutoServiceRegistration 的构造方法
public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
super(serviceRegistry, autoServiceRegistrationProperties);
this.registration = registration;
}
protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry, AutoServiceRegistrationProperties properties) {
this.serviceRegistry = serviceRegistry;
this.properties = properties;
}
- 通过 NacosAutoServiceRegistration 的构造函数可知,NacosAutoServiceRegistration 会将传入的三个参数分别赋予其
registration,serviceRegistry和properties三个成员变量。
NacosAutoServiceRegistration 的 register 方法
NacosAutoServiceRegistration 最重要的方法是其 register 方法,从方法名可知,这个方法是用于注册服务的方法。
// NacosAutoServiceRegistration
@Override
protected void register() {
if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
log.debug("Registration disabled.");
return;
}
if (this.registration.getPort() < 0) {
this.registration.setPort(getPort().get());
}
super.register();
}
// AbstractAutoServiceRegistration
protected void register() {
this.serviceRegistry.register(getRegistration());
}
通过分析 register 方法的调用链,发现 register 方法会在 NacosAutoServiceRegistration 的父类 AbstractAutoServiceRegistration 的 onApplicationEvent 方法中被调用。onApplicationEvent 方法是在 ApplicationListener 接口中被定义。
从 Spring Boot 容器启动的流程可知,Spring 的 ApplicationContext 在启动时会在 finishRefresh 方法发布 Application 事件,调用各种 ApplicationListener 实现类的 onApplicationEvent 方法,其中就包括了 AbstractAutoServiceRegistration 的 onApplicationEvent() 方法,并最终调用 NacosAutoServiceRegistration 的 register 方法进行服务自动化注册。

由 NacosAutoServiceRegistration 的 register 方法实现可知,register 方法最终会调用 NacosServiceRegistry 的 register 方法来完成服务注册。
此图可以很好的梳理服务自动注册逻辑:

- 通过
spring.factories文件,找到了一个注册相关的Configuration配置类,这个配置类里面定义了三个 Bean 对象。 - 创建第三个 Bean 对象,需要第一个、第二个 Bean 对象作为参数传进去,
第一个Bean 对象里面就有真正的register方法,并且赋值给了第三个 Bean 对象,父类中的serviceRegistry属性。 - 在第三个 Bean 对象的父类当中,有实现 Spring
监听器方法,所以在 Spring 容器启动的时候,会发布监听事件,从而执行 Nacos 注册的逻辑,调用 NacosServiceRegister 的 register 方法。
NacosServiceRegister 的 register 方法
// NacosRegistration
@Override
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for Nacos client...");
return;
}
// 获取 NacosNamingService
NamingService namingService = namingService();
// serviceId 是服务名称
String serviceId = registration.getServiceId();
// gourp 就是分组, Nacos 后台管理也是能看见分组的
String group = NacosDiscoveryProperties.getGroup();
// Instance 里面包含了有ip、port等信息,感觉这个很重要
Instance instance = getNacosInstanceFromRegistration(registration);
// 上面都是分支逻辑,不需要每一个都仔细去看,大概看一下就好了
try {
// 主线核心方法
// 我第一眼就看中了这个方法,注册实例 ,点击这个方法registerInstance
// 是一个接口,并且只有一个默认实现类 NacosNamingService
namingService.registerInstance(serviceId, group, instance);
log.info(" Nacos registry, {} {} {}:{} register finished", group, serviceId,
instance.getIp(), instance.getPort());
}
catch (Exception e) {
log.error(" Nacos registry, {} register failed...{},", serviceId,
registration.toString(), e);
rethrowRuntimeException(e);
}
}
NacosRegistration 的 register 方法的逻辑主要分为五部分:
- 检验 serviceId 是否存在。
- 获取 NamingService。
- 创建 Instance 对象。
- 获取 serviceId,group 和 instance 属性值。
- 调用 NamingService 的 registerInstance() 方法。
检验 serviceId 是否存在
register 首先会调用 NacosRegistration 获取 getServiceId() 方法,getServiceId() 的返回值来源于 NacosDiscoveryProperties 的 service 属性。得到 service 属性值后。如果发现值为空,则直接退出方法,不进行服务注册。
// NacosDiscoveryProperties
@Value("${spring.cloud.nacos.discovery.service:${spring.application.name:}}")
private String service;
- 通过 service 属性的 @Value() 注解可知,service 首先会从 application 配置文件的
spring.cloud.nacos.discovery.service属性读取值,如果读取失败,则会使用spring.application.name的属性值填充到 service 中。
获取 NamingService
检测 service 属性值无误后,会调用 namingService() 方法, nameService 方法最终会获取到 NacosNamingService,NacosNamingService 就代表着服务注册中心在客户端的抽象。通过调用 NacosNamingService 的 registerInstance 方法来完成服务实例的注册。
private NamingService namingService() {
return nacosServiceManager.getNamingService();
}
// NacosServiceManager
private volatile NamingService namingService;
public NamingService getNamingService() {
if (Objects.isNull(this.namingService)) {
buildNamingService(nacosDiscoveryProperties.getNacosProperties());
}
return namingService; // NacosNamingService
}
获取 serviceId,group 属性值
由检验 serviceId 是否存在的步骤可知,serviceId 的值来源于 application 配置文件的 spring.cloud.nacos.discovery.service 和 spring.application.name 属性。
而 group 的值则来源于 application 配置文件的 spring.cloud.nacos.discovery.group 属性,如果没有配置这个属性,则使用默认值 DEFAULT_GROUP。
创建 Instance 对象
Instance 对象代表一个服务实例。其中包含了服务实例相关的信息,包括:
ip:服务实例所在的 IP 地址。这是用于定位服务实例的关键信息之一,其他服务可以通过这个 IP 地址来访问该服务实例。port:服务实例监听的端口号。结合 IP 地址,确定了服务实例的具体网络位置。weight:服务实例的权重。可以用于负载均衡,决定在多个服务实例中分配请求的比例。例如,权重高的实例可能会接收更多的请求。healthy:表示服务实例的健康状态。通常是一个布尔值,用于指示服务实例是否正常运行,可被其他服务调用。enabled:表示服务实例是否可用。如果设置为false,即使服务实例在运行,也可能不会被服务发现机制返回给其他服务调用者。metadata:一个包含服务实例元数据的键值对集合。可以存储一些自定义的信息,如服务的版本号、所属环境等。这些元数据可以在服务发现和调用过程中被使用,例如根据特定的元数据进行服务筛选。
调用 NacosNamingService 的 registerInstance 方法
在获取到 serviceId,group 属性和创建好 Instance 对象后,就会调用 NacosNamingService 的 registerInstance 执行服务实例注册。
// NacosNamingService
@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
NamingUtils.checkInstanceIsLegal(instance);
clientProxy.registerService(serviceName, groupName, instance);
}
-
registerInstance 方法首先检查 Instance 是否合法。以下两种情况,被认为是非法
- 实例的心跳时间超过了心跳超时阈值,或者超过了 ip 删除超时时间。
- 实例的集群名称(如果不为空)是否符合特定的正则表达式要求。
-
如果 Instance 合法,则会调用 clientProxy 的 registerService 方法进行实例注册。
-
clientProxy 为 NamingClientProxy 的实现类,NamingClientProxy 接口有两个实现类,一个是 NamingHttpClientProxy,用于使用 http 协议注册服务实例。另一种是 NamingGrpcClientProxy,使用 grpc 协议注册。这两种 Proxy 又由 NameingClientProxyDelegate 来代理。
![image]()
-
NameingClientProxyDelegate会根据 application 配置的spring.cloud.nacos.discovery.ephemeral来决定应该使用使用哪个 NamingClientProxy- 如果
ephemeral=true,说明注册的示例是临时实例,则使用grpc协议注册。临时实例在服务正常运行时会向 Nacos 服务注册中心发送心跳,以表明自己的存活状态。如果 Nacos 服务注册中心在一段时间内没有收到某个临时实例的心跳,就会将该实例从服务列表中移除。 - 如果
ephemeral=false,则表示注册的服务实例为持久化实例,使用http协议。持久化实例不会因为一段时间没有发送心跳而被自动移除,通常用于一些需要长期存在且不依赖心跳保持注册状态的服务。
- 如果
NamingHttpClientProxy 的 registerService 方法
// NameingHttpClientProxy
@Override
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", namespaceId, serviceName,
instance);
String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
if (instance.isEphemeral()) {
throw new UnsupportedOperationException(
"Do not support register ephemeral instances by HTTP, please use gRPC replaced.");
}
final Map<String, String> params = new HashMap<>(32);
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, groupedServiceName);
params.put(CommonParams.GROUP_NAME, groupName);
params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
params.put(IP_PARAM, instance.getIp());
params.put(PORT_PARAM, String.valueOf(instance.getPort()));
params.put(WEIGHT_PARAM, String.valueOf(instance.getWeight()));
params.put(REGISTER_ENABLE_PARAM, String.valueOf(instance.isEnabled()));
params.put(HEALTHY_PARAM, String.valueOf(instance.isHealthy()));
params.put(EPHEMERAL_PARAM, String.valueOf(instance.isEphemeral()));
params.put(META_PARAM, JacksonUtils.toJson(instance.getMetadata()));
reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.POST);
}
-
通过 debug 查看发现,最终会使用 post 请求 nacos-service 的 /nacos/v1/ns/instance 方法注册服务实例。
-
reqApi 主要封装了 HTTP 请求实现,其关键方法的调用链为:
![image]()
-
最终的 HttpClientRequest 有三个实现类:
InterceptingHttpClientRequest:带拦截器的 HttpClientRequestDefaultHttpClientRequest:默认的 HttpClientRequest,底层的 HTTP 框架是 appache 的 http-clientJdkHttpClientRequest:使用 JDK 内自带的 http 客户端封装的 HttpClientRequest
NamingGrpcClientProxy 的 registerService 方法
// NamingGrpcClientProxy
@Override
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance {}", namespaceId, serviceName,
instance);
// 将 instance 缓存到 redosService,以备重试时使用
redoService.cacheInstanceForRedo(serviceName, groupName, instance);
// 执行服务注册
doRegisterService(serviceName, groupName, instance);
}
public void doRegisterService(String serviceName, String groupName, Instance instance) throws NacosException {
InstanceRequest request = new InstanceRequest(namespaceId, serviceName, groupName,
NamingRemoteConstants.REGISTER_INSTANCE, instance);
// 发送服务注册消息到 nacos 服务端
requestToServer(request, Response.class);
// 在 redoService 记录 instance 注册成功。
redoService.instanceRegistered(serviceName, groupName);
}
private <T extends Response> T requestToServer(AbstractNamingRequest request, Class<T> responseClass)
throws NacosException {
try {
request.putAllHeader(
getSecurityHeaders(request.getNamespace(), request.getGroupName(), request.getServiceName()));
// 调用 rpcClient 发送服务实例注册请求。
Response response =
requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout);
if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) {
throw new NacosException(response.getErrorCode(), response.getMessage());
}
if (responseClass.isAssignableFrom(response.getClass())) {
return (T) response;
}
NAMING_LOGGER.error("Server return unexpected response '{}', expected response should be '{}'",
response.getClass().getName(), responseClass.getName());
} catch (NacosException e) {
throw e;
} catch (Exception e) {
throw new NacosException(NacosException.SERVER_ERROR, "Request nacos server failed: ", e);
}
throw new NacosException(NacosException.SERVER_ERROR, "Server return invalid response");
}
- registerService 方法最核心的步骤是调用 GrpcClient 发送 request,Nacos 它在 RPC 的基础之上封装了一层 GrpcClient 对象,底层还是调用了 RPC 那一套。
- request 的内容如下所示:
![image]()
从自动注册可以知道,最终注册请求会通过 NamingGrpcClientProxy 或者 NamingHttpClientProxy 来发送,那这两个 Proxy 是什么时候创建的?
通过查看源码发现,NamingGrpcClientProxy 和 NamingHttpClientProxy 都是被 NamingClientProxyDelegate 创建的,而 NamingClientProxyDelegate 又是在NacosNamingService 实例化时创建的, NacosNamingService 则是在 NacosServiceRegistry 的 namingService 方法通过反射机制创建的。

接下来,就需要了解 NameGrpcClientProxy 和 NamingHttpClientProxy 的创建流程。
NameGrpcClientProxy 的创建流程
public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, ServerListFactory serverListFactory,
NacosClientProperties properties, ServiceInfoHolder serviceInfoHolder) throws NacosException {
super(securityProxy);
this.namespaceId = namespaceId;
this.uuid = UUID.randomUUID().toString();
this.requestTimeout = Long.parseLong(properties.getProperty(CommonParams.NAMING_REQUEST_TIMEOUT, "-1"));
Map<String, String> labels = new HashMap<>();
labels.put(RemoteConstants.LABEL_SOURCE, RemoteConstants.LABEL_SOURCE_SDK);
labels.put(RemoteConstants.LABEL_MODULE, RemoteConstants.LABEL_MODULE_NAMING);
this.rpcClient = RpcClientFactory.createClient(uuid, ConnectionType.GRPC, labels);
this.redoService = new NamingGrpcRedoService(this);
start(serverListFactory, serviceInfoHolder);
}
private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder) throws NacosException {
rpcClient.serverListFactory(serverListFactory);
rpcClient.registerConnectionListener(redoService);
rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder));
rpcClient.start();
NotifyCenter.registerSubscriber(this);
}
- NameGrpcClientProxy 的构造函数,为其成员变量赋值之后,
- 调用 RpcClientFactory 的 createClient 方法创建一个 RpcClient
- 创建一个 NamingGrpcRedoService,NamingGrpcRedoService 是重试服务器,在客户端请求 Nacos 失败后,会通过这个类发起重试请求。
- 最后调用 start 方法,start 方法最终会调用 RpcClient.start() 方法启动 RpcClient
RpcClient 的 start 方法
public final void start() throws NacosException {
// 修改rpc客户端状态为STARTING
boolean success = rpcClientStatus.compareAndSet(RpcClientStatus.INITIALIZED, RpcClientStatus.STARTING);
if (!success) {
return;
}
// 创建周期性任务线程池,线程池核心线程为2.
clientEventExecutor = new ScheduledThreadPoolExecutor(2, r -> {
Thread t = new Thread(r);
t.setName("com.alibaba.nacos.client.remote.worker");
t.setDaemon(true);
return t;
});
// connection event consumer.
// 向线程池提交处理连接事件的任务。
clientEventExecutor.submit(() -> {
// 首先判断线程池是否被关闭,如果被关闭,则不在执行。
while (!clientEventExecutor.isTerminated() && !clientEventExecutor.isShutdown()) {
ConnectionEvent take;
try {
// 从eventLinkedBlockingQueue拿连接事件
// 在连接成功时,会往队列中存放连接成功的事件;连接断开时会存放连接断开的事件
take = eventLinkedBlockingQueue.take();
// 判断事件是否是已连接的事件
if (take.isConnected()) {
// 是已连接的事件,则通知监听连接成功的监听器
// NamingGrpcRedoService 就是监听立连接成功监听器
// 连接成功后,NamingGrpcRedoService 将停止重试操作。
notifyConnected();
} else if (take.isDisConnected()) { // 如果事件是连接断开的事件
// 则通知监听连接断开的监听器。
// NamingGrpcRedoService 也监听这个事件
// 连接断开后,会通知 NamingGrpcRedoService 发起重试机制。
notifyDisConnected();
}
} catch (Throwable e) {
// Do nothing
}
}
});
// 服务端健康检查及重连服务,有重连的情况则不会进行健康检查,健康检查的前提是所有连接都健康
clientEventExecutor.submit(() -> {
while (true) {
try {
// 如果 rpclient 已经关闭,则退出循环
if (isShutdown()) {
break;
}
// 尝试获取重连信号。默认5秒内取不到则返回null
ReconnectContext reconnectContext = reconnectionSignal
.poll(rpcClientConfig.connectionKeepAlive(), TimeUnit.MILLISECONDS);
// 1、如果没有拿到ReconnectContext对象,则表示没有重连的需要,则会检查server的健康状况,
if (reconnectContext == null) {
// check alive time.
// 检查存活时间,这里默认会隔5s 检查一次 server 的健康情况
if (System.currentTimeMillis() - lastActiveTimeStamp >= rpcClientConfig.connectionKeepAlive()) {
// 判断当前连接是否健康
boolean isHealthy = healthCheck();
if (!isHealthy) {
// 如果currentConnection为空,表明还在启动连接server的情况,并不是server那边不健康的问题
if (currentConnection == null) {
continue;
}
// 如果 currentConnection 不为空,则是因为rpc客户端和服务端连接有异常。
LoggerUtils.printIfInfoEnabled(LOGGER,
"[{}] Server healthy check fail, currentConnection = {}",
rpcClientConfig.name(), currentConnection.getConnectionId());
// 获取当前 rpcClient 的状态
RpcClientStatus rpcClientStatus = RpcClient.this.rpcClientStatus.get();
// 如果客户端被关闭了,则直接循环。
// 如果不判断的话 SHUTDOWN 又被改为UNHEALTHY是不合理的
if (RpcClientStatus.SHUTDOWN.equals(rpcClientStatus)) {
break;
}
// 设置客户端不健康状态(使用 CAS 更新,必须要保证是从非 SHUTDOWN 状态切换到 UNHEALTHY 状态)
boolean statusFLowSuccess = RpcClient.this.rpcClientStatus
.compareAndSet(rpcClientStatus, RpcClientStatus.UNHEALTHY);
// 切换成功后,会设置一个serverInfo(相当于重新选一个随机的server来重连)
if (statusFLowSuccess) {
reconnectContext = new ReconnectContext(null, false);
} else {
// 如果设置失败,表示状态在更新之前发生了改变,则选择重试
// 可能是状态期间被改为SHUTDOWN了,也可能是状态期间被改为RUNNING
continue;
}
} else {
// 如果 isHealthy = true,说明是健康
// 记录最后一次的活跃时间。用于下一次对比存活时间。
lastActiveTimeStamp = System.currentTimeMillis();
continue;
}
} else {
// 如果 ReconnectContext对象不为空,说明需要重连
// 此时,不应该进行健康检测。
continue;
}
}
// 如果需要重连,且重连的服务器信息不为空,检查服务器的状态。
if (reconnectContext.serverInfo != null) {
// clear recommend server if server is not in server list.
boolean serverExist = false;
// 获取所有的服务器列表,遍历检查server是否存在,如果存在,则设置服务器的端口。
for (String server : getServerListFactory().getServerList()) {
ServerInfo serverInfo = resolveServerInfo(server);
if (serverInfo.getServerIp().equals(reconnectContext.serverInfo.getServerIp())) {
serverExist = true;
reconnectContext.serverInfo.serverPort = serverInfo.serverPort;
break;
}
}
// 如果服务器不存在则将ReconnectContext的服务信息设置为空。
if (!serverExist) {
LoggerUtils.printIfInfoEnabled(LOGGER,
"[{}] Recommend server is not in server list, ignore recommend server {}",
rpcClientConfig.name(), reconnectContext.serverInfo.getAddress());
reconnectContext.serverInfo = null;
}
}
// 执行重连操作
reconnect(reconnectContext.serverInfo, reconnectContext.onRequestFail);
} catch (Throwable throwable) {
// Do nothing
}
}
});
// connect to server, try to connect to server sync retryTimes times, async starting if failed.
// 连接服务器,如果连接失败,则根据 retry 时间重连。
Connection connectToServer = null;
rpcClientStatus.set(RpcClientStatus.STARTING);
int startUpRetryTimes = rpcClientConfig.retryTimes();
while (startUpRetryTimes > 0 && connectToServer == null) {
try {
startUpRetryTimes--;
// 获取服务器信息
ServerInfo serverInfo = nextRpcServer();
LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Try to connect to server on start up, server: {}",
rpcClientConfig.name(), serverInfo);
// 连接服务器
connectToServer = connectToServer(serverInfo);
} catch (Throwable e) {
LoggerUtils.printIfWarnEnabled(LOGGER,
"[{}] Fail to connect to server on start up, error message = {}, start up retry times left: {}",
rpcClientConfig.name(), e.getMessage(), startUpRetryTimes, e);
}
}
// 如果连接成功
if (connectToServer != null) {
LoggerUtils
.printIfInfoEnabled(LOGGER, "[{}] Success to connect to server [{}] on start up, connectionId = {}",
rpcClientConfig.name(), connectToServer.serverInfo.getAddress(),
connectToServer.getConnectionId());
// 设置当前 rpcClient 所连接的服务器信息。
this.currentConnection = connectToServer;
// 将 rpcClient 状态设置为正在运行。
rpcClientStatus.set(RpcClientStatus.RUNNING);
// 将连接事件队列加入连接成功事件。
eventLinkedBlockingQueue.offer(new ConnectionEvent(ConnectionEvent.CONNECTED));
} else {
// 如果连接失败,则切换服务器重新连接
switchServerAsync();
}
// 注册一个 Reset 请求的处理器。
registerServerRequestHandler(new ConnectResetRequestHandler());
// register client detection request.
// 注册一个 Detection 请求的处理器。
registerServerRequestHandler(request -> {
if (request instanceof ClientDetectionRequest) {
return new ClientDetectionResponse();
}
return null;
});
}
rpcClient 的 start 方法的主要逻辑包括:
- 创建一个有两个核心线程的线程池
- 向第一个线程提交处理连接事件的任务。该任务主要是从连接事件队列里获取连接事件,并通知事件监听器
- 向第二个线程提交健康检查的任务。该任务每个一段时间向 nacos 服务器发起健康检查。
- 向 nacos 服务器发起连接请求,
- 如果多次连接失败后,则切换服务器,再异步发起请求。
- 如果连接成功,则发布连接成功事件。
- 向请求处理器列表添加两个处理器
- ConnectResetRequestHandle
- ClientDetectionRequestHandler
NameHttpClientProxy 的创建流程
// NameHttpClientProxy
private final NacosRestTemplate nacosRestTemplate = NamingHttpClientManager.getInstance().getNacosRestTemplate();
public NamingHttpClientProxy(String namespaceId, SecurityProxy securityProxy, ServerListManager serverListManager,
NacosClientProperties properties) {
super(securityProxy);
this.serverListManager = serverListManager;
this.setServerPort(DEFAULT_SERVER_PORT);
this.namespaceId = namespaceId;
this.maxRetry = ConvertUtils.toInt(properties.getProperty(PropertyKeyConst.NAMING_REQUEST_DOMAIN_RETRY_COUNT,
String.valueOf(UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT)));
}
// AbstractNamingClientProxy
protected AbstractNamingClientProxy(SecurityProxy securityProxy) {
this.securityProxy = securityProxy;
}
- NameHttpClientProxy 首先会创建 NacosRestTemplate,NacosRestTemplate 是向 Nacos 发起请求的 Http 工具
- 在 NameHttpClientProxy 的构造函数中,会为其成员变量赋值。





浙公网安备 33010602011771号