【Go语言基础】调度器模型GPM与垃圾回收器GC

news2024/12/23 23:26:38

系列综述:
💞目的:本系列是个人整理为了Go语言学习的,整理期间苛求每个知识点,平衡理解简易度与深入程度。
🥰来源:材料主要源于Go语言趣学指南进行的,每个知识点的修正和深入主要参考各平台大佬的文章,其中也可能含有少量的个人实验自证。
🤭结语:如果有帮到你的地方,就点个赞关注一下呗,谢谢🎈🎄🌷!!!

🙌请先收藏,未完待续…


文章目录

    • 零、概述
      • 什么是Go语言
    • 一、基本语法
    • 二、Go的执行
      • 概述
      • 调度器scheduler
      • Goroutine
      • Processor
      • Machine
      • 监控
    • 三、垃圾回收
      • 概述
      • 算法原理
        • 语言比较
    • 二、编译
    • 参考博客


😊点此到文末惊喜↩︎


零、概述

什么是Go语言

  1. Go语言的特点
    • 编译型语言,执行效率高
    • 原生的并发支持,支持轻量级的携程和通信机制
    • 具有内存回收机制,支持安全自动的管理内存
  2. 第一个Go程序
    package main	// 声明当前文件所属的包
    import (		// 引入其他包供当前文件使用
    	 "fmt"		// fmt:用于格式化输入输出的包
    )
    // 注意 { 必须和func在同一行
    func main() {	// 定义一个名字为main的函数
    	// 变量和常量
    	var variable = 123;	// 变量的声明
    	variable *= 2;		// 运算符简写
    	variable++;			// go中没有前置++
    	const k = 11;		// 常量的声明
    	
    	// 格式化打印
    	fmt.Println("hello world",1*2)
    	fmt.Printf("%-15v : %4v\n", "SpaceX", 94)
    	// %v为占位符,前面的正负数字表示占用的位数,不足使用空格填充
    }
    // 函数定义
    func swap(x, y string) (string, string) {
    return y, x
    }
    
  3. Go程序的起点
    • main包中的main函数。当运行一个Go程序时,编译器会自动寻找main包,并执行其中的main函数。
  4. 常量和变量

