Nacos配置中心原理(二)服务端部分

上文回顾

Nacos客户端在启动的时候,初始化了一个长轮询任务,检查服务端的数据是否发生变更,如果没有发生变更,则服务端会挂起请求,29.5秒后比对数据将结果返回,如果发生变更,则马上对比数据,将变更的groupKey返回,客户端拿到groupKey后再去服务端获取最新值。将最新值设置到cacheData中拿到cacheDate的监听器,通知configServer数据发生变更。这就是客户端的实现过程。

服务端

接下来重点分析服务端如何实现,并且带着几个问题去寻找答案。

  • 客户端请求超时时间为什么要设置30s

  • 为什么配置更新客户端马上会得到响应

首先我们从客户端发送长轮询请求这个方法着手,发送post请求的代码入下

HttpRestResult<String> result = agent
      .httpPost(Constants.CONFIG_CONTROLLER_PATH + "/listener", 
                headers, params, agent.getEncode(),readTimeoutMs);

可知请求路径为  /v1/cs/configs/listener, 在服务端找到对应的controller方法,如下图,有一点需要注意的是,这个方法没有返回值,所以只能通过response向客户端写数据。

进入doPollingConfig方法,判断如果是长轮询则走长轮询方法,否则走短轮询方法,重点关注长轮询

进入到 addLongPollingClient 方法中

重点看下ClientLongPolling这个类,一个是属性,另一个是run方法

上面这部分代码主要功能是将客户端的请求封装成一个长轮询任务,将任务放到延时队列中,延时时间29.5秒,到期后从队列中取出任务执行,执行的逻辑为比对客户端和服务端MD5值,将变化的groupKey返回给客户端。这里有一个对象值得注意,那就是 allSubs ,allSubs 也存储了该长轮询任务,就是为了当数据变更的时候,能够从这个allSubs中拿到对应的任务,进而拿到里面的asyncTimeoutFuture和asyncContext,将响应提前返回,同时取消延时队列中的这个任务。

配置变更

当服务端接收到变更配置的请求时,服务端会做什么呢?我们在控制台修改数据定位到请求路径是 /nacos/v1/cs/configs 请求方式为post ,这样就定位到服务端的相关controller,com.alibaba.nacos.config.server.controller.ConfigController#publishConfig,跟进这个方法看看做了什么。

这个方法很长,获取请求参数和校验请求参数就没有截进来,只截取重要的部分,从代码可知,数据变更的操作是先保存再通知

看下通知方法:ConfigChangePublisher

.notifyConfigChange(new ConfigDataChangeEvent(false, dataId, group, tenant, time.getTime()));主要是创建一个 改变事件对象,将这个对象通过NotifyCenter发布出去。

这个方法较深,一步一步跟进去,发现最终调用了com.alibaba.nacos.common.notify.DefaultPublisher#publish

可以看到最终将事件放到了阻塞队列中,这应该是生产者消费者模式,继续寻找queue对象在哪里取出事件消费

首先在NotifyCenter类中有一个静态代码块,会初始化DefaultPublisher这个对象,而DefaultPublisher对象实现了Thread,表明他是一个线程对象,在其内部重写的run方法中,我们可以看到queue.take方法,入下

receiveEvent方法是遍历所有订阅者,忽略掉过期事件,通知订阅者该事件

进入notifySubscriber方法,发现创建了一个任务,然后执行该事件

 回调到注册订阅者的地方

执行任务是创建一个DataChangeTask对象,然后从allSubs中找到与这个对象的groupKey相同的任务,先将这个任务从allSubs中移除,再将这个groupKey返回给客户端。

最后看下clientSub.sendResponse(Arrays.asList(groupKey));这个方法做了什么

至此,服务端处理逻辑就完成了

总结:

  1. 接收到客户端的请求,将请求封装成clientLongPolling任务提交到延时调度线程池,线程池延时时间为29.5s,同时将这个clientLongPolling任务添加到一个allSubs集合中

  2. 在29.5秒这段期间内,没有数据变更,则29.5s后开始执行clientLongPolling任务,首先将clientLongPolling任务从allSubs中移除,再检查客户端的MD5和服务端的MD5值是否一致,将不一致的返回。

  3. 29.5s内服务端接收到数据更新操作,发布一个异步通知,就是将这个请求添加到一个阻塞队列,有一个线程从这个队列中取数据消费,消费逻辑是比对客户端和服务端的MD5是否一致,然后将比对结果返回,最后从allSub中取出相对应的clientLongPolling任务,将这个任务取消,这样延时队列到期将不会做任何操作。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值