1、启动配置
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${version}</version>
</dependency>
通过SpringCloud注解 @EnableDiscoveryClient 开启服务注册发现功能。
地址配置 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848。
2、NacosDiscoveryAutoConfiguration
使用spring.factories SPI机制加载,NacosDiscoveryAutoConfiguration创建NacosDiscoveryProperties 统一管理配置和NacosServiceDiscovery 服务发现两个对象。
@Bean
@ConditionalOnMissingBean
public NacosDiscoveryProperties nacosProperties() {
return new NacosDiscoveryProperties();
}
@Bean
@ConditionalOnMissingBean
public NacosServiceDiscovery nacosServiceDiscovery(
NacosDiscoveryProperties discoveryProperties,NacosServiceManager nacosServiceManager) {
return new NacosServiceDiscovery(discoveryProperties, nacosServiceManager);
}
NacosDiscoveryProperties 统一管理客户端配置项,默认搜索顺序 properties -> 命令行参数 -> 环境参数 -> 默认值。
调整优先级 (命令行配置) -Dnacos.env.first=PROPERTIES|JVM|ENV
调整优先级 (环境变量配置) NACOS_ENV_FIRST=PROPERTIES|JVM|ENV。
3、NacosServiceRegistryAutoConfiguration
NacosServiceRegistryAutoConfiguration 自动配置服务注册相关的功能。
自动注册配置方式: spring.cloud.service-registry.auto-registration.enabled=true,@EnableDiscoveryClient(autoRegister = true)。
@Bean
public NacosServiceRegistry nacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosDiscoveryProperties);
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosRegistration nacosRegistration(
ObjectProvider<List<NacosRegistrationCustomizer>> registrationCustomizers,
NacosDiscoveryProperties nacosDiscoveryProperties,ApplicationContext context) {
return new NacosRegistration(registrationCustomizers.getIfAvailable(),nacosDiscoveryProperties, context);
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
NacosServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry,autoServiceRegistrationProperties, registration);
}
NacosRegistration 创建服务实例的信息,如服务名称、IP 地址、端口号等。
NacosServiceRegistry 将服务实例的信息注册到 Nacos 服务器。
NacosAutoServiceRegistration 实现自动注册服务实例到 Nacos 服务器。
4、NacosServiceAutoConfiguration
NacosServiceAutoConfiguration,创建NacosServiceManager是service核心管理类。
@Bean
public NacosServiceManager nacosServiceManager() {
return new NacosServiceManager();
}
NacosServiceManager类的方法getNamingService ,获取NamingService实现类如果为空则通过反射创建对象。
public NamingService getNamingService(Properties properties) {
if (Objects.isNull(this.namingService)) {
buildNamingService(properties);
}
return namingService;
}
//使用双重检查,避免重复创建对象
private NamingService buildNamingService(Properties properties) {
if (Objects.isNull(namingService)) {
synchronized (NacosServiceManager.class) {
if (Objects.isNull(namingService)) {
namingService = createNewNamingService(properties);
}
}
}
return namingService;
}
5、NacosNamingService
NacosNamingService 主要功能包括服务实例的注册与注销、服务发现等。
public NacosNamingService(String serverList) throws NacosException {
Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverList);
init(properties);
}
public NacosNamingService(Properties properties) throws NacosException {
init(properties);
}
private void init(Properties properties) throws NacosException {
//创建新的配置文件类
NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties);
//检查是否正确的URL
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);
}
//根据不同的配置委托给不同对象,实现处理解耦
private NamingClientProxy getExecuteClientProxy(Instance instance) {
return instance.isEphemeral() ? grpcClientProxy : httpClientProxy;
}
spring.cloud.nacos.discovery.ephemeral=true|false 配置是否为临时节点。
持久化节点使用CP(Raft)协议保障数据一致性,临时节点使用AP (Distro) 协议保障数据一致性。
NamingClientProxyDelegate 会创建NamingHttpClientProxy和NamingGrpcClientProxy对象。
持久化节点注册使用HTTP客户端,临时节点注册使用gRPC客户端,默认为临时节点。
6、客户端建立连接
NamingGrpcClientProxy 创建和管理 gRPC客户端,GrpcClient#start()方法与Nacos服务端建立连接,每隔5秒执行健康检查,如果响应失败会重新连接或切换连接。
public final void start() throws NacosException {
//修改客户端连接状态
boolean success = rpcClientStatus.compareAndSet(RpcClientStatus.INITIALIZED, RpcClientStatus.STARTING);
if (!success) {
return;
}
clientEventExecutor = new ScheduledThreadPoolExecutor(2, r -> {
Thread t = new Thread(r);
t.setName("com.alibaba.nacos.client.remote.worker");
t.setDaemon(true);
return t;
});
// 处理连接事件的消费者
clientEventExecutor.submit(() -> {
while (!clientEventExecutor.isTerminated() && !clientEventExecutor.isShutdown()) {
ConnectionEvent take;
try {
//阻塞获取连接事件
take = eventLinkedBlockingQueue.take();
if (take.isConnected()) {
//连接成功的事件处理
notifyConnected();
} else if (take.isDisConnected()) {
//连接断开的事件处理
notifyDisConnected();
}
} catch (Throwable e) {
}
}
});
//健康检查或连接重连,默认5秒执行一次
clientEventExecutor.submit(() -> {
while (true) {
try {
if (isShutdown()) {
break;
}
//获取重新连接任务为空则执行健康检查
ReconnectContext reconnectContext = reconnectionSignal
.poll(rpcClientConfig.connectionKeepAlive(), TimeUnit.MILLISECONDS);
if (reconnectContext == null) {
//活动间隔时间是否超过默认值5秒,超过则执行健康检查
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) {
//设置空的连接对象,重新选择server重连
reconnectContext = new ReconnectContext(null, false);
} else {
continue;
}
} else {
//更新活动时间
lastActiveTimeStamp = System.currentTimeMillis();
continue;
}
} else {
continue;
}
}
//判断是否按照指定重新连接,为空会选新取server重连
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;
}
}
//执行重新连接,onRequestFail等于true会执行健康检查,false创建新的连接
reconnect(reconnectContext.serverInfo, reconnectContext.onRequestFail);
} catch (Throwable throwable) {
// Do nothing
}
}
});
Connection connectToServer = null;
rpcClientStatus.set(RpcClientStatus.STARTING);
int startUpRetryTimes = rpcClientConfig.retryTimes();
while (startUpRetryTimes > 0 && connectToServer == null) {
try {
startUpRetryTimes--;
//获取服务器地址信息
ServerInfo serverInfo = nextRpcServer();
//连接到服务器
connectToServer = connectToServer(serverInfo);
} catch (Throwable e) {
}
}
if (connectToServer != null) {
//设置连接状态和发送建立连接时间
this.currentConnection = connectToServer;
rpcClientStatus.set(RpcClientStatus.RUNNING);
eventLinkedBlockingQueue.offer(new ConnectionEvent(ConnectionEvent.CONNECTED));
} else {
//启动失败异步启动
switchServerAsync();
}
registerServerRequestHandler(new ConnectResetRequestHandler());
// register client detection request.
registerServerRequestHandler(request -> {
if (request instanceof ClientDetectionRequest) {
return new ClientDetectionResponse();
}
return null;
});
}