准备
使用分支:v2.x-develop
核心类
NacosNamingService:命名服务入口;
NamingClientProxyDelegate:命名客户端代理的委托;
NamingGrpcClientProxy:Grpc模式下命名客户端的代理;
RpcClient:抽象远程客户端连接到服务器;
源码解读
- 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);
}
- 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);//※客户端代理初始化
}
- 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)
}
- 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);
}
- 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链接创建成功时,客户端另起线程向服务端发送心跳请求维持客户端在线