本章分析一下nacos集群之间nacos服务器上线,下线原理

  • 每5秒运行定时任务ServerListManager.ServerListUpdater获取新上线的节点或下线的节点
  • 每2秒运行定时任务ServerListManager.ServerStatusReporter发送心跳数据到集群中的每个节点
  • 每5秒运行定时任务ServerStatusManager.ServerStatusUpdater检测和控制本地服务器的工作状态
  • 当前节点收到其他节点发送心跳数据后更新该节点最后心跳时间戳的Map对象
  • 执行ServerStatusReporter时候运行checkDistroHeartbeat方法更新健康的服务器节点集合

Nacos集群代码主要相关类:

// Nacos集群的成员节点
com.alibaba.nacos.naming.cluster.servers.Server; // 用于全局刷新和操作服务器列表的管理器。
com.alibaba.nacos.naming.cluster.ServerListManager; // 检测和控制本地服务器的工作状态
com.alibaba.nacos.naming.cluster.ServerStatusManager //向其他服务器报告本地服务器状态
com.alibaba.nacos.naming.misc.ServerStatusSynchronizer
  1. 启动定时任务
/**
* The manager to globally refresh and operate server list.
*/
@Component("serverListManager")
public class ServerListManager {
//如果服务器节点有变化需要通知的对象
private List<ServerChangeListener> listeners = new ArrayList<>();
//存储集群中所有服务器节点
private List<Server> servers = new ArrayList<>();
//存储集群中健康的服务器节点
private List<Server> healthyServers = new ArrayList<>();
//根据site=key集群节点集合
private Map<String, List<Server>> distroConfig = new ConcurrentHashMap<>();
//存储各个节点最后一次心跳时间戳
private Map<String, Long> distroBeats = new ConcurrentHashMap<>(16); //服务启动时候启动两个定时任务
@PostConstruct
public void init() {
//下面代码相当于:executorService.scheduleAtFixedRate(new ServerListUpdater(), 0, NACOS_SERVER_LIST_REFRESH_INTERVAL=5000, TimeUnit.MILLISECONDS);
GlobalExecutor.registerServerListUpdater(new ServerListUpdater()); //下面代码相当于:SERVER_STATUS_EXECUTOR.schedule(new ServerStatusReporter(), 2000, TimeUnit.MILLISECONDS);
GlobalExecutor.registerServerStatusReporter(new ServerStatusReporter(), 2000);
}
} @Service
public class ServerStatusManager {
private ServerStatus serverStatus = ServerStatus.STARTING; @PostConstruct
public void init() {
//executorService.scheduleAtFixedRate(runnable, 0, SERVER_STATUS_UPDATE_PERIOD=5000, TimeUnit.MILLISECONDS);
GlobalExecutor.registerServerStatusUpdater(new ServerStatusUpdater());
}
}
  1. ServerListManager.ServerListUpdater定时刷新本机集群节点列表的变化

/**
* ServerListManager的内部类,定时刷新本机集群节点列表的变化
*/
public class ServerListUpdater implements Runnable { @Override
public void run() {
try {
/**
* refreshServerList方法:
* 1. 如果是STANDALONE_MODE返回当前节点实例
* 2. 从cluster.conf配置文件中读取节点列表 (readClusterConf())返回
* 3. 如果为空则从System.getenv("naming_self_service_cluster_ips") 读取节点列表返回
*/
List<Server> refreshedServers = refreshServerList();
List<Server> oldServers = servers; if (CollectionUtils.isEmpty(refreshedServers)) {
Loggers.RAFT.warn("refresh server list failed, ignore it.");
return;
} boolean changed = false;
//新增加集群节点
List<Server> newServers = (List<Server>) CollectionUtils.subtract(refreshedServers, oldServers);
if (CollectionUtils.isNotEmpty(newServers)) {
servers.addAll(newServers);
changed = true;
}
//移除的集群节点
List<Server> deadServers = (List<Server>) CollectionUtils.subtract(oldServers, refreshedServers);
if (CollectionUtils.isNotEmpty(deadServers)) {
servers.removeAll(deadServers);
changed = true;
}
//如果服务器节点有变化,通知其他类
if (changed) {
notifyListeners();
} } catch (Exception e) {
Loggers.RAFT.info("error while updating server list.", e);
}
}
  1. ServerListManager.ServerStatusReporter定时发送心跳到集群中的其他节点