一、基本语法

  1. Go函数中的多个返回值是哪几种
    • 返回值类型,表示函数的执行结果
    • 返回error类型,表示函数的执行成功与否的情况
  2. Go中的数据类型类型
    • 值类型
      • 布尔类型(bool):表示true/false的值
      • 整数类型(int)
        • 有符号整型:包括int, int8, int16, int32, int64
        • 无符号整型:包括uint, uint8, uint16, uint32, uint64`
        • 指针无符号整型uintptr:用于和底层编程交互,常用于存储指针的整形表示
      • 浮点数类型:包括单精度浮点数(float32)双精度浮点数(float64)
      • 复数类型:由实部和虚部组成,有单精度复数(complex64)双精度复数(complex128)
      • 字符串类型(string):表示一串字符。
      • 字符类型(rune):表示一个Unicode字符。
      • 数组类型(array):具有固定大小和相同类型的连续元素的集合。
      • 结构体类型(struct):表示不同类型的字段组合。
    • 引用类型
      • 切片类型(slice)
        • 定义:是一个动态数组结构,包含起始位置、长度和容量。
        • 操作:超过容量会进行扩容,低于容量的 1 / 4 1/4 1/4会进行缩容
        • 开销:slice是基于数组的申请和复制实现的动态性的,会有开销
      • 映射类型(map):无序的键值对集合
      • 函数类型(func):可以将函数看成一种类型,用于变量的声明
      • 通道类型(channel):用于协程间安全同步的传送数据
    • 接口类型(interface):表示一组不实现的方法集合
      • 错误类型(error):表示错误信息的预定义的接口类型
  3. 介绍一下nil
    • 表示类型声明的变量未被初始化或赋值,
    • 注意
      • 不同类型的nil进行比较需要通过相应的类型判断函数
      • 如果指针类型未初始化,尝试对nil进行解引用会导致panic
  4. 介绍一下Go的异常类型
    • 定义:预定义的error接口类型
    • 作用:用error类型代替try…catch语句,节省资源,增加代码可读性
    // error接口定义
    type error interface {
        Error() string
    }
    // 错误示例
    package main
    import (
        "fmt"
        "errors"
    )
    func divide(a, b int) (int, error) {
    	//健壮性检查
        if b == 0 {	
            return 0, errors.New("division by zero")
        }
        return a / b, nil
    }
    func main() {
        result, err := divide(10, 2)	// 注意类型的接收
        if err != nil {
            fmt.Println("Error:", err)
        } else {
            fmt.Println("Result:", result)
        }
    }
    

二、Go的执行

概述

  1. Go程序的执行由两部分组成
    • Program:用于处理用户输入和执行用户态下的相关操作
    • Runtime:帮助用户程序处理与内核相关的系统调用,通过调度器scheduler提高执行效率
      在这里插入图片描述

调度器scheduler

  1. 定义
    • Go调度器通过GPM机制实现M:N的调度模型,提高垃圾回收效率,实现高效的并发编程。
  2. GPM机制
    • Goroutine(G)代表Go语言的协程,是调度的基本单位
    • Processor(P)代表执行Goroutine的上下文环境及资源,是中间调度器
    • Machine(M)是通过系统调用int 0x56创建的内核线程的抽象
    • 关系
      • 每个Go程序只有一个GPM调度器schedt
      • 每个M绑定一个内核线程
      • M可以关联多个P:因为P维护了上下文,可以在M绑定的内核线程阻塞的情况下,切换到另一个M上执行
      • P可以调度多个G:通过每个P本地就绪的G队列所有P共享的全局G队列实现
    • 池化优化:每个G、P和M都有自己的free队列,用于存储空闲的G/P/M对象,用时直接取,释放时放回,避免频繁的拷贝和销毁开销
    • P的数量:可通过runtime.GOMAXPROCS函数进行设定,默认为当前系统的CPU核数。
      在这里插入图片描述
      在这里插入图片描述
  3. M:N调度模型
    • 原理:N个协程G通过调度器P管理,从而映射到M个内核线程M上运行
    • 作用:平衡内核线程负载,充分提高系统资源的利用率
  4. 初始化执行过程
    • 执行schedinit 函数:初始化调度器相关的参数
    • 初始化 g0 栈:为运行 runtime 代码提供一个“环境”
    • 主线程绑定并初始化m0
    • 编译器将go func() {}翻译成newproc函数
    • newproc:获取函数的参数和代码段地址,通过g0创建G
    • newproc 函数获取了参数和当前g的pc信息,并通过g0调用newproc1去真正的执行创建或获取可用的g
    • newporc1 的作用就是创建或者获取一个空间的g,初始化这个g,并通过gfget尝试寻找一个p和m去执行g
      在这里插入图片描述
      在这里插入图片描述

Goroutine

  1. 定义:Goroutine是一种轻量级的并发实现方式,使用管道机制(channle)进行不同Goroutine间的通信,按照算法将goroutine分配到多个线程上执行,从同高效的利用多核处理器的并发性。
  2. Goroutine的组成
    • 栈帧(BP:SP):用于保存函数调用相关信息
    • 程序计数器(PC):指向当前正在执行的执行地址
    • 执行状态(State):表示当前Goroutine的运行状态
    • 其他基本属性信息,如抢占标记、链式指针、id等
  3. 特点
    • 轻量级(协程与内核线程的区别)
      • 动态小栈:内核线程栈空间通常2MB,而每个Goroutine的初始栈空间只有2KB,并能通过分段栈进行动态扩展,以适应程序的需求。
      • 上下文切换开销小:协程上下文切换只需在用户态下进行三个寄存器(PC、SP和BP)的切换。而线程上下文切换需要陷入内核,并进行16个寄存器的切换,大概5倍的性能开销。
    • 管道通信(channel)
      • 作用:channel是不同Goroutine间安全的数据传递同步机制
      • 组成:环形队列缓冲区和读写等待队列
      • 从channel读数据时,若channel缓冲区为空或没有缓冲区,则阻塞当前读线程,并加入到recvq队列中 。
      • 向channel写数据时,若缓冲区已满或没有缓冲区,则阻塞当前写线程,加入sendq队列中
    • M:N协程调度模型
      • 作用:将M个用户态线程(协程)映射到N个的内核线程上运行的调度模型。从而充分利用多核处理器的性能,同时减少线程上下文切换的开销。
      • 工作窃取(Work Stealing):当P的本地队列为空时,从其他P的本地队列偷取G运行
      • 调度器退化(Scheduler Pacing):通过抢占式调度,调度器中断并切换到其他协程。从而避免长时间运行的任务(例如计算密集型的任务)导致其他协程的饥饿现象
  4. goroutine的状态机模型
    • 创建:Go 必须对每个运行着的线程上的 Goroutine 进行调度和管理。这个调度的功能被委托给了一个叫做 g0 的特殊的 goroutine, g0 是每个 OS 线程创建的第一个goroutine。
    • 终止:在创建 goroutine 时,Go在开启实际go执行片段之前,通过PC寄存器设置了SP寄存器的首个函数栈帧(名为goexit的函数),这个技巧强制goroutine在结束工作后调用函数goexit。

Processor

  1. 定义:存储就绪状态的G队列,并调度和管理goroutine的执行,以实现高效的并发执行和资源利用
  2. P的相关队列
    • RunQueue:每个P拥有一个存储就绪状态(runnable)G的队列,避免对锁竞争激烈的全局队列的直接依赖
    • GlobalQueue:所有P共享同一个存储就绪状态G的队列,并由互斥锁进行并发访问的同步
  3. 作用
    • 维护本地的就绪Goroutine队列,并通过调度算法选择合适的Goroutine到M上执行
    • 管理资源:通过互斥锁同步对于共享资源的并发执行
    • 协调和通信:p会与其他p进行协调和通信,比如在负载均衡时,负责将Goroutine调度到其他p上执行,以充分利用系统的资源。
  4. P的调度算法
    • 新建的G会优先加入到P的本地队列。
    • P的本地队列满了:则将本地队列中一半的G移动到全局队列
    • M获取G的优先级
      • 从P的本地队列获取Goruntine
      • 从P共享的全局队列获取Goruntine
      • 处理网络IO阻塞的网络轮询器获取可运行的G
      • 其它的P的本地队列窃取Goroutine
        在这里插入图片描述

Machine

  1. 定义:是一个虚拟的执行环境,负责执行和管理Goroutine。
  2. 作用:
    • 执行Goroutine
    • 与内核线程进行交互,调度器根据系统负载动态创建或销毁M
    • 若Goroutine 执行时发生系统调用和阻塞,M会将该G标记阻塞状态,并交于调度器进行处理

监控

  1. 定义:在启动阶段,创建一个独立的M执行sysmon函数,不需要依赖P
  2. 原理
    • 调度器创建一个独立M执行sysmon函数
      • 释放闲置超过5分钟的span物理内存
      • 如果超过两分钟没有执行垃圾回收,则强制执行
      • 将长时间未处理的netpoll结果添加到任务队列
      • 向长时间运行的g进行抢占
      • 收回因为syscall而长时间阻塞的p
    • 监控线程首次休眠20us,每次执行完后,增加一倍的休眠时间,但是最多休眠10ms

三、垃圾回收

概述

  1. 定义:垃圾回收(Garbage Collection,简称GC)是一种自动内存管理机制,
    • 当开始垃圾回收时,运行时只需要等待当前正在CPU核上运行的那个Goroutine停止即可,而不需要等待所有的Goroutine。这样可以大大减少阻塞的时间,提高垃圾回收的效率。
  2. Go语言的GC流程(并发标记清除算法)
    • 标记阶段(Marking Phase):
    • 在标记阶段和再标记阶段,通过STW机制暂停所有的Go程来确保标记的准确性;
    • 在清除阶段和内存整理阶段,采用并发方式来提高垃圾回收的效率。
  3. GC的触发条件
    • 主动触发:通过调用 runtime.GC 来触发,但若有正在执行的GC,会阻塞等待
    • 被动触发:
      • 系统监控:当超过两分钟没有产生任何 GC 时,强制触发 GC。
      • 步调算法:?
  4. 如果内存分配速度超过了标记清除的速度怎么办?
    • 目前的 Go 实现中,当 GC 触发后,会首先进入并发标记的阶段。并发标记会设置一个标志,并在 mallocgc 调用时进行检查。当存在新的内存分配时,会暂停分配内存过快的那些 goroutine,并将其转去执行一些辅助标记(Mark Assist)的工作,从而达到放缓继续分配、辅助 GC 的标记工作的目的。
    • 编译器会分析用户代码,并在需要分配内存的位置,将申请内存的操作翻译为 mallocgc 调用,而 mallocgc 的实现决定了标记辅助的实现,
  5. GC的性能指标
    • CPU占用率:回收算法执行占用的CPU时间
    • GC停顿的时间和频率:需要考虑 STW 和 Mark Assist 两个部分可能造成的停顿
    • GC可扩展性:堆增大时,垃圾回收器的性能
  6. Go 的 GC 如何调优
    • 对停顿敏感:由于GC的执行,导致用户代码执行的滞后。优化用户代码从而减少分配内存的数量
    • 对资源消耗敏感:GC增加了对CPU的占用率
    • 原则
      • 避免过早优化,并只优化性能瓶颈
    • 减少内存申请次数(降本)
    • 提高内存申请速度(增效)
      • 池化算法:预申请和复用提高申请速度
  7. Go 的垃圾回收器有哪些相关的 API?其作用分别是什么?
    • runtime.GC:手动触发 G
    • runtime.ReadMemStats:读取内存相关的统计信息,其中包含部分 GC 相关的统计信
    • debug.FreeOSMemory:手动将内存归还给操作系
    • debug.ReadGCStats:读取关于 GC 的相关统计信
    • debug.SetGCPercent:设置 GOGC 调步变
    • debug.SetMaxHeap(尚未发布[10]):设置 Go 程序堆的上限值

算法原理

Go语言中的垃圾回收(GC)算法采用了三色标记(tricolor marking)算法,基于并发标记清除(concurrent mark and sweep)策略。

三色标记算法将所有对象分为三个颜色:白色、灰色和黑色。

白色:表示对象尚未被访问和标记。
灰色:表示对象已经被访问,但是其引用尚未全部扫描。
黑色:表示对象已经被访问,并且其引用已经全部扫描。
GC 运行的步调由两个阶段组成:标记(marking)和清除(sweeping)。

标记阶段:

标记阶段以根对象(root object)为起点,递归地遍历所有可达对象,并将其标记为灰色。
将标记为灰色的对象标记为黑色,并将其引用对象标记为灰色。
重复上述过程,直到没有灰色对象为止。
清除阶段:

从堆中的所有对象中,找到没有标记为黑色的对象,即为垃圾对象。
将垃圾对象释放或回收,并将其空闲内存归还给堆。
Go语言中的GC算法是基于并发标记清除的,即在标记阶段和清除阶段都可以和用户程序同时进行。这样可以最大程度地减小GC对程序性能的影响,并且尽量保持内存的使用效率。

总结起来,Go语言中的垃圾回收(GC)采用了三色标记算法,通过并发标记清除策略进行垃圾对象的标记和释放。这种算法可以高效地管理内存,并且尽量减少对程序性能的影响。

语言比较
  1. 是否具有原生GC
    • 原生GC:Python、Java、JavaScript、Objective-C、Swift
    • 手动内存管理:C、C++、Rust
  2. 垃圾回收的优劣
    • 优点:编程简单,无需程序员手动释放,减少内存泄漏等相关问题、
    • 缺点:具有额外的性能开销,仍然可能存在内存泄漏问题
  3. Java的GC原理
    • 分布式GC:将对象依据存活时间分配到不同的区域,每次回收只回收其中的一个区域。
    • 操作
      • 将堆分成年轻代、老年代和永久代,触发条件是用户的配置和实际代码行为的预测
      • 年轻代收集周期:只对年轻代对象进行收集与清理
      • 老年代收集周期:只对老年代对象进行收集与清理
      • 混合式收集周期:同时对年轻代和老年代进行收集与清理
      • 完整 GC 周期:完整的对整个堆进行收集与清理
  4. 目前 Go 语言的 GC 还存在哪些问题
    • Mark Assist 停顿时间过长
    • Sweep 停顿时间过长
    • 由于 GC 算法的不正确性导致 GC 周期被迫重新执行
    • 创建大量 Goroutine 后导致 GC 消耗更多的 CPU

二、编译

  1. 逃逸分析
    • 定义:在编译原理中,分析指针动态范围的方法。
    • 发生条件:当一个对象的指针被多个方法或线程引用
    • 模糊化处理:Go语言的逃逸分析是编译器执行静态代码分析后,对内存管理进行的优化和简化,它可以决定一个变量是分配到堆还栈上。所以堆和栈的区别对程序员“模糊化“了
  2. 基本原则
    • 函数返回对一个变量的引用
    • 编译器会分析代码的特征和代码生命周期,Go中的变量只有在编译器可以证明在函数返回后不会再被引用的,才分配到栈上,其他情况下都是分配到堆上。
    • 编译器通过分析代码来决定将变量分配到何处。
  3. 编译器会根据变量是否被外部引用来决定是否逃逸:
    • 如果函数外部没有引用,则优先放到栈中;
    • 如果函数外部存在引用,则必定放到堆中;
    • 原因:栈的分配和执行比堆快,如果无法在栈上分配,则会逃逸到堆上。但是堆的分配会增加GC压力
  4. GoPath 的作用在于提供一个可以寻找 .go 源码的路径,包含
    • src 存放源文件
    • pkg 存放源文件编译后的库文件,后缀为 .a
    • bin 则存放可执行文件。
  5. Go的编译过程
    • 词法分析、语法分析
    • 中间代码的生成与优化
    • 目标代码的生成与优化
    • 链接
      在这里插入图片描述


少年,我观你骨骼清奇,颖悟绝伦,必成人中龙凤。
不如点赞·收藏·关注一波


🚩点此跳转到首行↩︎

参考博客

  1. 知乎goroutine解释
  2. 知乎goroutine原理
  3. 协程管道通信机制
  4. golang 源码学习之GMP (goroutine)
  5. 深入理解Go-goroutine的实现及Scheduler分析
  6. Go 程序员面试笔试宝典
  7. Golang并发编程-GPM协程调度模型原理及结构分析
  8. 待定引用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2077099.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ubuntu系统在线安装下载firefox-esr流览器

1、在线firefox流览器 Firefox ESR(Extended Support Release)是火狐浏览器的长期支持版本,针对同一个主版本提供一年左右的安全性与稳定性支持。如果您因为火狐浏览器改版而导致有原本能用的功能变得不能使用的话(例如Firefox 64.0把RSS订阅的功能拿掉了)&#xf…

IO进程day02(文件IO)

目录 【1】什么是文件IO 1》概念 2》特点 3》操作 【2】函数接口 1》打开文件open() 思考:文件IO和标准IO的打开方式的对应关系 2》关闭文件 close() 3》读写文件 read write 1> 读文件 read() 2> 写文件 write() 练习:文件IO实现cp…

使用AWS的EC2服务如何降低成本

在现代企业中,云计算已经成为推动业务创新和发展的重要工具。亚马逊云服务(AWS)的弹性计算云(EC2)提供了灵活的计算能力,企业可以根据需求快速部署和管理应用。然而,如何在使用EC2服务的过程中有…

无人机之多旋翼无人机的用途

一、航拍与摄影 高清摄像:多旋翼无人机搭载高清摄像头,可以进行高空拍摄,获取清晰的图像和视频资料,广泛应用于影视制作和新闻报道。 实时传输:无人机可通过图像传输设备,实现实时画面回传,为…

linux常见基础命令

Linux基础命令 (下面这些命令都是最常见的命令.更复杂的会在之后的C语言学习陆续深入) 1、 pwd 功能: print work directory的缩写,显示当前目录的绝对路径 2、 cd 功能: change directory的缩写,切换目录 绝对路径:以…

知识改变命运 数据结构【二叉树OJ题】

1. 检查两颗树是否相同OJ链接 class Solution {public boolean isSameTree(TreeNode p, TreeNode q) {if(pnull&&q!null||p!null&&qnull) {return false;}if (pnull&&qnull) {return true;}if (p.val!q.val) {return false;}boolean leftisSameTree(p.…

数据库内容保密检查系统:及时发现“潜在”安全威胁

日前,国内专注于保密与非密领域的分级保护、等级保护、业务连续性安全和大数据安全产品解决方案与相关技术研究开发的领军企业——国联易安自主研发的国联数据库内容保密检查系统V1.0通过国保局涉密检测,获得涉密信息系统产品检测证书。其主要具备以下主…

nginx访问控制,用户认证,https

nginx访问控制 用于location段Allow:设定允许哪台或哪些主机访问,多个参数间用空格隔开Deny:设定禁止那台或哪些主机访问,多个参数间用空格隔开比如:allow 192.168.100.20 192.168.100.30;deny all;拒绝某台主机访问…

JavaEE 第18节 TCPUDP优缺点(对比)

目录 前言1、UDP(User Datagram Protocol)优点缺点 2、TCP(Transmission Control Protocol)优点缺点 3、总结 前言 基于TCP/IP协议栈,TCP和UDP属于传输层协议。在Java生态中也分别提供了相关套接字(Socket)1的API,方便网络编程。…

基于OpenCV+MFC的KCF测速软件

基于OpenCVMFC的KCF测速软件 引言原理介绍使用介绍(1)主界面(2)打开视频(3)点击KCF测速(4)框选待检测目标(5)测速结果 资源链接(包含源码&#xf…

How do I do function calling in Azure Openai using the javascript sdk

题意:如何使用 JavaScript SDK 在 Azure OpenAI 中进行函数调用? 问题背景: I want to be able to call functions based on the user input, I could do this with the openai library but cant find a way to do so in the azure openai l…

uniapp中$off没写正确引发的问题~

你们好,我是金金金。 场景 我正在使用uniapp开发微信小程序,涉及到几个页面之间的事件通信,采用的是uniapp里面的$on和&off来达到页面之间的互相通信功能如下:有一个选择城市的页面,当选择了某个城市,另…

树数据结构(Tree Data Structures)的全面指南:深度解析、算法实战与应用案例

树数据结构(Tree Data Structures)的全面指南:深度解析、算法实战与应用案例 引言 树数据结构(Tree Data Structures)作为计算机科学中的基石之一,以其独特的层次结构和分支特性,在众多领域发…

Hive SQL

一、基本数据类型 tinyint 1byte 有符号整数 smallint 2byte 有符号整数 int 4byte 有符号整数 bigint 8byte 有符号整数 boolean 布尔类型,true或者false float 单精度浮点数 double 双精度浮点数 decim…

系统数据迁移脱敏方案(word)

数据脱敏工作不仅要确保敏感信息被去除,还需要尽可能的平衡脱敏所花费的代价、使用方的业务需求等多个因素。因此,为了确保数据脱敏的过程、代价可控,得到的结果正确且满足业务需要,在实施数据脱敏时,应从技术和管理两…

stm32之I2C通信协议

文章目录 前言一、I2C通信协议二、I2C硬件电路三、I2C时序基本单元3.1 起始与终止信号3.2 发送与接收一个字节3.3 发送与接收应答 四、I2C时序分析4.1 指定地址写4.2 当前地址读4.3 指定地址读 前言 提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总…

刷机维修进阶教程-----紫光展讯芯片修改参数 修复基带 信号的一种步骤解析【二】

上期解析了一款紫光芯片机型 改写参数的步骤。今天来讲解另外一款紫光新机型修改参数的具体步骤。同类紫光展讯 展锐芯片机型可以参考尝试 通过博文了解; 1---紫光芯片机型新款机型改写参数的步骤解析 2----了解同类芯片修改参数的常用步骤 3----列举一些紫光芯片机型开启…

OpenStack常见模块详解

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

“NoSQL数据库技术及其应用”写作框架,软考高级,系统架构设计师

论文真题 随着互联网web2.0网站的兴起,传统关系数据库在应对web2.0 网站,特别是超大规模和高并发的web2.0纯动态SNS网站上已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展…

Zookeeper的watch机制是如何工作的?

ZooKeeper Watch 概述 ZooKeeper Watch 机制类似于 Java 设计模式中的观察者模式或者监听模式,唯一的不同是不再基于线程间通信,而是基于进程间通信。 ZooKeeper Watch 机制是指,客户端在所有的读命令上告知服务端:这个节点或者…