nacos-client模块学习《心跳维持》

准备

源码链接:gitee.com/mirrors/Nac...

使用分支:v2.x-develop

核心类

NacosNamingService:命名服务入口;

NamingClientProxyDelegate:命名客户端代理的委托;

NamingGrpcClientProxy:Grpc模式下命名客户端的代理;

RpcClient:抽象远程客户端连接到服务器;

源码解读

  1. client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java
java 复制代码
@BeforeEach
void before() throws NoSuchFieldException, NacosException, IllegalAccessException {
    Properties prop = new Properties();
    prop.setProperty("serverAddr", "localhost");
    prop.put(PropertyKeyConst.NAMESPACE, "test");
    client = new NacosNamingService(prop);//※创建命名服务
    injectMocks(client);
}
  1. client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java
java 复制代码
public NacosNamingService(Properties properties) throws NacosException {
    init(properties);//※开始初始化
}

private void init(Properties properties) throws NacosException {
    PreInitUtils.asyncPreLoadCostComponent();
    final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties);
    NAMING_LOGGER.info(ParamUtil.getInputParameters(nacosClientProperties.asProperties()));
    ValidatorUtils.checkInitParam(nacosClientProperties);
    this.namespace = InitUtils.initNamespaceForNaming(nacosClientProperties);
    InitUtils.initSerialization();
    InitUtils.initWebRootContext(nacosClientProperties);
    initLogName(nacosClientProperties);
    this.notifierEventScope = UUID.randomUUID().toString();
    this.changeNotifier = new InstancesChangeNotifier(this.notifierEventScope);
    NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384);
    NotifyCenter.registerSubscriber(changeNotifier);
    this.serviceInfoHolder = new ServiceInfoHolder(namespace, this.notifierEventScope, nacosClientProperties);
    this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, nacosClientProperties,
            changeNotifier);//※客户端代理初始化
}
  1. client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java
java 复制代码
public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder,
        NacosClientProperties properties, InstancesChangeNotifier changeNotifier) throws NacosException {
    this.serviceInfoUpdateService = new ServiceInfoUpdateService(properties, serviceInfoHolder, this,
            changeNotifier);
    this.serverListManager = new NamingServerListManager(properties, namespace);
    this.serverListManager.start();
    this.serviceInfoHolder = serviceInfoHolder;
    this.securityProxy = new SecurityProxy(this.serverListManager,
            NamingHttpClientManager.getInstance().getNacosRestTemplate());
    initSecurityProxy(properties);
    this.httpClientProxy = new NamingHttpClientProxy(namespace, securityProxy, serverListManager, properties);
    this.grpcClientProxy = new NamingGrpcClientProxy(namespace, securityProxy, serverListManager, properties,
            serviceInfoHolder);//※创建客户端的代理(Grpc)
}
  1. client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java
java 复制代码
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);
    labels.put(Constants.APPNAME, AppNameUtils.getAppName());
    GrpcClientConfig grpcClientConfig = RpcClientConfigFactory.getInstance()
            .createGrpcClientConfig(properties.asProperties(), labels);
    this.rpcClient = RpcClientFactory.createClient(uuid, ConnectionType.GRPC, grpcClientConfig);
    this.redoService = new NamingGrpcRedoService(this, properties);
    this.enableClientMetrics = Boolean.parseBoolean(
            properties.getProperty(PropertyKeyConst.ENABLE_CLIENT_METRICS, "true"));
    NAMING_LOGGER.info("Create naming rpc client for uuid->{}", uuid);
    start(serverListFactory, serviceInfoHolder);//※grpc客户端的启动入口
}

private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder) throws NacosException {
    rpcClient.serverListFactory(serverListFactory);
    rpcClient.registerConnectionListener(redoService);
    rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder));
    rpcClient.start();//※grpc客户端启动
    NotifyCenter.registerSubscriber(this);
}
  1. common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClient.java
java 复制代码
/**
 * Start this client.
 */
