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

相关推荐
DS小龙哥2 分钟前
基于华为云设计的智能宠物喂养管理系统
后端
liangsheng_g5 分钟前
kafka服务端架构总览
架构·kafka
David爱编程6 分钟前
synchronized 的可重入性:避免死锁的隐藏武器
java·后端
再学一点就睡10 分钟前
多端单点登录(SSO)实战:从架构设计到代码实现
前端·架构
二闹11 分钟前
Python字符串格式化:谁才是真正的硬汉?
后端·python
Livingbody20 分钟前
零代码实践自然语言处理 - 文本分类任务实现,以qwen模型和OpenAI SDK为例
后端
FrankYoou31 分钟前
spring boot autoconfigure 自动配置的类,和手工 @configuration + @bean 本质区别
java·spring boot·后端
lovebugs34 分钟前
JVM内存迷宫:破解OutOfMemoryError的终极指南
java·后端·面试
Swift社区35 分钟前
66项目中 Spring Boot 配置文件未生效该如何解决
java·spring boot·后端
阿拉伦42 分钟前
DDD柔性设计在智能交通拥堵治理中的设计模式落地实践
后端