    报文格式:SITE#IP:port#currentTimeMillis#weight\r\n
      /**
* ServerListManager的内部类,定时发送心跳到集群中的其他节点
*/
private class ServerStatusReporter implements Runnable { @Override
public void run() {
try {
//
checkDistroHeartbeat(); int weight = Runtime.getRuntime().availableProcessors() / 2;
if (weight <= 0) weight = 1;
long curTime = System.currentTimeMillis();
String status = LOCALHOST_SITE + "#" + NetUtils.localServer() + "#" + curTime + "#" + weight + "\r\n"; //send status to itself
onReceiveServerStatus(status); //集群所有节点(除了本机)发送心跳
List<Server> allServers = getServers();
for (com.alibaba.nacos.naming.cluster.servers.Server server : allServers) {
if (server.getKey().equals(NetUtils.localServer())) {
continue;
}
Message msg = new Message();
msg.setData(status);
synchronizer.send(server.getKey(), msg);
}
} catch (Exception e) {
Loggers.SRV_LOG.error("[SERVER-STATUS] Exception while sending server status", e);
} finally {
GlobalExecutor.registerServerStatusReporter(this, switchDomain.getServerStatusSynchronizationPeriodMillis());
} }
}
} /**
* Report local server status to other server
* @author nacos
*/
public class ServerStatusSynchronizer implements Synchronizer {
@Override
public void send(final String serverIP, Message msg) {
final Map<String, String> params = new HashMap<String, String>(2);
params.put("serverStatus", msg.getData());
//上报地址
String url = "http://serverIP:8848 + "/nacos/v1/ns/operator/server/status";
HttpClient.asyncHttpGet(url, null, params, new AsyncCompletionHandler() {
@Override
public Integer onCompleted(Response response) throws Exception {
if (response.getStatusCode() != HttpURLConnection.HTTP_OK) {
return 1;
}
return 0;
}
});
}
}
  1. 本地节点接收其他节点发送心跳后处理,主要代码
 @RequestMapping("/server/status")
public String serverStatus(@RequestParam String serverStatus) {
serverListManager.onReceiveServerStatus(serverStatus);
return "ok";
} public synchronized void onReceiveServerStatus(String config) {
List<Server> tmpServerList = new ArrayList<>();
//site:ip:lastReportTime:weight
String[] params = config.split("#");
Server server = new Server();
server.setSite(params[0]);
server.setIp(params[1].split(UtilsAndCommons.IP_PORT_SPLITER)[0]);
server.setServePort(Integer.parseInt(params[1].split(UtilsAndCommons.IP_PORT_SPLITER)[1]));
server.setLastRefTime(Long.parseLong(params[2])); //如果服务器的两个报告间隔大于distroServerExpiredMillis,则认为服务器已过期。
Long lastBeat = distroBeats.get(server.getKey());
long now = System.currentTimeMillis();
if (null != lastBeat) {
server.setAlive(now - lastBeat < switchDomain.getDistroServerExpiredMillis());
}
//更新当前节点最后一次心跳时间
distroBeats.put(server.getKey(), now);
Date date = new Date(Long.parseLong(params[2]));
server.setLastRefTimeStr(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));
server.setWeight(params.length == 4 ? Integer.parseInt(params[3]) : 1); //如果当前节点在列表中更新为现在数据
List<Server> list = distroConfig.get(server.getSite());
for (Server s : list) {
String serverId = s.getKey() + "_" + s.getSite();
String newServerId = server.getKey() + "_" + server.getSite(); if (serverId.equals(newServerId)) {
tmpServerList.add(server);
continue;
}
tmpServerList.add(s);
}
//如果没在列表中则添加到列表中
if (!tmpServerList.contains(server)) {
tmpServerList.add(server);
}
distroConfig.put(server.getSite(), tmpServerList); }
  1. 检查心跳时间戳确定节点是否Alive
private void checkDistroHeartbeat() {
List<Server> newHealthyList = new ArrayList<>(servers.size());
long now = System.currentTimeMillis();
for (Server s: servers) {
Long lastBeat = distroBeats.get(s.getKey());
if (null == lastBeat) {
continue;
}
s.setAlive(now - lastBeat < switchDomain.getDistroServerExpiredMillis());
} //local site servers
List<String> allLocalSiteSrvs = new ArrayList<>();
for (Server server : servers) {
server.setAdWeight(switchDomain.getAdWeight(server.getKey()) == null ? 0 : switchDomain.getAdWeight(server.getKey()));
for (int i = 0; i < server.getWeight() + server.getAdWeight(); i++) {
if (!allLocalSiteSrvs.contains(server.getKey())) {
allLocalSiteSrvs.add(server.getKey());
}
if (server.isAlive() && !newHealthyList.contains(server)) {
newHealthyList.add(server);
}
}
} Collections.sort(newHealthyList);
if (!CollectionUtils.isEqualCollection(healthyServers, newHealthyList)) {
healthyServers = newHealthyList;
notifyListeners();
}
}
  1. ServerStatusManager.ServerStatusUpdater 检测和控制本地服务器的工作状态

public class ServerStatusUpdater implements Runnable { @Override
public void run() {
refreshServerStatus();
}
} private void refreshServerStatus() {
if (StringUtils.isNotBlank(switchDomain.getOverriddenServerStatus())) {
serverStatus = ServerStatus.valueOf(switchDomain.getOverriddenServerStatus());
return;
} if (consistencyService.isAvailable()) {
serverStatus = ServerStatus.UP;
} else {
serverStatus = ServerStatus.DOWN;
}
}

