learn.lianglianglee.com/文章/分布式链路追踪:集群管理设计.md.html
2022-08-14 03:40:33 +08:00

678 lines
39 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>分布式链路追踪:集群管理设计.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/文章/AQS 万字图文全面解析.md.html">AQS 万字图文全面解析</a>
</li>
<li>
<a href="/文章/Docker 镜像构建原理及源码分析.md.html">Docker 镜像构建原理及源码分析</a>
</li>
<li>
<a href="/文章/ElasticSearch 小白从入门到精通.md.html">ElasticSearch 小白从入门到精通</a>
</li>
<li>
<a href="/文章/JVM CPU Profiler技术原理及源码深度解析.md.html">JVM CPU Profiler技术原理及源码深度解析</a>
</li>
<li>
<a href="/文章/JVM 垃圾收集器.md.html">JVM 垃圾收集器</a>
</li>
<li>
<a href="/文章/JVM 面试的 30 个知识点.md.html">JVM 面试的 30 个知识点</a>
</li>
<li>
<a href="/文章/Java IO 体系、线程模型大总结.md.html">Java IO 体系、线程模型大总结</a>
</li>
<li>
<a href="/文章/Java NIO浅析.md.html">Java NIO浅析</a>
</li>
<li>
<a href="/文章/Java 面试题集锦(网络篇).md.html">Java 面试题集锦(网络篇)</a>
</li>
<li>
<a href="/文章/Java-直接内存 DirectMemory 详解.md.html">Java-直接内存 DirectMemory 详解</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决.md.html">Java中9种常见的CMS GC问题分析与解决</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决.md.html">Java中9种常见的CMS GC问题分析与解决</a>
</li>
<li>
<a href="/文章/Java中的SPI.md.html">Java中的SPI</a>
</li>
<li>
<a href="/文章/Java中的ThreadLocal.md.html">Java中的ThreadLocal</a>
</li>
<li>
<a href="/文章/Java线程池实现原理及其在美团业务中的实践.md.html">Java线程池实现原理及其在美团业务中的实践</a>
</li>
<li>
<a href="/文章/Java魔法类Unsafe应用解析.md.html">Java魔法类Unsafe应用解析</a>
</li>
<li>
<a href="/文章/Kafka 源码阅读笔记.md.html">Kafka 源码阅读笔记</a>
</li>
<li>
<a href="/文章/Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理.md.html">Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB Buffer Pool.md.html">MySQL · 引擎特性 · InnoDB Buffer Pool</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB IO子系统.md.html">MySQL · 引擎特性 · InnoDB IO子系统</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 事务系统.md.html">MySQL · 引擎特性 · InnoDB 事务系统</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 同步机制.md.html">MySQL · 引擎特性 · InnoDB 同步机制</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 数据页解析.md.html">MySQL · 引擎特性 · InnoDB 数据页解析</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB崩溃恢复.md.html">MySQL · 引擎特性 · InnoDB崩溃恢复</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · 临时表那些事儿.md.html">MySQL · 引擎特性 · 临时表那些事儿</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 半同步复制.md.html">MySQL 主从复制 半同步复制</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 基于GTID复制.md.html">MySQL 主从复制 基于GTID复制</a>
</li>
<li>
<a href="/文章/MySQL 主从复制.md.html">MySQL 主从复制</a>
</li>
<li>
<a href="/文章/MySQL 事务日志(redo log和undo log).md.html">MySQL 事务日志(redo log和undo log)</a>
</li>
<li>
<a href="/文章/MySQL 亿级别数据迁移实战代码分享.md.html">MySQL 亿级别数据迁移实战代码分享</a>
</li>
<li>
<a href="/文章/MySQL 从一条数据说起-InnoDB行存储数据结构.md.html">MySQL 从一条数据说起-InnoDB行存储数据结构</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:事务和锁的面纱.md.html">MySQL 地基基础:事务和锁的面纱</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据字典.md.html">MySQL 地基基础:数据字典</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据库字符集.md.html">MySQL 地基基础:数据库字符集</a>
</li>
<li>
<a href="/文章/MySQL 性能优化:碎片整理.md.html">MySQL 性能优化:碎片整理</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?.md.html">MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:如何在日志中轻松定位大事务.md.html">MySQL 故障诊断:如何在日志中轻松定位大事务</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:教你快速定位加锁的 SQL.md.html">MySQL 故障诊断:教你快速定位加锁的 SQL</a>
</li>
<li>
<a href="/文章/MySQL 日志详解.md.html">MySQL 日志详解</a>
</li>
<li>
<a href="/文章/MySQL 的半同步是什么?.md.html">MySQL 的半同步是什么?</a>
</li>
<li>
<a href="/文章/MySQL中的事务和MVCC.md.html">MySQL中的事务和MVCC</a>
</li>
<li>
<a href="/文章/MySQL事务_事务隔离级别详解.md.html">MySQL事务_事务隔离级别详解</a>
</li>
<li>
<a href="/文章/MySQL优化优化 select count().md.html">MySQL优化优化 select count()</a>
</li>
<li>
<a href="/文章/MySQL共享锁、排他锁、悲观锁、乐观锁.md.html">MySQL共享锁、排他锁、悲观锁、乐观锁</a>
</li>
<li>
<a href="/文章/MySQL的MVCC多版本并发控制.md.html">MySQL的MVCC多版本并发控制</a>
</li>
<li>
<a href="/文章/QingStor 对象存储架构设计及最佳实践.md.html">QingStor 对象存储架构设计及最佳实践</a>
</li>
<li>
<a href="/文章/RocketMQ 面试题集锦.md.html">RocketMQ 面试题集锦</a>
</li>
<li>
<a href="/文章/SnowFlake 雪花算法生成分布式 ID.md.html">SnowFlake 雪花算法生成分布式 ID</a>
</li>
<li>
<a href="/文章/Spring Boot 2.x 结合 k8s 实现分布式微服务架构.md.html">Spring Boot 2.x 结合 k8s 实现分布式微服务架构</a>
</li>
<li>
<a href="/文章/Spring Boot 教程:如何开发一个 starter.md.html">Spring Boot 教程:如何开发一个 starter</a>
</li>
<li>
<a href="/文章/Spring MVC 原理.md.html">Spring MVC 原理</a>
</li>
<li>
<a href="/文章/Spring MyBatis和Spring整合的奥秘.md.html">Spring MyBatis和Spring整合的奥秘</a>
</li>
<li>
<a href="/文章/Spring 帮助你更好的理解Spring循环依赖.md.html">Spring 帮助你更好的理解Spring循环依赖</a>
</li>
<li>
<a href="/文章/Spring 循环依赖及解决方式.md.html">Spring 循环依赖及解决方式</a>
</li>
<li>
<a href="/文章/Spring中眼花缭乱的BeanDefinition.md.html">Spring中眼花缭乱的BeanDefinition</a>
</li>
<li>
<a href="/文章/Vert.x 基础入门.md.html">Vert.x 基础入门</a>
</li>
<li>
<a href="/文章/eBay 的 Elasticsearch 性能调优实践.md.html">eBay 的 Elasticsearch 性能调优实践</a>
</li>
<li>
<a href="/文章/不可不说的Java“锁”事.md.html">不可不说的Java“锁”事</a>
</li>
<li>
<a href="/文章/互联网并发限流实战.md.html">互联网并发限流实战</a>
</li>
<li>
<a href="/文章/从ReentrantLock的实现看AQS的原理及应用.md.html">从ReentrantLock的实现看AQS的原理及应用</a>
</li>
<li>
<a href="/文章/从SpringCloud开始聊微服务架构.md.html">从SpringCloud开始聊微服务架构</a>
</li>
<li>
<a href="/文章/全面了解 JDK 线程池实现原理.md.html">全面了解 JDK 线程池实现原理</a>
</li>
<li>
<a href="/文章/分布式一致性理论与算法.md.html">分布式一致性理论与算法</a>
</li>
<li>
<a href="/文章/分布式一致性算法 Raft.md.html">分布式一致性算法 Raft</a>
</li>
<li>
<a href="/文章/分布式唯一 ID 解析.md.html">分布式唯一 ID 解析</a>
</li>
<li>
<a class="current-tab" href="/文章/分布式链路追踪:集群管理设计.md.html">分布式链路追踪:集群管理设计</a>
</li>
<li>
<a href="/文章/动态代理种类及原理,你知道多少?.md.html">动态代理种类及原理,你知道多少?</a>
</li>
<li>
<a href="/文章/响应式架构与 RxJava 在有赞零售的实践.md.html">响应式架构与 RxJava 在有赞零售的实践</a>
</li>
<li>
<a href="/文章/大数据算法——布隆过滤器.md.html">大数据算法——布隆过滤器</a>
</li>
<li>
<a href="/文章/如何优雅地记录操作日志?.md.html">如何优雅地记录操作日志?</a>
</li>
<li>
<a href="/文章/如何设计一个亿级消息量的 IM 系统.md.html">如何设计一个亿级消息量的 IM 系统</a>
</li>
<li>
<a href="/文章/异步网络模型.md.html">异步网络模型</a>
</li>
<li>
<a href="/文章/当我们在讨论CQRS时我们在讨论些神马.md.html">当我们在讨论CQRS时我们在讨论些神马</a>
</li>
<li>
<a href="/文章/彻底理解 MySQL 的索引机制.md.html">彻底理解 MySQL 的索引机制</a>
</li>
<li>
<a href="/文章/最全的 116 道 Redis 面试题解答.md.html">最全的 116 道 Redis 面试题解答</a>
</li>
<li>
<a href="/文章/有赞权限系统(SAM).md.html">有赞权限系统(SAM)</a>
</li>
<li>
<a href="/文章/有赞零售中台建设方法的探索与实践.md.html">有赞零售中台建设方法的探索与实践</a>
</li>
<li>
<a href="/文章/服务注册与发现原理剖析Eureka、Zookeeper、Nacos.md.html">服务注册与发现原理剖析Eureka、Zookeeper、Nacos</a>
</li>
<li>
<a href="/文章/深入浅出Cache.md.html">深入浅出Cache</a>
</li>
<li>
<a href="/文章/深入理解 MySQL 底层实现.md.html">深入理解 MySQL 底层实现</a>
</li>
<li>
<a href="/文章/漫画讲解 git rebase VS git merge.md.html">漫画讲解 git rebase VS git merge</a>
</li>
<li>
<a href="/文章/生成浏览器唯一稳定 ID 的探索.md.html">生成浏览器唯一稳定 ID 的探索</a>
</li>
<li>
<a href="/文章/缓存 如何保证缓存与数据库的双写一致性?.md.html">缓存 如何保证缓存与数据库的双写一致性?</a>
</li>
<li>
<a href="/文章/网易严选怎么做全链路监控的?.md.html">网易严选怎么做全链路监控的?</a>
</li>
<li>
<a href="/文章/美团万亿级 KV 存储架构与实践.md.html">美团万亿级 KV 存储架构与实践</a>
</li>
<li>
<a href="/文章/美团点评Kubernetes集群管理实践.md.html">美团点评Kubernetes集群管理实践</a>
</li>
<li>
<a href="/文章/美团百亿规模API网关服务Shepherd的设计与实现.md.html">美团百亿规模API网关服务Shepherd的设计与实现</a>
</li>
<li>
<a href="/文章/解读《阿里巴巴 Java 开发手册》背后的思考.md.html">解读《阿里巴巴 Java 开发手册》背后的思考</a>
</li>
<li>
<a href="/文章/认识 MySQL 和 Redis 的数据一致性问题.md.html">认识 MySQL 和 Redis 的数据一致性问题</a>
</li>
<li>
<a href="/文章/进阶Dockerfile 高阶使用指南及镜像优化.md.html">进阶Dockerfile 高阶使用指南及镜像优化</a>
</li>
<li>
<a href="/文章/铁总在用的高性能分布式缓存计算框架 Geode.md.html">铁总在用的高性能分布式缓存计算框架 Geode</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析</a>
</li>
<li>
<a href="/文章/面试最常被问的 Java 后端题.md.html">面试最常被问的 Java 后端题</a>
</li>
<li>
<a href="/文章/领域驱动设计在互联网业务开发中的实践.md.html">领域驱动设计在互联网业务开发中的实践</a>
</li>
<li>
<a href="/文章/领域驱动设计的菱形对称架构.md.html">领域驱动设计的菱形对称架构</a>
</li>
<li>
<a href="/文章/高效构建 Docker 镜像的最佳实践.md.html">高效构建 Docker 镜像的最佳实践</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>分布式链路追踪:集群管理设计</h1>
<p>SkyWalking 是一个开源 APM 系统,包括针对 Cloud Native 体系结构中的分布式系统的监视,跟踪,诊断功能。核心功能如下:</p>
<ul>
<li>服务、服务实例、端点指标分析</li>
<li>根本原因分析,在运行时分析代码</li>
<li>服务拓扑图分析</li>
<li>服务,服务实例和端点依赖性分析</li>
<li>检测到慢速服务和端点</li>
<li>性能优化</li>
<li>分布式跟踪和上下文传播</li>
<li>数据库访问指标。检测慢速数据库访问语句(包括 SQL 语句)</li>
<li>报警</li>
</ul>
<p>SkyWalking 目前是 Apache 顶级项目,作为这么优秀的开源项目,它的架构设计理念肯定会有很多值得我们借鉴。</p>
<p>本文会包含如下内容:</p>
<ul>
<li>集群管理生态方法论</li>
<li>SkyWalking 集群管理设计</li>
</ul>
<p>本篇文章适合人群:架构师、技术专家以及对全链路监控非常感兴趣的高级工程师。</p>
<h3>集群管理生态方法论</h3>
<p>集群管理的方法论有很多,当今社会又是一个信息膨胀的时代,所以会有很多书籍或者文章会去剖析一些方法论,我相信很多都具备很强的收藏价值。</p>
<p>比如我现在需要做一个缓存系统,最开始我们肯定会考虑使用本地单机缓存,因为这样性能高,实现简单,我们只需要使用一个容器来承载这些数据,然后相办法如何保证数据的读写线程安全就行了,于是我们就会考虑单虚拟机下的性能优化,比如如何用多线程操作替代单线程,如何用事件驱动去替换同步,如何转换成异步,其实这些都会是在单机本地缓存上效果最明显,因为没有网络开销。</p>
<p>但是随着服务能力的提升以及运营给力我们的应用程序单机的流量越来越大线上单机根本不够用我们需要主从部署这样问题就来了我们需要数据同步slave 需要从 master 上同步数据,涉及到跨进程的数据同步,也就是这个时候就需要保证数据同步的高可用、高性能、高并发等特性,那么这个时候就需要集群管理了,需要我们去管理这些数据同步的操作。</p>
<p>那么我们首先会想到,我们原先是基于内存的,我们可以改成基于 openAPI 模式,如果一台机器上的内存数据变化了,我就通过 openAPI 实时的同步到其他集群节点上,然后更新对应机器节点的内存数据,这样数据就保证一致性了,只是说这个数据不会持久化,一旦有节点挂掉了,数据就全丢失了。</p>
<p>怎么办?程序员是很聪明的,我们可以持久化啊,把内存中的数据同步到磁盘文件,做备份,如果节点挂掉,再重新启动会去加载已经备份的文件。那么问题又来了,如果每次更新内存都会去持久化文件,如果有大量的请求,这样整个集群抗并发的能力会非常的差,所以又发明了异步刷盘机制以及机器硬盘的缓存机制。</p>
<p>其实上面所说的集群间数据的管理功能,在分布式领域里面属于 AP 模式,只会保证最终一致性。</p>
<p>那么如何保证 CP 的强一致性了,那么程序员的进阶之路,我们肯定需要刨根问底,这个时间基于 Raft 算法的分布式能力,就是 CP 算法,所以现在有很多框架的集群管理都会采用分布式算法 Raft因为这个算法高效并且稳定。</p>
<p>使用 Raft 算法来保证集群管理能力的有很多优秀的框架,比如:</p>
<ul>
<li>Nacos</li>
<li>Rocket MQ</li>
<li>蚂蚁金服的 JRaft</li>
</ul>
<p>所以上升到集群管理,业界一般都会采用 CP 或者 AP 模式,很少有框架能够同时实现 CAP 模式的。</p>
<p>关于 Nacos 的分布式选举算法,欢迎关注作者的另外一篇 Chat 文章——《调侃面试官,分布式选举算法 Raft 在 Nacos 中的应用 》,关于 Rocket MQ 的集群管理功能,欢迎关注作者的另外一篇 Chat 文章——《你所不知道的 RocketMQ 的集群管理:副本机制 》。</p>
<h3>SkyWalking 集群管理设计</h3>
<p>SkyWalking 集群管理支持能力点包括:基于 Consul 的集群管理,基于 etcd 的集群管理,基于 Kubernetes 的集群管理,基于 Nacos 的集群管理,基于 ZooKeeper 的集群管理。SkyWalking 的集群管理又是靠 Selector 来做配置切换的。</p>
<pre><code class="language-yaml">selector: ${SW_CLUSTER:nacos}
standalone:
</code></pre>
<h4>基于 Consul 的集群管理</h4>
<p>既然可以用 Consul 做集群管理肯定是要先加载配置文件SkyWalking 定义了 ClusterModuleConsulConfig会加载 Consul 的基础配置信息。</p>
<ul>
<li>serviceName服务名称</li>
<li>hostPortIP + 端口</li>
<li>internalComHost内部通信 IP</li>
<li>aclTokenacl 认证 token</li>
<li>internalComPort内部通信端口</li>
</ul>
<p>对 SkyWalking 比较了解的人会知道,它所有的功能都是按照模块来加载的,所以 Consul 也会自定义一个模块 ClusterModuleConsulProvider。</p>
<p>定义模块的名称为 Consul定义模块的基础类模块为 ClusterModule绑定模块的配置文件 ClusterModuleConsulConfig重写 prepare() 方法,方便整个 OAP- SERVER 初始化的时候,完成 Consul 集群的加载。</p>
<p>加载的过程中就会植入集群能力,比如 ConsulCoordinator集群能力肯定是具备服务注册和服务发现功能SkyWalking 统一封装了 ClusterRegister 和 ClusterNodesQuery 接口能力ClusterRegister 具备 registerRemote 能力ClusterNodesQuery 具备能力。</p>
<p>ConsulCoordinator 初始化 Consul 客户端 client并获取到 Consul 集群选举出来的 HealthClient并通过客户端获取到健康的数据节点列表并将节点列表转换为平台能够识别的远程节点信息列表并返回。从 ServiceHealth 转换为 RemoteInstance</p>
<h4>基于 etcd 的集群管理</h4>
<p>加载集群配置信息 ClusterModuleEtcdConfig 继承 ModuleConfig</p>
<ul>
<li>serviceName服务名称</li>
<li>hostPortIP 加端口</li>
<li>isSSL是否开启 SSL 认证</li>
<li>internalComHost内部通信 IP 地址</li>
<li>internalComPort内部通信端口号</li>
</ul>
<p>集群能力初始化模块ClusterModuleEtcdProvider继承基础模块 ModuleProvider这点和 Consul 集群管理的原理是一样的。加载配置文件 ClusterModuleEtcdConfig并初始化 EtcdClient赋值模块名称为 etcd并通过 prepare() 方法完成集群能力加载。解析配置文件,并和 EtcdClient 绑定,并通过 EtcdCoordinator 和 EtcdClient 绑定一起完成集群的能力。</p>
<p>EtcdCoordinator 是集群管理的核心能力,通过客户端以及 serviceName 来获取指定服务的节点信息列表,并将 Etcd 集群能够识别的节点信息 EtcdNode 转换为平台能够识别的节点信息 RemoteInstance。</p>
<h4>基于 Kubernetes 的集群管理</h4>
<p>K8s 集群管理配置文件加载:</p>
<ul>
<li>watchTimeoutSeconds监听超时时间</li>
<li>namespace命名空间</li>
<li>labelSelector标签选择器</li>
<li>uidEnvNameuid 环境名称</li>
</ul>
<p>ClusterModuleKubernetesProvider基于 K8s 的能力加载模块。配置集群模块名称 Kubernetes绑定集群模块和配置文件 ClusterModule 和 ClusterModuleKubernetesConfig。模块在初始化过程中会初始化 KubernetesCoordinator基于 K8s 的集群选举核心能力。这里有一个小细节,基于 K8s 的集群管理,是假想 Skywalking 自身本身就是 K8s 里面的一个服务,依托于 K8s 的服务治理能力,所以,集群选举能力,在注册 IP 的过程中,是和 K8s 共用一套 API。</p>
<p>通过 Provider 中的 notifyAfterCompleted 完成 coordinator.start(),开启集群选举,集群选举通过一个 SingleThreadExecutor 并结合定时器去执行监听器方法,实时的维护注册节点缓存,供 Skywalking 节点使用。</p>
<h4>基于 Nacos 的集群管理</h4>
<p>加载 ClusterModuleNacosConfig 配置,配置中会加载如下属性:</p>
<ul>
<li>serviceName服务名称</li>
<li>hostPortIP + 端口</li>
<li>namespace命名空间</li>
</ul>
<p>集群模块加载器 ClusterModuleNacosProvider命名为 Nacos 模块,构建 NamingService 模块NamingService 这个是分布式集群管理 Nacos 的服务发现的 API依托这个 API 可以找到对应服务名称所属的集群信息,包含 IP + 端口。</p>
<p>NacosCoordinator 集群选举模块,通过 NamingService 的方法 registerInstance 和 selectInstances 去注册和发现服务元数据信息。</p>
<h4>基于 ZooKeeper 的集群管理</h4>
<p>ZooKeeper 的集群管理,基本原理就是节点信息 + 监听器机制,这里也会加载基础配置信息 ClusterModuleZookeeperConfig</p>
<ul>
<li>
<p>nameSpace命名空间</p>
</li>
<li>
<p>hostPortIP + 端口</p>
</li>
<li>
<p>baseSleepTimeMs休眠时间</p>
</li>
<li>
<p>maxRetries最大重试次数</p>
</li>
<li>
<p>internalComHost内部通信 IP 地址</p>
</li>
<li>
<p>internalComPort内部通信端口号</p>
</li>
<li>
<p>enableACL是否开启 ACL 认证</p>
</li>
<li>
<p>schema数据库 schema</p>
</li>
<li>
<p>expression匹配表达式</p>
<p>集群选举加载模块 ClusterModuleZookeeperProvider封装 CuratorFramework 客户端,熟悉 ZooKeeper 的人都知道,这个是 curator 框架针对 ZooKeeper 客户端的封装,也是一个高性能的中间件框架,配置 ZooKeeper 模块。那么模块初始化会加载哪些信息,比如 ACL 认证信息,初始化客户端,初始化 ZookeeperCoordinator 选举 API绑定配置文件和客户度完成集群选举能力的初始化。</p>
</li>
</ul>
<p>ZookeeperCoordinator 是 Skywalking 封装的真正的集群选举能力,包括集群信息的适配等。</p>
<h4>Skywalking 集群管理总结</h4>
<p>集群管理,我们总得了解 Skywalking 为什么要用集群管理,在它的架构设计理念中,整个 OAP 平台的角色主要分为如下:</p>
<ul>
<li>CoreModuleConfig.Role.Mixed</li>
<li>CoreModuleConfig.Role.Aggregator</li>
<li>CoreModuleConfig.Role.Receiver</li>
</ul>
<p>这里简单的解释下Mixed 是混合模式,既包含 Aggregator 和 Receiver。Aggregator是聚合器模式也就是说数据收集到 OAP 平台之后数据需要做过滤、清晰和聚合然后再存储。Receiver 是收集器模式,也就是原始数据会直接存储,不做任何处理,当然这个肯定会有最核心的链路数据,就不会产生很多通过聚合之后产生的指标数据了。</p>
<p>那么 Skywalking 中的集群管理主要是针对 Aggregator 模式,当然肯定也会包含 Mixed 模式。那么为什么 Aggregator 模式需要分布式集群管理功能,这个我们应该能够理解,因为需要处理数据,那么肯定需要保证 CAP 或者 BASE 理论了,也就是要保证集群节点之间的分布式特性,所以 Skywalking 就针对你所需要的集群功能,然后通过选择器架构模式,来充分满足平台的深度用户可以任意的挑选符合自己业务场景的集群管理能力。比如我们公司的技术栈是 Nacos那么我们肯定会优先选择它作为集群管理非常灵活。</p>
<p><img src="assets/caa5fa00-c916-11ea-ac32-8d2af7162ed8.png.png" alt="img" /></p>
<p>再聊聊 Skywalking 集群管理服务的能力有哪些,首先我们聚焦在服务发现,因为用到了集群能力,肯定是要服务发现,找到集群上注册的服务提供者的基础元数据。</p>
<p>ClusterNodesQuery.queryRemoteNodes() 能力,在 Skywalking 中如何被利用,首先我们关注下 RemoteClientManager ,这个类管理 OAP 服务节点集群之间的连接。有一个任务调度会自动查询服务节点列表从集群模块。比如 ZooKeeper 集群模块或 Kubernetes 集群模块。</p>
<p>从集群模块查询 OAP 服务器列表,并为新节点创建一个新连接。创建 OAP 服务器有序,因为每个服务节点会通过哈希码互相发送流数据。通过 queryRemoteNodes 的集群能力,找到集群中的节点信息列表。</p>
<p>由于 OAP 服务器注册由 UUID 与进程号一对一映射,注册信息没有立即删除后,进程关闭,因为总是发生网络故障,不是真的关闭过程。因此,集群模块必须等待几秒钟来确认。然后有多个注册的集群中的信息。</p>
<p>所以在拿到集群信息列表之后需要去重distinct并排序然后比较目前使用的集群远程客户端列表和当前最新的集群节点信息列表如果不同就会做同步更新。比较现有客户端和远程实例收集之间的客户端。将客户机移动到新的客户机集合中避免创建新的通道。关闭在集群配置中找不到的客户端。为除自实例外的远程实例创建一个 gRPC 客户端。</p>
<p>Skywalking 集群管理的能力是 gRPC 客户端集群,也就是说节点之间的 RPC 通信通道是 gRPC其实这点和 Dubbo 的集群管理本质上也是一样的,因为 Dubbo 管理的是 Dubbo 自己的 RPC比如 Netty。</p>
<p>然后又是怎么植入到 Skywalking 的功能领域的呢,这个就得通过 RemoteSenderService这个类包装了 RemoteClientManager首先从它里面获取到 RemoteClient 列表,然后拿到了列表之后,我们只能说知道了集群的能力,还需要考虑负载均衡,这点 Skywalking 就简单的封装了负载的能力,包含如下几种:</p>
<ul>
<li>HashCode通过 HashCodeSelector 完成按照 hash 取模的负载均衡算法。</li>
<li>Rolling通过 RollingSelector 完成按照轮询的负载均衡算法</li>
<li>ForeverFirst通过 ForeverFirstSelector 完成简单的总是第一个节点的负载均衡算法。</li>
</ul>
<p>那么问题又来了这里也是只是封装了集群负载的能力那么到底是哪部分能力在用集群的功能了这里有说明如下两个类MetricsRemoteWorker 和 RegisterRemoteWorker前者是从 agent 客户端收集到的度量信息,也就是聚合分析产出的数据,后者是本节点能力的分布式注册,比如 MetricsPersistentWorker 等。</p>
<p>关于 Skywalking 的核心 worker 能力,会在后续文章中详细的 Chat。</p>
<h3>本文总结</h3>
<p>本文从集群管理入手,分析了一下它的集群管理功能,之所以会写这篇文章,是因为自己的团队现在在深度的使用 Nacos 以及 Skywalking这些也都是自己 review 源码之后的一些心得,如果有不对的地方欢迎拍砖。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/文章/分布式唯一 ID 解析.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/文章/动态代理种类及原理,你知道多少?.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"709980479a1d8b66","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>