public final void start() throws NacosException {
    
    //---------------------忽略--------------------
    /**
     * 另起线程循环向服务端发送心跳校验直到客户端关闭
     */
    clientEventExecutor.submit(() -> {
        while (true) {
            try {
                if (isShutdown()) {
                    break;
                }
                ReconnectContext reconnectContext = reconnectionSignal
                        .poll(rpcClientConfig.connectionKeepAlive(), TimeUnit.MILLISECONDS);
                if (reconnectContext == null) {
                    // 检测最后一次活动时间是否在保活区间内,不在则重新检测
                    if (System.currentTimeMillis() - lastActiveTimeStamp >= rpcClientConfig.connectionKeepAlive()) {
                        boolean isHealthy = healthCheck();//※客户端健康检测
                        if (!isHealthy) {
                            if (currentConnection == null) {
                                continue;
                            }
                            LoggerUtils.printIfInfoEnabled(LOGGER,
                                    "[{}] Server healthy check fail, currentConnection = {}",
                                    rpcClientConfig.name(), currentConnection.getConnectionId());
                            
                            RpcClientStatus rpcClientStatus = RpcClient.this.rpcClientStatus.get();
                            if (RpcClientStatus.SHUTDOWN.equals(rpcClientStatus)) {
                                break;
                            }
                            
                            boolean statusFLowSuccess = RpcClient.this.rpcClientStatus
                                    .compareAndSet(rpcClientStatus, RpcClientStatus.UNHEALTHY);
                            if (statusFLowSuccess) {
                                reconnectContext = new ReconnectContext(null, false);
                            } else {
                                continue;
                            }
                            
                        } else {
                            lastActiveTimeStamp = System.currentTimeMillis();
                            continue;
                        }
                    } else {
                        continue;
                    }
                    
                }
                
                if (reconnectContext.serverInfo != null) {
                    // clear recommend server if server is not in server list.
                    boolean serverExist = false;
                    for (String server : getServerListFactory().getServerList()) {
                        ServerInfo serverInfo = resolveServerInfo(server);
                        if (serverInfo.getServerIp().equals(reconnectContext.serverInfo.getServerIp())) {
                            serverExist = true;
                            reconnectContext.serverInfo.serverPort = serverInfo.serverPort;
                            break;
                        }
                    }
                    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
            }
        }
    });
    
    //---------------忽略---------------------
}
/**
 * 向服务端发送心跳,维持设备健康状态【默认重试三次】
 * @return
 */
private boolean healthCheck() {
    HealthCheckRequest healthCheckRequest = new HealthCheckRequest();
    if (this.currentConnection == null) {
        return false;
    }
    int reTryTimes = rpcClientConfig.healthCheckRetryTimes();
    Random random = new Random();
    while (reTryTimes >= 0) {
        reTryTimes--;
        try {
            if (reTryTimes > 1) {
                Thread.sleep(random.nextInt(500));
            }
            Response response = this.currentConnection
                    .request(healthCheckRequest, rpcClientConfig.healthCheckTimeOut());
            // not only check server is ok, also check connection is register.
            return response != null && response.isSuccess();
        } catch (Exception e) {
            // ignore
        }
    }
    return false;
}

一句话概括

当nacos客户端与服务端的Grpc链接创建成功时,客户端另起线程向服务端发送心跳请求维持客户端在线

相关推荐
IT_陈寒15 小时前
Redis性能翻倍的5个冷门技巧,90%开发者都不知道第3个!
前端·人工智能·后端
p***976115 小时前
SpringBoot(7)-Swagger
java·spring boot·后端
j***294815 小时前
springboot集成onlyoffice(部署+开发)
java·spring boot·后端
晨非辰15 小时前
C++ 波澜壮阔 40 年:从基础I/O到函数重载与引用的完整构建
运维·c++·人工智能·后端·python·深度学习·c++40周年
张较瘦_15 小时前
Springboot | Spring Boot 3 纯 JDBC 实现宠物管理系统增删改查(无 ORM 框架)
spring boot·后端·数据库开发
h***673717 小时前
SpringBoot整合easy-es
spring boot·后端·elasticsearch
ALex_zry19 小时前
Docker Compose运维技术实战分享:从安装到架构解析
运维·docker·架构
S***26751 天前
基于SpringBoot和Leaflet的行政区划地图掩膜效果实战
java·spring boot·后端
不爱吃糖的程序媛1 天前
华为 CANN:昇腾 AI 的异构计算架构核心与开源生态解析
人工智能·华为·架构
晚霞的不甘1 天前
升腾异构计算架构 CANN 详解:从底层到应用的全栈解析
架构