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链接创建成功时,客户端另起线程向服务端发送心跳请求维持客户端在线

相关推荐
hdsoft_huge1 小时前
SpringBoot 与 JPA 整合全解析:架构优势、应用场景、集成指南与最佳实践
java·spring boot·架构
Livingbody2 小时前
基于【ERNIE-4.5-VL-28B-A3B】模型的图片内容分析系统
后端
你的人类朋友3 小时前
🍃Kubernetes(k8s)核心概念一览
前端·后端·自动化运维
追逐时光者4 小时前
面试第一步,先准备一份简洁、优雅的简历模板!
后端·面试
DoraBigHead4 小时前
你写前端按钮,他们扛服务器压力:搞懂后端那些“黑话”!
前端·javascript·架构
慕木兮人可4 小时前
Docker部署MySQL镜像
spring boot·后端·mysql·docker·ecs服务器
发粪的屎壳郎4 小时前
ASP.NET Core 8 轻松配置Serilog日志
后端·asp.net·serilog
isNotNullX5 小时前
数据中台架构解析:湖仓一体的实战设计
java·大数据·数据库·架构·spark
倔强青铜三5 小时前
苦练Python第4天:Python变量与数据类型入门
前端·后端·python
倔强青铜三6 小时前
苦练Python第3天:Hello, World! + input()
前端·后端·python