nacos注册服务失败_nacos 服务注册 | 源码解析

本文通过源码分析了Nacos服务注册过程,从创建NamingService到初始化各个组件,包括服务地址、事件分发、心跳处理等,详细探讨了可能导致注册失败的环节,帮助理解Nacos内部工作机制。
摘要由CSDN通过智能技术生成

// 创建 NamingService 代码如下

public static NamingService createNamingService(String serverList) throws NacosException{

try {

Class> driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");

Constructor constructor = driverImplClass.getConstructor(String.class);

NamingService vendorImpl = (NamingService)constructor.newInstance(serverList);

return vendorImpl;

} catch (Throwable e) {

throw new NacosException(-400, e.getMessage());

}

}

// 构造函数如下

public NacosNamingService(String serverList){

Properties properties = new Properties();

properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverList);

init(properties);

}

// init 函数如下

private void init(Properties properties){

// 初始化命名空间,如果没有设置,默认值 "public"

namespace = InitUtils.initNamespaceForNaming(properties);

// 初始化服务地址,没有配置 endpoint,使用传入的 ip和端口;

// 如果有配置 endpoint,则将 serverList 置为空字符串

// 域名解析规则:在endpoint端配置网段和环境的映射关系,

// endpoint在接收到客户端的请求之后,根据客户端的来源IP所属网段,计算出该客户端所属环境,

// 然后找到对应环境的IP列表返回给客户端,如上图二

// 可以使用 nginx 的geo 模块实现简单的地址服务器

// 更多参考:https://nacos.io/zh-cn/blog/address-server.html

initServerAddr(properties);

// 用于支持阿里云的 web context

InitUtils.initWebRootContext();

// 初始化缓存目录,默认为 {user.home} + "/nacos/naming/" + namespace;

initCacheDir();

// 初始化日志名,默认为 "naming.log"

initLogName(properties);

// 新建事件分发对象,构造函数如下

eventDispatcher = new EventDispatcher();

// 新建服务代理,如果有配置 endpoint,创建单线程 ScheduledThreadPoolExecutor,定时刷新服务地址列表

serverProxy = new NamingProxy(namespace, endpoint, serverList);

serverProxy.setProperties(properties);

// initClientBeatThreadCount(properties) 初始化客户端心跳检测线程数量,默认值为

// Runtime.getRuntime().availableProcessors() > 1 ? Runtime.getRuntime().availableProcessors() / 2 : 1;

// Runtime.getRuntime().availableProcessors() 返回的处理器数量不一定准确

// 参考: https://blog.csdn.net/zhanghongzheng3213/article/details/83376571

// 新建 BeatReactor 对象,使用 ScheduledThreadPoolExecutor 创建守护线程,定时运行 BeatProcessor

beatReactor = new BeatReactor(serverProxy, initClientBeatThreadCount(properties));

// initPollingThreadCount(properties) 和上面初始化客户端心跳检测线程数量一样

// isLoadCacheAtStart(properties) 启动时是否加载缓存的服务信息,默认值 false

// 新建 HostReactor 对象,HostReactor 创建 ScheduledThreadPoolExecutor 线程池、FailoverReactor 对象和 PushReceiver 对象

// 其中 FailoverReactor 负责故障转移功能,参考下面 FailoverReactor inti() 代码

// PushReceiver 使用 DatagramSocket(UDP),接收消息,如果是 dom 类型,处理接收的数据,

// 如果服务有变动,添加 serviceInfo 到 EventDispatcher 的 changedServices 里面

// 更新本地缓存文件,更新 MetricsMonitor 中服务数量,返回 ack 信息

hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir, isLoadCacheAtStart(properties), initPollingThreadCount(properties));

}

// EventDispatcher 对象构造函数

public EventDispatcher(){

// 新建自定义的单线程线程池,持有一个守护线程

executor = Executors.newSingleThreadExecutor(new ThreadFactory() {

@Override

public Thread newThread(Runnable r){

Thread thread = new Thread(r, "com.alibaba.nacos.naming.client.listener");

thread.setDaemon(true);

return thread;

}

});

// EventDispatcher 有一个 changedServices = new LinkedBlockingQueue()

// 事件分发

// 新建任务 Notifier 监听 changedServices 的消息,定时拉取数据,如果相关服务变更,通知观察者

executor.execute(new Notifier());

}

// BeatProcessor 对象

class BeatProcessor implements Runnable{

@Override

public void run(){

try {

for (Map.Entry entry : dom2Beat.entrySet()) {

BeatInfo beatInfo = entry.getValue();

// volatile 字段 scheduled,防止重复发送心跳

if (beatInfo.isScheduled()) {

continue;

}

beatInfo.setScheduled(true);

executorService.schedule(new BeatTask(beatInfo), 0, TimeUnit.MILLISECONDS);

}

} catch (Exception e) {

NAMING_LOGGER.error("[CLIENT-BEAT] Exception while scheduling beat.", e);

} finally {

executorService.schedule(this, clientBeatInterval, TimeUnit.MILLISECONDS);

}

}

}

class BeatTask implements Runnable{

BeatInfo beatInfo;

public BeatTask(BeatInfo beatInfo){

this.beatInfo = beatInfo;

}

@Override

public void run(){

// 发送心跳,代码如下

long result = serverProxy.sendBeat(beatInfo);

beatInfo.setScheduled(false);

if (result > 0) {

// 设置新的心跳周期,volatile 字段

clientBeatInterval = result;

}

}

}

// 发送心跳,返回值为新的心跳周期,更新心跳周期

public long sendBeat(BeatInfo beatInfo){

try {

if (NAMING_LOGGER.isDebugEnabled()) {

NAMING_LOGGER.debug("[BEAT] {} sending beat to server: {}", namespaceId, beatInfo.toString());

}

Map params = new HashMap(4);

params.put("beat", JSON.toJSONString(beatInfo));

params.put(CommonParams.NAMESPACE_ID, namespaceId);

params.put(CommonParams.SERVICE_NAME, beatInfo.getServiceName());

String result = reqAPI(UtilAndComs.NACOS_URL_BASE + "/instance/beat", params, HttpMethod.PUT);

JSONObject jsonObject = JSON.parseObject(result);

if (jsonObject != null) {

return jsonObject.getLong("clientBeatInterval");

}

} catch (Exception e) {

NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: " + JSON.toJSONString(beatInfo), e);

}

return 0L;

}

// FailoverReactor 对象的初始化方法如下

public void init(){

// 定时检测故障转移模式是否开启,如果开启,则新建 FailoverFileReader 任务并调用 run()

// run 方法会读取缓存目录下 failover 文件夹的文件

executorService.scheduleWithFixedDelay(new SwitchRefresher(), 0L, 5000L, TimeUnit.MILLISECONDS);

// 一天备份一次服务信息到 failover 文件夹

executorService.scheduleWithFixedDelay(new DiskFileWriter(), 30, DAY_PERIOD_MINUTES, TimeUnit.MINUTES);

// backup file on startup if failover directory is empty.

executorService.schedule(new Runnable() {

@Override

public void run(){

try {

File cacheDir = new File(failoverDir);

if (!cacheDir.exists() && !cacheDir.mkdirs()) {

throw new IllegalStateException("failed to create cache dir: " + failoverDir);

}

File[] files = cacheDir.listFiles();

if (files == null || files.length <= 0) {

new DiskFileWriter().run();

}

} catch (Throwable e) {

NAMING_LOGGER.error("[NA] failed to backup file on startup.", e);

}

}

}, 10000L, TimeUnit.MILLISECONDS);

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值