nacos集群的更多相关文章

  1. Nacos集群环境的搭建与配置

    Nacos集群环境的搭建与配置 集群搭建 一.环境: 服务器环境:CENTOS-7.4-64位 三台服务器IP:192.168.102.57:8848,192.168.102.59:8848,192. ...

  2. Spring Cloud Alibaba | Nacos集群部署

    目录 Spring Cloud Alibaba | Nacos集群部署 1. Nacos支持三种部署模式 2. 集群模式下部署Nacos 2.1 架构图 2.2 下载源码或者安装包 2.3 配置集群配 ...

  3. Nacos(九):Nacos集群部署和遇到的问题

    前言 前面的系列文章已经介绍了Nacos的如何接入SpringCloud,以及Nacos的基本使用方式 之前的文章中都是基于单机模式部署进行讲解的,本文对Nacos的集群部署方式进行说明 环境准备 J ...

  4. Nacos 集群部署

    关于nacos 集群部署,网上的示例往往不全或不可用,而官方的教程太简单了.官方也提供了一个 docker  + nacos 的伪集群的 部署示例.但毕竟是 伪, 不能实际生产使用. 全网就几乎就没有 ...

  5. nacos集群搭建

    nacos介绍 Nacos 支持基于 DNS 和基于 RPC 的服务发现(可以作为springcloud的注册中心).动态配置服务(可以做配置中心).动态 DNS 服务. 1.从官网下载nacos压缩 ...

  6. java架构之路-(微服务专题)nacos集群精讲实战

    上次回顾: 上次博客,我们主要说了微服务的发展历程和nacos集群单机的搭建,单机需要-m standalone启动,集群建议使用nginx做一下反向代理,自行保证mysql和ngxin的高可用. 本 ...

  7. CentOS 7 Nacos 集群搭建

    环境 CentOS 7.4 MySQL 5.7 nacos-server-1.1.2 本次安装的软件全部在 /home/javateam 目录下. MySQL 安装 首先下载 rpm 安装包,地址:h ...

  8. windows下Nacos集群搭建与nginx集成

    前言: nacos集群至少需要三个(一般为奇数个)nacos实 例,其前面顶nginx,外界入口从nginx入 一.windows下Nacos集群搭建 将Nacos的解压包复制分成3份,分别是: na ...

  9. nacos 集群搭建

    nacos 集群搭建 1.单机部署 从nacos官网下载zip/tar包,https://github.com/alibaba/nacos/releases/tag/2.0.2 解压后即可启动 外置数 ...

随机推荐

  1. idea使用maven的打包工具package不会打上主类解决方法

  2. CTGU_训练实录

    前言 之前做题都没有感觉,慢慢出去比赛后,打Codeforces,看别的人博客,知乎上的讨论,慢慢的对算法有一些自己的思考.特写是最近看知乎上别人说的Dijkstra利用水流去理解,LCA的学习,感觉 ...

  3. POJ-2411 Mondriann's Dream (状压DP)

    求把\(N*M(1\le N,M \le 11)\) 的棋盘分割成若干个\(1\times 2\) 的长方形,有多少种方案.例如当 \(N=2,M=4\)时,共有5种方案.当\(N=2,M=3\)时, ...

  4. DFS——求图的连通性问题

    DFS作为一个竞赛必学的一个知识点,怎么说我都得写一下 遍历就相当于爆搜,只不过是搜的方式比较规整罢了. 深度优先遍历:为了避免重复访问某个顶点,可以设一个标志数组vis[i],未访问时值为0,访问一 ...

  5. 回溯法、子集树、排列树、满m叉树

    显示图: 明确给出了图中的各顶点及边 隐式图: 仅给出初始节点.目标节点及产生子节点的条件(一般有问题提议隐含给出)的情况下,构造一个图. 回溯法: 从初始状态出发,在隐式图中以深度优先的方式搜索问题 ...

  6. HDU5213 Lucky【容斥+莫队】

    HDU5213 Lucky 题意: 给出\(N\)个数和\(k\),有\(m\)次询问,每次询问区间\([L1,R1]\)和区间\([L2,R2]\)中分别取一个数能相加得到\(k\)的方案数 题解: ...

  7. Codeforces Global Round 8 D. AND, OR and square sum(位运算)

    题目链接:https://codeforces.com/contest/1368/problem/D 题意 给出一个大小为 $n$ 的数组 $a$,每次可以选两个下标不同的元素,一个赋为二者相与的值, ...

  8. Codeforces Round #340 (Div. 2) E. XOR and Favorite Number

    time limit per test 4 seconds memory limit per test 256 megabytes input standard input output standa ...

  9. vi、wc、gzip、bzip2、tar、yum安装、dpek、用户信息操作等命令

    命令模式 输入"dd"即可将这一行删除 按下"p"即可粘贴 插入模式: a:从光标这个位置之后插入 A:在行尾插入 i:从光标之前插入 I:行首插入 o:在光标 ...

  10. kafka——集群安裝部署(自带zookeeper)

    kafka系列文章 第一章 linux单机安装kafka 第二章 kafka--集群安裝部署(自带zookeeper) 一.kafka简介 kafka官网:http://kafka.apache.or ...