「Nacos源码之配置管理 六」集群模式下服务器之间是如何互相感知
suiw9 2024-10-29 16:42 17 浏览 0 评论
Part1前言
我们用Nacos当配置中心的时候,上一篇文章中【Nacos源码之配置管理 五】为什么把配置文件Dump到磁盘中 知道了,所有的配置文件都会Dump到服务器的本地磁盘中, 那么集群模式下:
- [x] 服务器之间如何彼此感知发现的?
- [x] 当某一台机器宕机挂掉之后怎么处理的?
- [x] 如何获取本地Ip
- [x] 获取服务器列表
- [x] 服务器列表健康检查
阅读完本文,会带你对上面的问题有个很清晰的认知;
Part2集群模式
我们先集群模式启动,开启调试
1集群模式启动
- 配置集群机器列表;文件distribution/conf/cluster.conf 中配置所有的机器列表;IP:PORT的形式;例如
- 执行打包命令
mvn -Prelease-nacos clean install -U -Dmaven.test.skip=true
- 打包完毕,执行启动脚本
sh distribution/target/nacos-server-{version}/nacos/bin/startup.sh
启动之后就可以进行远程调试了;怎么调试可以参考【Nacos源码之配置管理 一】阅读源码第一步,本地启动Nacos
2ServerListService 服务器列表
在看源码之前先说明一下Nacos读取服务器列表的两种方式
方式一:本地读取cluster.conf
每台服务器本地维护一份集群配置文件 cluster.conf
方式二:读取统一配置中心配置文件
在这里插入图片描述
ApplicationListener 监听器
ServerListService实现了SpringBoot的扩展类ApplicationListener;并且事件源是WebServerInitializedEvent: 是WebServer初始化的事件;通过WebServerInitializedEvent可以拿到WeServer的实例;通过WeServer.getPort()拿到启动的端口; 关于Spring的事件监听可以看 【Nacos源码之配置管理 二】Nacos中的事件发布与订阅--观察者模式
在ServerListService中就是通过这个获取Server的端口号
这个ServerListService是服务器列表,这里面保存着所有的服务器信息; 那么是如何获取所有服务器信息的呢?;接下来分析源码
初始化方法init
这个初始化的init方法里面做了什么事情呢?
Spring启动时,执行@PostConstruct 注解的初始化方法;
@Service
public class ServerListService implements ApplicationListener<WebServerInitializedEvent> {
@Autowired
private Environment env;
@Autowired
private ServletContext servletContext;
private int port;
@PostConstruct
public void init() {
serverPort = System.getProperty("nacos.server.port", "8848");
String envDomainName = System.getenv("address_server_domain");
if (StringUtils.isBlank(envDomainName)) {
domainName = System.getProperty("address.server.domain", "jmenv.tbsite.net");
} else {
domainName = envDomainName;
}
String envAddressPort = System.getenv("address_server_port");
if (StringUtils.isBlank(envAddressPort)) {
addressPort = System.getProperty("address.server.port", "8080");
} else {
addressPort = envAddressPort;
}
addressUrl = System.getProperty("address.server.url",
servletContext.getContextPath() + "/" + RunningConfigUtils.getClusterName());
addressServerUrl = "http://" + domainName + ":" + addressPort + addressUrl;
envIdUrl = "http://" + domainName + ":" + addressPort + "/env";
defaultLog.info("ServerListService address-server port:" + serverPort);
defaultLog.info("ADDRESS_SERVER_URL:" + addressServerUrl);
isHealthCheck = PropertyUtil.isHealthCheck();
maxFailCount = PropertyUtil.getMaxHealthCheckFailCount();
try {
String val = null;
val = env.getProperty("useAddressServer");
if (val != null && FALSE_STR.equals(val)) {
isUseAddressServer = false;
}
fatalLog.warn("useAddressServer:{}", isUseAddressServer);
} catch (Exception e) {
fatalLog.error("read application.properties wrong", e);
}
GetServerListTask task = new GetServerListTask();
task.run();
if (null == serverList || serverList.isEmpty()) {
fatalLog.error("########## cannot get serverlist, so exit.");
throw new RuntimeException("cannot get serverlist, so exit.");
} else {
TimerTaskService.scheduleWithFixedDelay(task, 0L, 5L, TimeUnit.SECONDS);
}
httpclient.start();
CheckServerHealthTask checkServerHealthTask = new CheckServerHealthTask();
TimerTaskService.scheduleWithFixedDelay(checkServerHealthTask, 0L, 5L, TimeUnit.SECONDS);
}
}
- 获取服务端口 serverPort; 可以通过设置Jvm属性nacos.server.port设置这个端口,例如启动脚本加上-Dnacos.server.port=8848;默认不填写情况端口是8848;
- 获取方式二中的域名地址domainName ,读取环境变量System.getenv("address_server_domain") ;如果环境变量没有获取到也可以通过Jvm属性 System.getProperty("address.server.domain", "jmenv.tbsite.net") 配置这个属性;如果都没有默认是jmenv.tbsite.net, 如果集群的机器列表是配置在本地(上面的方式一)其实这个domainName就没有什么作用,如果是方式二; 才会使用到这个;这个就是服务器列表配置中心的域名
- 获取方式二中的服务器列表配置中心的端口addressPort ,先从环境变量中获取 System.getenv("address_server_port"),如果没有则从Jvm属性里面获取System.getProperty("address.server.port", "8080"); ;如果都没有配置默认就是8080;
- 获取方式二中的请求地址addressUrl ;默认/nacos/serverlist ;可以通过Jvm设置属性-Daddress.server.url=地址
- 最终的请求地址是 "http://" + domainName + ":" + addressPort + addressUrl;
- 根据配置文件中的属性useAddressServer=true/false 判断是否使用方式二这种服务器列表配置中心的形式;useAddressServer默认就是true
- 获取服务器列表 GetServerListTask ;在执行一次之后,开始每隔5秒执行一次
- 每隔5秒做一次服务器列表健康检查 CheckServerHealthTask
如果本地配置了cluster.conf,也配置了useAddressServer=true 会读取哪个?
就算 6 中的useAddressServer=true 也不一定会去请求读取远程的服务器列表;如果本地也配置了 cluster.conf的话,那么会优先读取本地的配置的; 如果本地的读取不到列表,才会去读取远程的服务器列表
本地cluster.conf的路径是到底在哪里?
private static String getClusterConfFilePath() {
return NACOS_HOME + File.separator + "conf" + File.separator + "cluster.conf";
}
{NACOS_HOME}/conf/cluster.conf
那NACOS_HOME是什么路径? 我在之前的文章 【Nacos源码之配置管理 四】DumpService如何将配置文件全部Dump到磁盘中 有讲过NACOS_HOME 的地址和配置;打开文章全文搜索一下NACOS_HOME 就可以看到;
getApacheServerList() 获取服务器列表的方法
这个方法就是获取服务器列表的方法的具体细节,代码我就不放出来,我直接说流程;
- 优先从本地文件读取服务列表,如果读取到了直接返回;
- 如果1中没有读取到,则判断useAddressServer=true;如果=true,则读取远程服务器中的服务器列表,如果读取到了直接返回;
- 如果2中执行了maxFailCount=12次还是没有获取到,则标识 isAddressServerHealth = false;;说明远程服务器挂掉了;
- 如果本地没有数据,并且useAddressServer=false;那么就会把自己的Ip加入到服务器列表;也就是说只有一台机器;
- 这个方法只是获取运维配置的集群服务器列表;并没有去检验每个集群列表的机器是否健康! 如果使用方式二;远程配置中心服务器不可访问那么返回的是一个空列表;
如何获取自己的Ip
上面的4中说到,把自己的Ip放入到服务器列表,这个自己的Ip是多少?
- 先看看Jvm属性配置了nacos.server.ip=IP地址没有;如果有就是它;
- 如果1中没有,则看看配置文件application.properties中有没有属性nacos.inetutils.ip-address=IP地址;如果有就是它
- 如果还没有,那判断是否优先使用hostname;preferHostnameOverIp 的判断逻辑是;先判断JVM属性有没有配置nacos.preferHostnameOverIp=true/false;如果false,再去判断配置文件application.properties中有没有属性 nacos.inetutils.prefer-hostname-over-ip=true/false;如果有的话 就优先获取hostname; inetAddress.getHostName();
- 否则的话 就获取所有网卡中第一个非回环地址
selfIp = findFirstNonLoopbackAddress().getHostAddress();
就是不会找到 127.0.0.1这样的回环地址; 具体代码在类 InetUtils中;
GetServerListTask 每五秒重新获取一次
每五秒执行一次这个任务 updateIfChanged方法见名思意就是如果服务器列表有更改(例如新上线,下线,宕机)的时候就要及时的把服务器列表更新一下;
class GetServerListTask implements Runnable {
@Override
public void run() {
try {
updateIfChanged(getApacheServerList());
} catch (Exception e) {
defaultLog.error("[serverlist] failed to get serverlist, " + e.toString(), e);
}
}
}
- getApacheServerList()获取最新的服务器列表配置newList ; (这个时候并不知道这些服务器是否健康)
- 在ServerListService类中有 List全局属性 serverListUnhealth; 存放的是当前配置中(当前配置意思是,如果配置中移除了某个机器,那么这个对应的不健康服务器列表也要移除)不健康的服务器列表; (这个属性由谁维护,就是CheckServerHealthTask的做的事情)
- 如果最新的服务器列表newList中的Ip不存在在serverListUnhealth中了,就从serverListUnhealth中把这个Ip移除掉 ((可能的情况就是,运维知道某台服务挂掉了,就从服务器配置文件中把这个不健康的Ip手动移除;、)
- 发送服务器变更事件EventDispatcher.fireEvent(new ServerlistChangeEvent()); ;但是系统中还暂时没有监听这个事件的监听器;
至于EventDispatcher.fireEvent(new ServerlistChangeEvent()); 不懂的可以看我之前的文章 【Nacos源码之配置管理 二】Nacos中的事件发布与订阅--观察者模式
一句话总结这个作用: 每五秒查询最新的服务器列表配置,如果配置中把之前不健康的移除掉了,则也从属性serverListUnhealth中移除掉;
CheckServerHealthTask 服务器健康检查
系统会每隔5秒执行一次服务器健康检查,那么是怎么检查是否健康呢?其实就是给所有的服务器列表发起一个Http请求; 根据返回值判断是否健康
private void checkServerHealth() {
long startCheckTime = System.currentTimeMillis();
for (String serverIp : serverList) {
// Compatible with old codes,use status.taobao
String url = "http://" + serverIp + servletContext.getContextPath() + Constants.HEALTH_CONTROLLER_PATH;
// "/nacos/health";
HttpGet request = new HttpGet(url);
httpclient.execute(request, new AyscCheckServerHealthCallBack(serverIp));
}
long endCheckTime = System.currentTimeMillis();
long cost = endCheckTime - startCheckTime;
defaultLog.debug("checkServerHealth cost: {}", cost);
}
代码中可以看到,最终是发起了一个http请求;这个请求的链接是
String url = "http://" + serverIp + servletContext.getContextPath() + Constants.HEALTH_CONTROLLER_PATH;
解析得到的链接是 http://ip:port/nacos/v1/cs/health 一句话说就是,访问每个服务器列表的nacos/v1/cs/health 方法;包括自己的; 最终请求的是HealthController 这个类的getHealth方法
Http异步请求回调
上面的
httpclient.execute(request, new AyscCheckServerHealthCallBack(serverIp));
是一个异步请求;AyscCheckServerHealthCallBack实现了FutureCallback类;
class AyscCheckServerHealthCallBack implements FutureCallback<HttpResponse> {
private String serverIp;
public AyscCheckServerHealthCallBack(String serverIp) {
this.serverIp = serverIp;
}
@Override
public void completed(HttpResponse response) {
if (response.getStatusLine().getStatusCode() == HttpServletResponse.SC_OK) {
serverIp2unhealthCount.put(serverIp, 0);
if (serverListUnhealth.contains(serverIp)) {
serverListUnhealth.remove(serverIp);
}
HttpClientUtils.closeQuietly(response);
}
}
@Override
public void failed(Exception ex) {
Integer failCount = serverIp2unhealthCount.get(serverIp);
failCount = failCount == null ? Integer.valueOf(0) : failCount;
failCount++;
serverIp2unhealthCount.put(serverIp, failCount);
if (failCount > maxFailCount) {
if (!serverListUnhealth.contains(serverIp)) {
serverListUnhealth.add(serverIp);
}
defaultLog.error("unhealthIp:{}, unhealthCount:{}", serverIp, failCount);
MetricsMonitor.getUnhealthException().increment();
}
}
}
上面实现的是,当请求成功(返回码:200)说明服务器健康; 如果之前是不健康的状态,则将其从serverListUnhealth中移除; 如果请求失败了;则将请求的服务器加入到serverListUnhealth中;
注意:这里的检查是否健康是判断 返回码:200; 并不是 HealthController这个类的 getHealth 方法返回的值; (能够请求到接口,说明服务器是健康的;并不关心方法返回了什么数据)
当某一台机器宕机挂掉之后怎么处理的
当服务器挂掉或者宕机; 每五秒的健康检查会检查到服务宕机了,会将其剔除;
相关推荐
- 5款Syslog集中系统日志常用工具对比推荐
-
一、为何要集中管理Syslog?Syslog由Linux/Unix系统及其他网络设备生成,广泛分布于整个网络。因其包含关键信息,可用于识别网络中的恶意活动,所以必须对其进行持续监控。将Sys...
- 跨平台、多数据库支持的开源数据库管理工具——DBeaver
-
简介今天给大家推荐一个开源的数据库管理工具——DBeaver。它支持多种数据库系统,包括Mysql、Oracle、PostgreSQL、SLQLite、SQLServer等。DBeaver的界面友好...
- 强烈推荐!数据库管理工具:Navicat Premium 16.3.2 (64位)
-
NavicatPremium,一款集数据迁移、数据库管理、SQL/查询编辑、智能设计、高效协作于一体的全能数据库开发工具。无论你是MySQL、MariaDB、MongoDB、SQLServer、O...
- 3 年 Java 程序员还玩不转 MongoDB,网友:失望
-
一、什么场景使用MongoDB?...
- 拯救MongoDB管理员的GUI工具大赏:从菜鸟到极客的生存指南
-
作为一名在NoSQL丛林中披荆斩棘的数据猎人,没有比GUI工具更称手的瑞士军刀了。本文将带你围观五款主流MongoDB管理神器的特性与暗坑,附赠精准到扎心的吐槽指南一、MongoDBCompass:...
- mongodb/redis/neo4j 如何自己打造一个 web 数据库可视化客户端?
-
前言最近在做neo4j相关的同步处理,因为产线的可视化工具短暂不可用,发现写起来各种脚本非常麻烦。...
- solidworks使用心得,纯干货!建议大家收藏
-
SolidWorks常见问题...
- 统一规约-关乎数字化的真正实现(规范统一性)
-
尽管数字化转型的浪潮如此深入人心,但是,对于OPCUA和TSN的了解却又甚少,这难免让人质疑其可实现性,因为,如果缺乏统一的语义互操作规范,以及更为具有广泛适用的网络与通信,则数字化实际上几乎难以具...
- Elasticsearch节点角色配置详解(Node)
-
本篇文章将介绍如下内容:节点角色简介...
- 产前母婴用品分享 篇一:我的母婴购物清单及单品推荐
-
作者:DaisyH8746在张大妈上已经混迹很久了,有事没事看看“什么值得买”已渐渐成了一种生活习惯,然而却从来没有想过自己要写篇文章发布上来,直到由于我产前功课做得“太过认真”(认真到都有点过了,...
- 比任何人都光彩照人的假期!水润、紧致的肌肤护理程序
-
图片来源:谜尚愉快的假期临近了。身心振奋的休假季节。但是不能因为这种心情而失去珍贵的东西,那就是皮肤健康。炙热的阳光和强烈的紫外线是使我们皮肤老化的主犯。因此,如果怀着快乐的心情对皮肤置之不理,就会使...
- Arm发布Armv9边缘AI计算平台,支持运行超10亿参数端侧AI模型
-
中关村在线2月27日消息,Arm正式发布Armv9边缘人工智能(AI)计算平台。据悉,该平台以全新的ArmCortex-A320CPU和领先的边缘AI加速器ArmEthos-U85NPU为核心...
- 柔性——面向大规模定制生产的数字化实现的基本特征
-
大规模定制生产模式的核心是柔性,尤其是体现在其对定制的要求方面。既然是定制,并且是大规模的定制,对于制造系统的柔性以及借助于数字化手段实现的柔性,就提出了更高的要求。面向大规模定制生产的数字化业务管控...
- 创建PLC内部标准——企业前进的道路
-
作者:FrankBurger...
- 标准化编程之 ----------- 西门子LPMLV30测试总结
-
PackML乃是由OMAC开发且被ISA所采用的自动化标准TR88.00.02,能够更为便捷地传输与检索一致的机器数据。PackML的主要宗旨在于于整个工厂车间倡导通用的“外观和感觉”,...
你 发表评论:
欢迎- 一周热门
-
-
Linux:Ubuntu22.04上安装python3.11,简单易上手
-
宝马阿布达比分公司推出独特M4升级套件,整套升级约在20万
-
MATLAB中图片保存的五种方法(一)(matlab中保存图片命令)
-
别再傻傻搞不清楚Workstation Player和Workstation Pro的区别了
-
Linux上使用tinyproxy快速搭建HTTP/HTTPS代理器
-
如何提取、修改、强刷A卡bios a卡刷bios工具
-
Element Plus 的 Dialog 组件实现点击遮罩层不关闭对话框
-
日本组合“岚”将于2020年12月31日停止团体活动
-
SpringCloud OpenFeign 使用 okhttp 发送 HTTP 请求与 HTTP/2 探索
-
tinymce 号称富文本编辑器世界第一,大家同意么?
-
- 最近发表
-
- 5款Syslog集中系统日志常用工具对比推荐
- 跨平台、多数据库支持的开源数据库管理工具——DBeaver
- 强烈推荐!数据库管理工具:Navicat Premium 16.3.2 (64位)
- 3 年 Java 程序员还玩不转 MongoDB,网友:失望
- 拯救MongoDB管理员的GUI工具大赏:从菜鸟到极客的生存指南
- mongodb/redis/neo4j 如何自己打造一个 web 数据库可视化客户端?
- solidworks使用心得,纯干货!建议大家收藏
- 统一规约-关乎数字化的真正实现(规范统一性)
- Elasticsearch节点角色配置详解(Node)
- 产前母婴用品分享 篇一:我的母婴购物清单及单品推荐
- 标签列表
-
- dialog.js (57)
- importnew (44)
- windows93网页版 (44)
- yii2框架的优缺点 (45)
- tinyeditor (45)
- qt5.5 (60)
- windowsserver2016镜像下载 (52)
- okhttputils (51)
- android-gif-drawable (53)
- 时间轴插件 (56)
- docker systemd (65)
- slider.js (47)
- android webview缓存 (46)
- pagination.js (59)
- loadjs (62)
- openssl1.0.2 (48)
- velocity模板引擎 (48)
- pcre library (47)
- zabbix微信报警脚本 (63)
- jnetpcap (49)
- pdfrenderer (43)
- fastutil (48)
- uinavigationcontroller (53)
- bitbucket.org (44)
- python websocket-client (47)