u
832
专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html
Normal file
@@ -0,0 +1,832 @@
|
||||
<!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>00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md</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 class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发</h1>
|
||||
|
||||
<p>你好,我是那朋,在 Java 领域从业十年,曾负责京东金融白条、金条等相关业务的技术架构研发工作,目前在一家在线教育公司担任架构师,负责公司整体的系统架构工作。</p>
|
||||
|
||||
<p>这十年,我见证了系统从单一架构到垂直架构,再到分布式架构的技术发展过程,也目睹了传统软件行业到互联网行业的快速更迭。面对大流量高并发的用户访问,以及随之产生的海量数据处理等诸多挑战,如何能为用户提供稳定可靠的服务,成为目前很多互联网大公司面临的技术问题。</p>
|
||||
|
||||
<p>比如,每年的京东 618 购物节,仅是网站支付系统的京东白条接口,每分钟的访问量都是上千万次,这是一个非常大的流量冲击,相当于单体架构下上万台机器总和的处理能力。而支付系统又是购物中最重要的一环,要想在这样的高并发场景下实现“5 个 9”(99.999%)的可用性,来保证支付成功率,使用单一架构显然是无法做到的。</p>
|
||||
|
||||
<p>而如果采用集群方式的垂直架构,当业务不断发展,应用和服务会变得越来越多,项目的维护和部署也会变得极为复杂。</p>
|
||||
|
||||
<p>因此,越来越多的公司采用分布式架构来开发自己的业务系统,通过将一个系统横向切分成若干个子系统或服务,实现服务性能的动态扩容。这样不但大幅提高了服务的处理能力,而且降低了程序的开发维护以及部署的难度。</p>
|
||||
|
||||
<p>掌握分布式系统相关知识的 IT 从业人员因此成为各大公司争抢的对象,从拉勾招聘平台我们也可以看到,分布式系统开发工程师的薪水也相对更高,平均起薪 25k 以上。正因如此,学习和提高分布式系统开发能力,也<strong>成为传统软件开发人员转行和寻求高薪职位的必要条件</strong>。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl6yLsaAJf-3AACcfiC01UY046.png" alt="image" />.png]</p>
|
||||
|
||||
<h4>学好 ZooKeeper,提升分布式开发与架构能力</h4>
|
||||
|
||||
<p>分布式技术也因此有了很多拥趸,但在工作以及和朋友的交流中,我也发现了人才供需之间的一些矛盾:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>我本身就职于传统的软件开发企业,没有分布式系统的学习与实践环境,如何更好地学习分布式知识呢?</li>
|
||||
|
||||
<li>分布式知识非常零散,涉及网络传输、进程间通信、事务的并发与控制、安全性等诸多知识点,应该从何处入手?面对市面上 ZooKeeper、Dubbo、Kafka 等诸多开源框架,应该如何选择?</li>
|
||||
|
||||
<li>大多数面试者,都或多或少地掌握一些分布式相关知识,但在问及深层次的原理和为什么要这样做的时候,往往就顾左右而言他。</li>
|
||||
|
||||
<li>工作中能够通过在网络上搜索解决一些常见的技术问题,但每个公司和项目的架构都各有特点,在遇到特殊使用场景或特定程序运行环境下产生的问题时,却没有了解决思路。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>这其实是由于对分布式技术体系缺乏整体认识,导致在学习分布式的时候总觉得无从下手。而开始接触分布式开发的相关人员对一些框架(比如 ZooKeeper)的底层原理并不了解,在产生问题的时候就无法很好地定位问题。</p>
|
||||
|
||||
<p><strong>而对于选择从事分布式开发,或者想进一步提高分布式架构能力的技术人员,我认为深入学习 ZooKeeper 是最佳选择。</strong></p>
|
||||
|
||||
<p>这是因为一个分布式系统的本质,是分布在不同网络或计算机上的程序或组件,彼此通过信息传递来协同工作的系统,而 ZooKeeper 正是一个分布式应用协调框架,在分布式系统架构中具有广泛的应用场景,是业界首选的一致性解决方案。而其开源的特性更是为我们学习底层原理,进一步提高分布式架构设计的能力提供了很好的帮助。</p>
|
||||
|
||||
<p>ZooKeeper 可以实现分布式系统下的配置管理、域名服务、分布式同步、发布订阅等使用场景,而这些场景基本就是分布式系统中最常见的问题,因此可以说:<strong>掌握了 ZooKeeper,就是掌握了分布式系统最关键的知识</strong>。</p>
|
||||
|
||||
<h4>如何才能学好 ZooKeeper?</h4>
|
||||
|
||||
<p>2015年,我最开始学习 ZooKeeper 的时候,市面上的学习资料非常少,我只能通过参考官方文档,然后在工作中不断摸索实践来总结经验,当时往往知其然而不知其所以然,只是简单地掌握了 ZooKeeper 应用层 API 的使用方法,而不知道其底层实现原理,因此在实际的应用场景和面试中遇到了各种各样的问题。</p>
|
||||
|
||||
<p>为此,我开始深入研究源码来了解 ZooKeeper 的底层实现,分析产生问题的原因,渐渐地我对 ZooKeeper 开始有了更为深度的理解,而这方面能力的提升也非常明显地体现在日常工作实践中:我发现自己在复杂的分布式环境下,定位问题的效率提升了,并且可以快速、有效地找到解决方法,解决复杂多变的实际问题。</p>
|
||||
|
||||
<p>记得一次用 ZooKeeper 实现一个分布式锁,在生产环境运行的时候出现了加锁错误的问题,具体表现在持有锁的 c1 客户端在未主动释放锁的情况下,另一个 c2 客户端也成功获取了锁,最终导致程序运行错误。这种在本地调试排查问题的时候没有任何异常,上线却出现了问题,而本地又找不到错误的情况,相信也是很多开发人员最苦恼的了。</p>
|
||||
|
||||
<p>我第一时间搜索答案但未果,于时开始从底层实现角度去分析问题。最终发现,原来是因为运行客户端 c1 的 JVM 发生 GC,导致服务器没有检测到 c1 客户端的”心跳“,误认为客户端下线而自动删除了临时节点,从而产生了分布式锁失效的情况。<strong>定位了问题缘由,解决问题就是自然而然的事儿了。</strong></p>
|
||||
|
||||
<p>从我的经历中你可以看出,BAT、京东、滴滴这些大型互联网公司对技术人员的要求更高,而它们的相关职位也基本占据了薪资金字塔的顶层。面对激烈的行业竞争,除了知识的广度,我们更应该注重知识的深度,知其然更知其所以然,才能有脱颖而出的机会。</p>
|
||||
|
||||
<p>同理,如果你想真正掌握 ZooKeeper 这门技术,不能局限在软件应用层面,而是应该从实际的应用场景出发,学习具体的解决方案,再深入底层分析实现原理,具备相关的实践经验,这样才是真正地学会了。</p>
|
||||
|
||||
<p>但是目前网络上的一些学习资料,往往非常零散,不够完备和体系化;一些课程和图书又往往重点讲解 ZooKeeper 的使用技巧,而没有深入分析底层的实现原理和设计目的,对于想进一步提高 ZooKeeper 能力的人帮助甚微。</p>
|
||||
|
||||
<p>而这也正是我写作这个课程的初心。为了能够让你更好地理解底层原理技术,我想要帮助你从零开始学习 ZooKeeper,但不只是单纯地讲解概念和理论,而是从 ZooKeeper 的每个知识点出发,结合实际应用场景来抛出问题,并给出解决方案,带着你思考问题的解决思路。</p>
|
||||
|
||||
<p>要想掌握一个开源框架最底层的实现原理,最直接的方式就是阅读源代码,但是通常需要投入大量的精力和时间。而且,很多人阅读代码的方式就是部署好框架源码,然后从程序的入口到程序的出口一步步地调试来跟踪程序的执行逻辑,但是在这个过程中,你往往会被程序复杂地调用搞晕,最后也就失去了阅读源码的兴趣。</p>
|
||||
|
||||
<p>在这个专栏,我会从 ZooKeeper 的每个知识点和核心功能入手,将复杂的 ZooKeeper 框架划分为各个功能独立的模块,带你从源码层面分析底层的实现原理,在节省时间的同时,希望能够让你对 ZooKeeper 和分布式问题的解决方法有更深层次的、体系化的认知。</p>
|
||||
|
||||
<h4>课程设置</h4>
|
||||
|
||||
<p>本课程分 4个模块,共 32 篇文章,从基础知识点到底层原理全面深入展开介绍:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><strong>基础篇</strong>:介绍 ZooKeeper 的数据节点、Watch 机制、ACL 权限控制、Jute 序列化等基础知识,并结合实际工作中的应用场景帮助你强化对这些知识点的理解,为后面的学习打下基础。</li>
|
||||
|
||||
<li><strong>进阶篇</strong>:ZooKeeper 主要通过客户端与服务端的相互通信完成工作,而会话则是其中最核心的概念。诸如临时节点、Watch 监控机制等功能和特性都与客户端的会话状态紧紧相关,所以了解 Zookeeper 服务器从创建到对外提供服务的整个过程,清楚会话在 Zookeeper 服务运行过程的不同状态,以及掌握 Zookeeper 的会话管理策略和底层实现原理非常重要。之后,我还会带你学习如何使用 Curator 来降低开发 ZooKeeper 的复杂度。</li>
|
||||
|
||||
<li><strong>高级篇</strong>:介绍了 ZooKeeper 集群的工作方式以及内部的实现原理,重点介绍了 ZooKeeper 的 Leader 群首选举算法,集群中的 Leader、Follow、Observer 3 种角色和其各自功能。之后,我还深入源码层面分析了 3 种角色处理请求的内部实现,以及相互间的通信协作机制。</li>
|
||||
|
||||
<li><strong>核心原理与实战篇</strong>:在掌握了 ZooKeeper 相关知识点的情况下,这个模块中我将再带你学习分布式事务算法二阶段提交、三阶段提交,以及 Paxos、ZAB 协议等算法,进一步加强你对 Zookeeper 的理解,并通过大量的实战案例分析,帮助你提高在日常工作中使用 ZooKeeper 解决问题的能力。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>如果你是以下用户,那么本课程一定适合你:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><strong>希望从事分布式系统开发的技术人员</strong>:本课程非常适合作为分布式开发的入门,其中不但全面系统地讲解了 ZooKeeper 的相关知识,而且大量的案例和丰富的使用场景使你能够快速掌握分布式开发技术。</li>
|
||||
|
||||
<li><strong>希望进一步提高分布式技术能力的工程师</strong>:课程中的每个模块,都涉及对源码的解析,希望加深你对 ZooKeeper 架构以及底层实现原理的了解,从而对解决工作中多变的现实问题打下坚实的基础。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h4>结语</h4>
|
||||
|
||||
<p>这些年来,我一直工作在技术开发的最前线,负责过很多产品的架构工作,并因为工作原因,面试了相当多的求职者。我深知一名技术人员在“征服”薪资金字塔的过程中需要经过多少道修炼。技术更新的速度太快了,没有一个工程师能够停下学习的步伐,但学习不应该是焦虑的过程,从本质内容入手,我们能够更从容、有效地提升自己的技术能力,让自己的职业发展走得更顺畅、更长久。</p>
|
||||
|
||||
<p>工程师之所以会获得“攻城师”的称号,不仅因为在工作中要不断征服各种难题,更因为这个过程的漫长性,想要升职加薪,就要先征服眼前的“星辰大海”。很荣幸我能够在拉勾为你分享我的学习经验,为你指引一些方向。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">下一页</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":"709975c0a8183cfa","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>
|
||||
|
||||
926
专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md.html
Normal file
@@ -0,0 +1,926 @@
|
||||
<!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>01 ZooKeeper 数据模型:节点的特性与应用.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>01 ZooKeeper 数据模型:节点的特性与应用</h1>
|
||||
|
||||
<p>你好,我是那朋,ZooKeeper 专栏作者。</p>
|
||||
|
||||
<p>正如开篇词提到的,ZooKeeper 作为一个分布式协调服务,给出了在分布式环境下一致性问题的工业解决方案,目前流行的很多开源框架技术背后都有 ZooKeeper 的身影。那么 ZooKeeper 是如何做到这一点的,在平时开发中我们应该如何使用 ZooKeeper?要想了解这些问题,我们先要对 ZooKeeper 的基础知识进行全面的掌握。</p>
|
||||
|
||||
<p>ZooKeeper 基础知识基本分为三大模块:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>数据模型</li>
|
||||
|
||||
<li>ACL 权限控制</li>
|
||||
|
||||
<li>Watch 监控</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>其中,数据模型是最重要的,很多 ZooKeeper 中典型的应用场景都是利用这些基础模块实现的。比如我们可以利用数据模型中的临时节点和 Watch 监控机制来实现一个发布订阅的功能。</p>
|
||||
|
||||
<p>因此,今天主要通过理论知识结合实际的应用场景来给你介绍数据模型。掌握本课时的知识对于理解 ZooKeeper 内部原理,以及在日常工作中使用好 ZooKeeper 非常重要。</p>
|
||||
|
||||
<h3>数据模型</h3>
|
||||
|
||||
<p>计算机最根本的作用其实就是处理和存储数据,作为一款分布式一致性框架,ZooKeeper 也是如此。数据模型就是 ZooKeeper 用来存储和处理数据的一种逻辑结构。就像我们用 MySQL 数据库一样,要想处理复杂业务。前提是先学会如何往里边新增数据。ZooKeeper 数据模型最根本的功能就像一个数据库。</p>
|
||||
|
||||
<p>现在,数据模型对我们来说还是一个比较抽象的概念,接下来我们开始部署一个开发测试环境,并在上面做一些简单的操作。来看看 ZooKeeper 的数据模型究竟是什么样的:</p>
|
||||
|
||||
<ol>
|
||||
|
||||
<li>配置文件</li>
|
||||
|
||||
</ol>
|
||||
|
||||
<pre><code class="language-java">tickTime=2000
|
||||
|
||||
|
||||
|
||||
dataDir=/var/lib/zookeeper
|
||||
|
||||
|
||||
|
||||
clientPort=2181
|
||||
|
||||
</code></pre>
|
||||
|
||||
<ol>
|
||||
|
||||
<li>服务启动</li>
|
||||
|
||||
</ol>
|
||||
|
||||
<pre><code class="language-java">bin/zkServer.sh start
|
||||
|
||||
</code></pre>
|
||||
|
||||
<ol>
|
||||
|
||||
<li>使用客户端连接服务器</li>
|
||||
|
||||
</ol>
|
||||
|
||||
<pre><code class="language-java">bin/zkCli.sh -server 127.0.0.1:2181
|
||||
|
||||
</code></pre>
|
||||
|
||||
<ol>
|
||||
|
||||
<li>这样单机版的开发环境就已经构建完成了,接下来我们通过 ZooKeeper 提供的 create 命令来创建几个节点,分别是:“/locks”“/servers”“/works”:</li>
|
||||
|
||||
</ol>
|
||||
|
||||
<pre><code class="language-java">create /locks
|
||||
|
||||
|
||||
|
||||
create /servers
|
||||
|
||||
|
||||
|
||||
create /works
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>最终在 ZooKeeper 服务器上会得到一个具有层级关系的数据结构,如下图所示,这个数据结构就是 ZooKeeper 中的数据模型。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F6yL9OAUUguAAAtBwGI74E989.png" alt="image" /></p>
|
||||
|
||||
<p>ZooKeeper 中的数据模型是一种树形结构,非常像电脑中的文件系统,有一个根文件夹,下面还有很多子文件夹。ZooKeeper 的数据模型也具有一个固定的根节点(/),我们可以在根节点下创建子节点,并在子节点下继续创建下一级节点。ZooKeeper 树中的每一层级用斜杠(/)分隔开,且只能用绝对路径(如“get /work/task1”)的方式查询 ZooKeeper 节点,而不能使用相对路径。具体的结构你可以看看下面这张图:</p>
|
||||
|
||||
<p><img src="assets/CgqCHl6yL9uAbpHYAABF_GHyGNc950.png" alt="image" /></p>
|
||||
|
||||
<h3>znode 节点类型与特性</h3>
|
||||
|
||||
<p>知道了 ZooKeeper 的数据模型是一种树形结构,就像在 MySQL 中数据是存在于数据表中,ZooKeeper 中的数据是由多个数据节点最终构成的一个层级的树状结构,和我们在创建 MySOL 数据表时会定义不同类型的数据列字段,ZooKeeper 中的数据节点也分为持久节点、临时节点和有序节点三种类型:</p>
|
||||
|
||||
<h4>1、持久节点</h4>
|
||||
|
||||
<p>我们第一个介绍的是持久节点,这种节点也是在 ZooKeeper 最为常用的,几乎所有业务场景中都会包含持久节点的创建。之所以叫作持久节点是因为一旦将节点创建为持久节点,该数据节点会一直存储在 ZooKeeper 服务器上,即使创建该节点的客户端与服务端的会话关闭了,该节点依然不会被删除。如果我们想删除持久节点,就要显式调用 delete 函数进行删除操作。</p>
|
||||
|
||||
<h4>2、临时节点</h4>
|
||||
|
||||
<p>接下来我们来介绍临时节点。从名称上我们可以看出该节点的一个最重要的特性就是临时性。所谓临时性是指,如果将节点创建为临时节点,那么该节点数据不会一直存储在 ZooKeeper 服务器上。当创建该临时节点的客户端会话因超时或发生异常而关闭时,该节点也相应在 ZooKeeper 服务器上被删除。同样,我们可以像删除持久节点一样主动删除临时节点。</p>
|
||||
|
||||
<p>在平时的开发中,我们可以利用临时节点的这一特性来做服务器集群内机器运行情况的统计,将集群设置为“/servers”节点,并为集群下的每台服务器创建一个临时节点“/servers/host”,当服务器下线时该节点自动被删除,最后统计临时节点个数就可以知道集群中的运行情况。如下图所示:</p>
|
||||
|
||||
<p><img src="assets/CgqCHl6yL-SAb0zaAABQBLohKvo019.png" alt="image" /></p>
|
||||
|
||||
<h4>3、有序节点</h4>
|
||||
|
||||
<p>最后我们再说一下有序节点,其实有序节点并不算是一种单独种类的节点,而是在之前提到的持久节点和临时节点特性的基础上,增加了一个节点有序的性质。所谓节点有序是说在我们创建有序节点的时候,ZooKeeper 服务器会自动使用一个单调递增的数字作为后缀,追加到我们创建节点的后边。例如一个客户端创建了一个路径为 works/task- 的有序节点,那么 ZooKeeper 将会生成一个序号并追加到该节点的路径后,最后该节点的路径为 works/task-1。通过这种方式我们可以直观的查看到节点的创建顺序。</p>
|
||||
|
||||
<p>到目前为止我们知道在 ZooKeeper 服务器上存储数据的基本信息,知道了 ZooKeeper 中的数据节点种类有持久节点和临时节点等。上述这几种数据节点虽然类型不同,但 ZooKeeper 中的每个节点都维护有这些内容:一个二进制数组(byte data[]),用来存储节点的数据、ACL 访问控制信息、子节点数据(因为临时节点不允许有子节点,所以其子节点字段为 null),除此之外每个数据节点还有一个记录自身状态信息的字段 stat。</p>
|
||||
|
||||
<p>下面我们详细说明节点的状态信息。</p>
|
||||
|
||||
<h3>节点的状态结构</h3>
|
||||
|
||||
<p>每个节点都有属于自己的状态信息,这就很像我们每个人的身份信息一样,我们打开之前的客户端,执行 stat /zk_test,可以看到控制台输出了一些信息,这些就是节点状态信息。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F6yL-yAKn9QAABsJSpQkFI688.png" alt="image" /></p>
|
||||
|
||||
<p>每一个节点都有一个自己的状态属性,记录了节点本身的一些信息,这些属性包括的内容我列在了下面这个表格里:</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F6zbwWAVkt5AAC_yMQVCFo712.png" alt="表.png" /></p>
|
||||
|
||||
<h3>数据节点的版本</h3>
|
||||
|
||||
<p>这里我们重点讲解一下版本相关的属性,在 ZooKeeper 中为数据节点引入了版本的概念,每个数据节点有 3 种类型的版本信息,对数据节点的任何更新操作都会引起版本号的变化。ZooKeeper 的版本信息表示的是对节点数据内容、子节点信息或者是 ACL 信息的修改次数。</p>
|
||||
|
||||
<h4>使用 ZooKeeper 实现锁</h4>
|
||||
|
||||
<p>学习了 ZooKeeper 的数据模型和数据节点的相关知识,下面我们通过实际的应用进一步加深理解。</p>
|
||||
|
||||
<p>设想这样一个情景:一个购物网站,某个商品库存只剩一件,客户 A 搜索到这件商品并准备下单,但在这期间客户 B 也查询到了该件商品并提交了购买,于此同时,客户 A 也下单购买了此商品,这样就出现了只有一件库存的商品实际上卖出了两件的情况。为了解决这个问题,我们可以在客户 A 对商品进行操作的时候对这件商品进行锁定从而避免这种超卖的情况发生。</p>
|
||||
|
||||
<p>实现锁的方式有很多中,这里我们主要介绍两种:悲观锁、乐观锁。</p>
|
||||
|
||||
<p><strong>悲观锁</strong>
|
||||
|
||||
悲观锁认为进程对临界区的竞争总是会出现,为了保证进程在操作数据时,该条数据不被其他进程修改。数据会一直处于被锁定的状态。</p>
|
||||
|
||||
<p>我们假设一个具有 n 个进程的应用,同时访问临界区资源,我们通过进程创建 ZooKeeper 节点 /locks 的方式获取锁。</p>
|
||||
|
||||
<p>线程 a 通过成功创建 ZooKeeper 节点“/locks”的方式获取锁后继续执行,如下图所示:</p>
|
||||
|
||||
<p><img src="assets/CgqCHl6yL_WAAnymAAB32xbrhxQ973.png" alt="image" /></p>
|
||||
|
||||
<p>这时进程 b 也要访问临界区资源,于是进程 b 也尝试创建“/locks”节点来获取锁,因为之前进程 a 已经创建该节点,所以进程 b 创建节点失败无法获得锁。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl6yL_6AOIONAAB3daUjikw147.png" alt="image" /></p>
|
||||
|
||||
<p>这样就实现了一个简单的悲观锁,不过这也有一个隐含的问题,就是当进程 a 因为异常中断导致 /locks 节点始终存在,其他线程因为无法再次创建节点而无法获取锁,这就产生了一个死锁问题。针对这种情况我们可以通过将节点设置为临时节点的方式避免。并通过在服务器端添加监听事件来通知其他进程重新获取锁。</p>
|
||||
|
||||
<p><strong>乐观锁</strong>
|
||||
|
||||
乐观锁认为,进程对临界区资源的竞争不会总是出现,所以相对悲观锁而言。加锁方式没有那么激烈,不会全程的锁定资源,而是在数据进行提交更新的时候,对数据的冲突与否进行检测,如果发现冲突了,则拒绝操作。</p>
|
||||
|
||||
<p>**乐观锁基本可以分为读取、校验、写入三个步骤。**CAS(Compare-And-Swap),即比较并替换,就是一个乐观锁的实现。CAS 有 3 个操作数,内存值 V,旧的预期值 A,要修改的新值 B。当且仅当预期值 A 和内存值 V 相同时,将内存值 V 修改为 B,否则什么都不做。</p>
|
||||
|
||||
<p>在 ZooKeeper 中的 version 属性就是用来实现乐观锁机制中的“校验”的,ZooKeeper 每个节点都有数据版本的概念,在调用更新操作的时候,假如有一个客户端试图进行更新操作,它会携带上次获取到的 version 值进行更新。而如果在这段时间内,ZooKeeper 服务器上该节点的数值恰好已经被其他客户端更新了,那么其数据版本一定也会发生变化,因此肯定与客户端携带的 version 无法匹配,便无法成功更新,因此可以有效地避免一些分布式更新的并发问题。</p>
|
||||
|
||||
<p>在 ZooKeeper 的底层实现中,当服务端处理 setDataRequest 请求时,首先会调用 checkAndIncVersion 方法进行数据版本校验。ZooKeeper 会从 setDataRequest 请求中获取当前请求的版本 version,同时通过 getRecordForPath 方法获取服务器数据记录 nodeRecord, 从中得到当前服务器上的版本信息 currentversion。如果 version 为 -1,表示该请求操作不使用乐观锁,可以忽略版本对比;如果 version 不是 -1,那么就对比 version 和 currentversion,如果相等,则进行更新操作,否则就会抛出 BadVersionException 异常中断操作。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl6yMBKAZzwGAABPrrtajyI575.png" alt="image" /></p>
|
||||
|
||||
<h3>总结</h3>
|
||||
|
||||
<p>本节课主要介绍了ZooKeeper的基础知识点——数据模型。并深入介绍了节点类型、stat 状态属性等知识,并利用目前学到的知识解决了集群中服务器运行情况统计、悲观锁、乐观锁等问题。这些知识对接下来的课程至关重要,请务必掌握。</p>
|
||||
|
||||
<p>了解了 ZooKeeper 数据模型的基本原理后,我们来思考一个问题:<strong>为什么 ZooKeeper 不能采用相对路径查找节点呢?</strong></p>
|
||||
|
||||
<p>这是因为 ZooKeeper 大多是应用场景是定位数据模型上的节点,并在相关节点上进行操作。像这种查找与给定值相等的记录问题最适合用散列来解决。因此 ZooKeeper 在底层实现的时候,使用了一个 hashtable,即 hashtableConcurrentHashMap<String, DataNode> nodes ,用节点的完整路径来作为 key 存储节点数据。这样就大大提高了 ZooKeeper 的性能。</p>
|
||||
|
||||
<blockquote>
|
||||
|
||||
<p>引用参考
|
||||
|
||||
官网:
|
||||
|
||||
<a href="https://zookeeper.apache.org/doc/r3.6.0/zookeeperProgrammers.html">https://zookeeper.apache.org/doc/r3.6.0/zookeeperProgrammers.html</a></p>
|
||||
|
||||
</blockquote>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">下一页</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":"709975c36e973cfa","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>
|
||||
|
||||
1420
专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html
Normal file
1050
专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md.html
Normal file
982
专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md.html
Normal file
@@ -0,0 +1,982 @@
|
||||
<!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>04 ZooKeeper 如何进行序列化?.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>04 ZooKeeper 如何进行序列化?</h1>
|
||||
|
||||
<p>通过前几课时的学习,我们大概清楚了使用 ZooKeeper 实现一些功能的主要方式,也就是通过客户端与服务端之间的相互通信。那么首先要解决的问题就是通过网络传输数据,而要想通过网络传输我们定义好的 Java 对象数据,必须要先对其进行序列化。例如,我们通过 ZooKeeper 客户端发送 ACL 权限控制请求时,需要把请求信息封装成 packet 类型,经过序列化后才能通过网络将 ACL 信息发送给 ZooKeeper 服务端进行处理。</p>
|
||||
|
||||
<h4>什么是序列化,为什么要进行序列化操作</h4>
|
||||
|
||||
<p>序列化是指将我们定义好的 Java 类型转化成数据流的形式。之所以这么做是因为在网络传输过程中,TCP 协议采用“流通信”的方式,提供了可以读写的字节流。而这种设计的好处在于避免了在网络传输过程中经常出现的问题:比如消息丢失、消息重复和排序等问题。那么什么时候需要序列化呢?如果我们需要通过网络传递对象或将对象信息进行持久化的时候,就需要将该对象进行序列化。</p>
|
||||
|
||||
<p>我们较为熟悉的序列化操作是在 Java中,当我们要序列化一个对象的时候,首先要实现一个 Serializable 接口。</p>
|
||||
|
||||
<pre><code class="language-java">public class User implements Serializable{
|
||||
|
||||
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
|
||||
private Long ids;
|
||||
|
||||
|
||||
|
||||
private String name;
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>实现了 Serializable 接口后其实没有做什么实际的工作,它是一个没有任何内容的空接口,起到的作用就是标识该类是需要进行序列化的,这个就与我们后边要重点讲解的 ZooKeeper 序列化实现方法有很大的不同,这里请你先记住当前的写法,后边我们会展开讲解。</p>
|
||||
|
||||
<pre><code class="language-java">public interface Serializable {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>定义好序列化接口后,我们再看一下如何进行序列化和反序列化的操作。Java 中进行序列化和反序列化的过程中,主要用到了 ObjectInputStream 和 ObjectOutputStream 两个 IO 类。</p>
|
||||
|
||||
<p>ObjectOutputStream 负责将对象进行序列化并存储到本地。而 ObjectInputStream 从本地存储中读取对象信息反序列化对象。</p>
|
||||
|
||||
<pre><code class="language-java">//序列化
|
||||
|
||||
|
||||
|
||||
ObjectOutputStream oo = new ObjectOutputStream()
|
||||
|
||||
|
||||
|
||||
oo.writeObject(user);
|
||||
|
||||
|
||||
|
||||
//反序列化
|
||||
|
||||
|
||||
|
||||
ObjectInputStream ois = new ObjectInputStream();
|
||||
|
||||
|
||||
|
||||
User user = (User) ois.readObject();
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>到目前为止我们了解了什么是序列化,以及为什么要进行序列化,并通过我们熟悉的 Java 编程语言中的序列化实现,进一步对序列化操作有更加具体的了解。我们知道,当我们要把对象进行本地存储或网络传输时是需要进行序列化操作的,而在 ZooKeeper 中需要频繁的网络传输工作,那么在 ZooKeeper 中是如何进行序列化的呢,我们带着这个问题继续下面的学习。</p>
|
||||
|
||||
<h4>ZooKeeper 中的序列化方案</h4>
|
||||
|
||||
<p>在 ZooKeeper 中并没有采用和 Java 一样的序列化方式,而是采用了一个 Jute 的序列解决方案作为 ZooKeeper 框架自身的序列化方式,说到 Jute 框架,它最早作为 Hadoop 中的序列化组件。之后 Jute 从 Hadoop 中独立出来,成为一个独立的序列化解决方案。ZooKeeper 从最开始就采用 Jute 作为其序列化解决方案,直到其最新的版本依然没有更改。</p>
|
||||
|
||||
<p>虽然 ZooKeeper 一直将 Jute 框架作为序列化解决方案,但这并不意味着 Jute 相对其他框架性能更好,反倒是 Apache Avro、Thrift 等框架在性能上优于前者。之所以 ZooKeeper 一直采用 Jute 作为序列化解决方案,主要是新老版本的兼容等问题,这里也请你注意,也许在之后的版本中,ZooKeeper 会选择更加高效的序列化解决方案。</p>
|
||||
|
||||
<h4>使用 Jute 实现序列化</h4>
|
||||
|
||||
<p>简单介绍了 Jute 框架的发展过程,下面我们来看一下如何使用 Jute 在 ZooKeeper 中实现序列化。如果我们要想将某个定义的类进行序列化,首先需要该类实现 Record 接口的 serilize 和 deserialize 方法,这两个方法分别是序列化和反序列化方法。下边这段代码给出了我们一般在 ZooKeeper 中进行序列化的具体实现:首先,我们定义了一个 test_jute 类,为了能够对它进行序列化,需要该 test_jute 类实现 Record 接口,并在对应的 serialize 序列化方法和 deserialize 反序列化方法中编辑具体的实现逻辑。</p>
|
||||
|
||||
<pre><code class="language-java">class test_jute implements Record{
|
||||
|
||||
|
||||
|
||||
private long ids;
|
||||
|
||||
|
||||
|
||||
private String name;
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
public void serialize(OutpurArchive a_,String tag){
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void deserialize(INputArchive a_,String tag){
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>而在序列化方法 serialize 中,我们要实现的逻辑是,首先通过字符类型参数 tag 传递标记序列化标识符,之后使用 writeLong 和 writeString 等方法分别将对象属性字段进行序列化。</p>
|
||||
|
||||
<pre><code class="language-java">public void serialize(OutpurArchive a_,String tag) throws ...{
|
||||
|
||||
|
||||
|
||||
a_.startRecord(this.tag);
|
||||
|
||||
|
||||
|
||||
a_.writeLong(ids,"ids");
|
||||
|
||||
|
||||
|
||||
a_.writeString(type,"name");
|
||||
|
||||
|
||||
|
||||
a_.endRecord(this,tag);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>而调用 derseralize 在实现反序列化的过程则与我们上边说的序列化过程正好相反。</p>
|
||||
|
||||
<pre><code class="language-java">public void deserialize(INputArchive a_,String tag) throws {
|
||||
|
||||
|
||||
|
||||
a_.startRecord(tag);
|
||||
|
||||
|
||||
|
||||
ids = a_.readLong("ids");
|
||||
|
||||
|
||||
|
||||
name = a_.readString("name");
|
||||
|
||||
|
||||
|
||||
a_.endRecord(tag);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>到这里我们就介绍完了如何在 ZooKeeper 中使用 Jute 实现序列化,需要注意的是,<strong>在实现了Record 接口后,具体的序列化和反序列化逻辑要我们自己在 serialize 和 deserialize 函数中完成</strong>。</p>
|
||||
|
||||
<p>序列化和反序列化的实现逻辑编码方式相对固定,首先通过 startRecord 开启一段序列化操作,之后通过 writeLong、writeString 或 readLong、 readString 等方法执行序列化或反序列化。本例中只是实现了长整型和字符型的序列化和反序列化操作,除此之外 ZooKeeper 中的 Jute 框架还支持 整数类型(Int)、布尔类型(Bool)、双精度类型(Double)以及 Byte/Buffer 类型。</p>
|
||||
|
||||
<h4>Jute 在 ZooKeeper 中的底层实现</h4>
|
||||
|
||||
<p>正因为 ZooKeeper 的设计目的是将复杂的底层操作封装成简单易用的接口,从而方便用户调用,也使得我们在使用 ZooKeeper 实现序列化的时候能够更加容易。</p>
|
||||
|
||||
<p>学会了利用 Jute 实现序列化和反序列化后,我们深入底层,看一下 ZooKeeper 框架具体是如何实现序列化操作的。正如上边我们提到的,通过简单的实现 Record 接口就可以实现序列化,那么我们接下来就以这个接口作为入口,详细分析其底层原理。</p>
|
||||
|
||||
<p>Record 接口可以理解为 ZooKeeper 中专门用来进行网络传输或本地存储时使用的数据类型。因此所有我们实现的类要想传输或者存储到本地都要实现该 Record 接口。</p>
|
||||
|
||||
<pre><code class="language-java">public interface Record{
|
||||
|
||||
|
||||
|
||||
public void serialize(OutputArchive archive, String tag)
|
||||
|
||||
|
||||
|
||||
throws IOException;
|
||||
|
||||
|
||||
|
||||
public void deserialize(InputArchive archive, String tag)
|
||||
|
||||
|
||||
|
||||
throws IOException;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>Record 接口的内部实现逻辑非常简单,只是定义了一个 序列化方法 serialize 和一个反序列化方法 deserialize 。而在 Record 起到关键作用的则是两个重要的类:OutputArchive 和 InputArchive ,其实这两个类才是真正的序列化和反序列化工具类。</p>
|
||||
|
||||
<p>在 OutputArchive 中定义了可进行序列化的参数类型,根据不同的序列化方式调用不同的实现类进行序列化操作。如下图所示,Jute 可以通过 Binary 、 Csv 、Xml 等方式进行序列化操作。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F6-a8CAUVfhAABcAV_NNXw402.png" alt="image" /></p>
|
||||
|
||||
<p>而对应于序列化操作,在反序列化时也会相应调用不同的实现类,来进行反序列化操作。
|
||||
|
||||
如下图所示:</p>
|
||||
|
||||
<p><img src="assets/CgqCHl6-a8mAOP1YAABW8fO1GAM913.png" alt="image" /></p>
|
||||
|
||||
<p>注意:无论是序列化还是反序列化,都可以对多个对象进行操作,所以当我们在定义序列化和反序列化方法时,需要字符类型参数 tag 表示要序列化或反序列化哪个对象。</p>
|
||||
|
||||
<h4>总结</h4>
|
||||
|
||||
<p>本课时介绍了什么是序列化以及为什么要进行序列化,简单来说,就是将对象编译成字节码的形式,方便将对象信息存储到本地或通过网络传输。</p>
|
||||
|
||||
<p>之后我们复习了 Java 中的序列化方式,与 ZooKeeper 中的序列化使用不同的是,在 Java 中,Serializable 接口是一个空接口,只是起到标识作用,实现了该接口的对象是需要进行序列化的。而在 ZooKeeper 的实现中,序列化对象需要实现的 Record 接口里边存在两个重要的方法,分别是 serialize 和 deserialize 方法。需要序列化的对象在继承 Record 接口的同时,还需要实现这两个方法。而具体的实现过程就是我们要序列化和反序列化的实现逻辑。</p>
|
||||
|
||||
<p>到现在我们已经对 ZooKeeper 中的序列化知识有了一个全面的掌握,请你思考一个问题:如果说序列化就是将对象转化成字节流的格式,那么为什么 ZooKeeper 的 Jute 序列化框架还提供了对 Byte/Buffer 这两种类型的序列化操作呢?</p>
|
||||
|
||||
<p>其实这其中最关键的作用就是在不同操作系统中存在大端和小端的问题,为了避免不同操作系统环境的差异,在传输字节类型时也要进行序列化和反序列化。这里你需要在日常使用中多注意。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">下一页</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":"709975caee933cfa","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>
|
||||
|
||||
1040
专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md.html
Normal file
964
专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md.html
Normal file
@@ -0,0 +1,964 @@
|
||||
<!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>06 ZooKeeper 的网络通信协议详解.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>06 ZooKeeper 的网络通信协议详解</h1>
|
||||
|
||||
<p>本节课我们将学习 ZooKeeper 的网络通信协议。同时,本节课也是基础篇中的最后一节课。在 ZooKeeper 中无论是客户端和服务器之间的通信,还是集群之间服务器的内部协同工作都是基于网络进行通信的。而网络通信协议则是影响 ZooKeeper 性能和稳定性的核心点。</p>
|
||||
|
||||
<h3>ZooKeeper 协议简述</h3>
|
||||
|
||||
<p>说到网络通信协议我们最为熟悉的应该就是 TCP/IP 协议。而 ZooKeeper 则是在 TCP/IP 协议的基础上实现了自己特有的通信协议格式。在 ZooKeeper 中一次客户端的请求协议由请求头、请求体组成。而在一次服务端的响应协议中由响应头和响应体组成。</p>
|
||||
|
||||
<h3>ZooKeeper 协议的底层实现</h3>
|
||||
|
||||
<p>我们大概了解了 ZooKeeper 中的网络通信协议的结构后。接下来我们看一下在 ZooKeeper 中的内部对于网络通信协议的底层是怎么样实现的。</p>
|
||||
|
||||
<h4>请求协议</h4>
|
||||
|
||||
<p>请求协议就是客户端向服务端发送的协议。比如我们经常用到的会话创建、数据节点查询等操作。都是客户端通过网络向 ZooKeeper 服务端发送请求协议完成的。</p>
|
||||
|
||||
<h4>客户端请求头底层解析</h4>
|
||||
|
||||
<p>首先,我们先看一下请求头的内部的实现原理。在 ZooKeeper 中请求头是通过 RequestHeader 类实现的。首先 RequestHeader 类实现了 Record 接口,用于之后在网络传输中进行序列化操作。</p>
|
||||
|
||||
<p>我们可以看到 RequestHeader 类中只有两个属性字段分别是 xid 和 type。这两个字段在我们第一节课 ZooKeeper 的数据模型中介绍过,分别代表客户端序号用于记录客户端请求的发起顺序以及请求操作的类型。</p>
|
||||
|
||||
<pre><code class="language-java">class RequestHeader implements Record{
|
||||
|
||||
|
||||
|
||||
private int xid;
|
||||
|
||||
|
||||
|
||||
private int type;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4>客户端请求体底层解析</h4>
|
||||
|
||||
<p>我们接下来再看一下客户端请求协议的请求体,协议的请求体包括了协议处理逻辑的全部内容,一次会话请求的所有操作内容都涵盖在请求体中。在 ZooKeeper 的内部实现中,根据不同的请求操作类型,会采用不同的结构封装请求体。接下来我们就以最常用的创建一次会话和数据节点的查询和更新这三种操作来介绍,深入底层看看 ZooKeeper 在内部是如何实现的。</p>
|
||||
|
||||
<p><strong>会话创建</strong></p>
|
||||
|
||||
<p>前面的课程我们已经介绍了 ZooKeeper 中的会话创建以及会话管理等相关知识。通过之前的学习我们知道了在 ZooKeeper 客户端发起会话时,会向服务端发送一个会话创建请求,该请求的作用就是通知 ZooKeeper 服务端需要处理一个来自客户端的访问链接。</p>
|
||||
|
||||
<p>而服务端处理会话创建请求时所需要的所有信息都包括在请求体内。在 ZooKeeper 中该请求体是通过 ConnectRequest 类实现的,其内部一共包括了五种属性字段。分别是 protocolVersion 表示该请求协议的版本信息、lastZxidSeen 最后一次接收到的服务器的 zxid 序号、timeOut 会话的超时时间、会话标识符 sessionId 以及会话的密码 password。有了这些信息 ZooKeeper 服务端在接收一个请求时,就可以根据请求体的信息进行相关的操作了。</p>
|
||||
|
||||
<pre><code class="language-java">ConnectRequest implements Record {
|
||||
|
||||
|
||||
|
||||
private int protocolVersion;
|
||||
|
||||
|
||||
|
||||
private long lastZxidSeen;
|
||||
|
||||
|
||||
|
||||
private int timeOut;
|
||||
|
||||
|
||||
|
||||
private long sessionId;
|
||||
|
||||
|
||||
|
||||
private byte[] passwd;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p><strong>节点查询</strong></p>
|
||||
|
||||
<p>在我们通过客户端 API 查询 ZooKeeper 服务器上的数据节点时,客户端会向服务端发送 GetDataRequest 会话请求。与上面介绍的会话请求不同。ZooKeeper 在处理获取数据节点会话请求时,选择了另一种结构作为该协议的请求体。而具体的实现类则是 GetDataRequest 。在 GetDataRequest 类中首先实现了 Record 接口用于序列化操作。其具有两个属性分别是字符类型 path 表示要请求的数据节点路径以及布尔类型 watch 表示该节点是否注册了 Watch 监控。</p>
|
||||
|
||||
<p>节点路径如下:</p>
|
||||
|
||||
<pre><code class="language-java">public class GetDataRequest implements Record {
|
||||
|
||||
|
||||
|
||||
private String path;
|
||||
|
||||
|
||||
|
||||
private boolean watch;
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p><strong>节点更新</strong></p>
|
||||
|
||||
<p>最后,我们来看一下最后一种会话操作类型即节点的更新操作,同样的在客户端向服务端发送一个数据节点更新操作时,其在网络上实际发送的是更新操作的请求协议。而在 ZooKeeper 中对于协议内部的请求体,ZooKeeper 通过 SetDataRequest 类进行了封装。在 SetDataRequest 内部也包含了三种属性,分别是 path 表示节点的路径、data 表示节点数据信息以及 version 表示节点期望的版本号用于锁的验证。</p>
|
||||
|
||||
<pre><code class="language-java">public class SetDataRequest implements Record {
|
||||
|
||||
|
||||
|
||||
private String path;
|
||||
|
||||
|
||||
|
||||
private byte[] data;
|
||||
|
||||
|
||||
|
||||
private int version;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>到目前为止我们就对 ZooKeeper 客户端在一次网络会话请求中所发送的请求协议的内部结构和底层实现都做了介绍,然而这些都是客户端向服务器端的请求协议,接下来我们就继续分析 ZooKeeper 服务端向客户端发送的响应协议是如何实现的。</p>
|
||||
|
||||
<h4>响应协议</h4>
|
||||
|
||||
<p>响应协议可以理解为服务端在处理客户端的请求后,返回相关信息给客户端。而服务端所采用的响应协议类型则要根据客户端的请求协议类型来选择。</p>
|
||||
|
||||
<h4>服务端请求头解析</h4>
|
||||
|
||||
<p>在服务端接收到客户端的请求后,执行相关操作将结果通知给客户端。而在 ZooKeeper 服务端向客户单发送的响应协议中,也是包括了请求头和请求体。而与客户端的请求头不同的是在 ZooKeeper 服务端的请求头多了一个错误状态字段。具体的实现类是 ReplyHeader。</p>
|
||||
|
||||
<pre><code class="language-java">public class ReplyHeader implements Record {
|
||||
|
||||
|
||||
|
||||
private int xid;
|
||||
|
||||
|
||||
|
||||
private long zxid;
|
||||
|
||||
|
||||
|
||||
private int err;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4>服务端请求体解析</h4>
|
||||
|
||||
<p>下面我们再看一下响应协议的请求体部分,服务端的请求体可以理解为对客户端所请求内容的封装,一个服务端的请求体包含了客户端所要查询的数据而对于不同的请求类型,在 ZooKeeper 的服务端也是采用了不同的结构进行处理的。与上面我们讲解客户端请求体的方法一样,我们还是通过会话的创建、数据节点的查询和修改这三种请求操作来介绍,看看 ZooKeeper 服务端是如何响应客户端请求的。</p>
|
||||
|
||||
<p><strong>响应会话创建</strong></p>
|
||||
|
||||
<p>对于客户端发起的一次会话连接操作,ZooKeeper 服务端在处理后,会返回给客户端一个 Response 响应。而在底层代码中 ZooKeeper 是通过 ConnectRespose 类来实现的。在该类中有四个属性,分别是 protocolVersion 请求协议的版本信息、timeOut 会话超时时间、sessionId 会话标识符以及 passwd 会话密码。</p>
|
||||
|
||||
<pre><code class="language-java">public class ConnectResponse implements Record {
|
||||
|
||||
|
||||
|
||||
private int protocolVersion;
|
||||
|
||||
|
||||
|
||||
private int timeOut;
|
||||
|
||||
|
||||
|
||||
private long sessionId;
|
||||
|
||||
|
||||
|
||||
private byte[] passwd;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p><strong>响应节点查询</strong></p>
|
||||
|
||||
<p>在客户端发起查询节点数据的请求时,服务端根据客户端发送的节点路径,并验证客户端具有相应的权限后,会将节点数据返回给客户端。而 ZooKeeper 服务端通过 GetDataResponse 类来封装查询到的节点相关信息到响应协议的请求体中。在 GetDataResponse 内部有两种属性字段分别是 data 属性表示节点数据的内容和 stat 属性表示节点的状态信息。</p>
|
||||
|
||||
<pre><code class="language-java">public class GetDataResponse implements Record {
|
||||
|
||||
|
||||
|
||||
private byte[] data;
|
||||
|
||||
|
||||
|
||||
private org.apache.zookeeper.data.Stat stat;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p><strong>响应节点更新</strong></p>
|
||||
|
||||
<p>在客户端发送一个节点变更操作后, ZooKeeper 服务端在处理完相关逻辑后,会发送一个响应给客户端。而在 ZooKeeper 中更新完节点后会将操作结果返回给客户端,节点更新操作的响应协议请求体通过 SetDataResponse 类来实现。而在该类的内部只有一个属性就是 stat 字段,表示该节点数据更新后的最新状态信息。</p>
|
||||
|
||||
<pre><code class="language-java">public class SetDataResponse implements Record {
|
||||
|
||||
|
||||
|
||||
private org.apache.zookeeper.data.Stat stat;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h3>结束</h3>
|
||||
|
||||
<p>会话相关的知识点在之前的课程中我们已经讲到过了,从客户端到服务端的处理过程我们都已经很熟悉了。本节课我们从网络传输的角度来分析 ZooKeeper 底层的实现,通过本节课的学习后,我们就将 ZooKeeper 会话请求的全部过程都介绍完了。</p>
|
||||
|
||||
<p>通过本节课的学习,我们知道了 ZooKeeper 中的网络通信协议是通过不同的类具体实现的。那么我们思考这样一个问题。如果一个会话的超时时间是在服务器端实现的。会话超时服务器端会主动关闭会话。所以在会话请求的请求体中包括了设置会话超时的属性字段,而我们经常会遇到一个问题就是明明通过客户端将超时时间设置了一个值,而在实际执行的时候会话超时的时间可能远远小于我们设置的时间。这是因为在 ZooKeeper 处理会话超时时间的时候不只是简单地使用客户端传来的超时时间还会根据 minSessionTimeout 和 maxSessionTimeout 这两个参数来调整时间的设置。这里请大家在日常设置中多注意。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">下一页</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":"709975cf78ab3cfa","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>
|
||||
|
||||
990
专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md.html
Normal file
@@ -0,0 +1,990 @@
|
||||
<!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>07 单机模式:服务器如何从初始化到对外提供服务?.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>07 单机模式:服务器如何从初始化到对外提供服务?</h1>
|
||||
|
||||
<p>本课时我们开始学习 ZooKeeper 服务器的启动管理与初始化相关的内容。</p>
|
||||
|
||||
<p>通过基础篇的学习我们已经掌握了 ZooKeeper 相关的基础知识,今天我们就开始进阶篇中的第一节课,本节课主要通过对单机版的 ZooKeeper 中的启动与服务的初始化过程进行分析,来学习 ZooKeeper 服务端相关的处理知识。现在我们就开始深入到服务器端看一看 ZooKeeper 是如何从初始化到对外提供服务的。</p>
|
||||
|
||||
<h3>启动准备实现</h3>
|
||||
|
||||
<p>在 ZooKeeper 服务的初始化之前,首先要对配置文件等信息进行解析和载入。也就是在真正开始服务的初始化之前需要对服务的相关参数进行准备,而 ZooKeeper 服务的准备阶段大体上可分为启动程序入口、zoo.cfg 配置文件解析、创建历史文件清理器等,如下图所示:</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F7OQ_uAU5yzAABbrnOyALM516.png" alt="image" /></p>
|
||||
|
||||
<p>QuorumPeerMain 类是 ZooKeeper 服务的启动接口,可以理解为 Java 中的 main 函数。 通常我们在控制台启动 ZooKeeper 服务的时候,输入 zkServer.cm 或 zkServer.sh 命令就是用来启动这个 Java 类的。如下代码所示,QuorumPeerMain 类函数只有一个 initializeAndRun 方法,是作用为所有 ZooKeeper 服务启动逻辑的入口。</p>
|
||||
|
||||
<pre><code class="language-java">package org.apache.zookeeper.server.quorum
|
||||
|
||||
|
||||
|
||||
public class QuorumPeerMain {
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
main.initializeAndRun(args);
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4>解析配置文件</h4>
|
||||
|
||||
<p>知道了 ZooKeeper 服务的程序启动入口,那么我们现在就分析 ZooKeeper 的启动过程。在 ZooKeeper 启动过程中,首先要做的事情就是解析配置文件 zoo.cfg。在之前的课程中我们提到过,zoo.cfg 是服务端的配置文件,在这个文件中我们可以配置数据目录、端口号等信息。所以解析 zoo.cfg 配置文件是 ZooKeeper 服务启动的关键步骤。zoo.cfg 文件的具体解析过程会在后边的课程中专门展开讲解,这里我们只需要知道在服务启动的过程中会进行配置文件的解析。</p>
|
||||
|
||||
<h4>创建文件清理器</h4>
|
||||
|
||||
<p>文件清理器在我们日常的使用中非常重要,我们都知道面对大流量的网络访问,ZooKeeper 会因此产生海量的数据,如果磁盘数据过多或者磁盘空间不足,则会导致 ZooKeeper 服务器不能正常运行,进而影响整个分布式系统。所以面对这种问题,ZooKeeper 采用了 DatadirCleanupManager 类作为历史文件的清理工具类。在 3.4.0 版本后的 ZooKeeper 中更是增加了自动清理历史数据的功能以尽量避免磁盘空间的浪费。如下代码所示,DatadirCleanupManager 类有 5 个属性,其中 snapDir 和 dataLogDir 分别表示数据快照地址以及日志数据的存放地址。而我们在日常工作中可以通过在 zoo.cfg 文件中配置 autopurge.snapRetainCount 和 autopurge.purgeInterval 这两个参数实现数据文件的定时清理功能,autopurge.purgeInterval 这个参数指定了清理频率,以小时为单位,需要填写一个 1 或更大的整数,默认是 0,表示不开启自己清理功能。autopurge.snapRetainCount 这个参数和上面的参数搭配使用,这个参数指定了需要保留的文件数目,默认是保留 3 个。</p>
|
||||
|
||||
<pre><code class="language-java">public class DatadirCleanupManager {
|
||||
|
||||
|
||||
|
||||
private final File snapDir;
|
||||
|
||||
|
||||
|
||||
private final File dataLogDir;
|
||||
|
||||
|
||||
|
||||
private final int snapRetainCount;
|
||||
|
||||
|
||||
|
||||
private final int purgeInterval;
|
||||
|
||||
|
||||
|
||||
private Timer timer;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h3>服务初始化</h3>
|
||||
|
||||
<p>经过了上面的配置文件解析等准备阶段后, ZooKeeper 开始服务的初始化阶段。初始化阶段可以理解为根据解析准备阶段的配置信息,实例化服务对象。服务初始化阶段的主要工作是创建用于服务统计的工具类,如下图所示主要有以下几种:</p>
|
||||
|
||||
<ol>
|
||||
|
||||
<li>ServerStats 类,它可以用于服务运行信息统计;</li>
|
||||
|
||||
<li>FileTxnSnapLog 类,可以用于数据管理。</li>
|
||||
|
||||
<li>会话管理类,设置服务器 TickTime 和会话超时时间、创建启动会话管理器等操作。</li>
|
||||
|
||||
</ol>
|
||||
|
||||
<p><img src="assets/CgqCHl7ORB-AdNM1AABRbvNSlEE886.png" alt="image" /></p>
|
||||
|
||||
<p>下面我们就分别分析一下这几个关键步骤在 ZooKeeper 中的底层实现过程。</p>
|
||||
|
||||
<h4>ServerStats创建</h4>
|
||||
|
||||
<p>首先,我们来看一下统计工具类 ServerStats。ServerStats 类用于统计 ZooKeeper 服务运行时的状态信息统计。主要统计的数据有服务端向客户端发送的响应包次数、接收到的客户端发送的请求包次数、服务端处理请求的延迟情况以及处理客户端的请求次数。在日常运维工作中,监控服务器的性能以及运行状态等参数很多都是这个类负责收集的。</p>
|
||||
|
||||
<pre><code class="language-java">public class ServerStats {
|
||||
|
||||
|
||||
|
||||
private long packetsSent;
|
||||
|
||||
|
||||
|
||||
private long packetsReceived;
|
||||
|
||||
|
||||
|
||||
private long maxLatency;
|
||||
|
||||
|
||||
|
||||
private long minLatency = Long.MAX_VALUE;
|
||||
|
||||
|
||||
|
||||
private long totalLatency = 0;
|
||||
|
||||
|
||||
|
||||
private long count = 0;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4>FileTxnSnapLog 类</h4>
|
||||
|
||||
<p>现在,我们再看一下另一个工具类 FileTxnSnapLog 类,该类的作用是用来管理 ZooKeeper 的数据存储等相关操作,可以看作为 ZooKeeper 服务层提供底层持久化的接口。在 ZooKeeper 服务启动过程中,它会根据 zoo.cfg 配置文件中的 dataDir 数据快照目录和 dataLogDir 事物日志目录来创建 FileTxnSnapLog 类。</p>
|
||||
|
||||
<pre><code class="language-java">package org.apache.zookeeper.server.persistence;
|
||||
|
||||
|
||||
|
||||
public class FileTxnSnapLog {
|
||||
|
||||
|
||||
|
||||
private final File dataDir;
|
||||
|
||||
|
||||
|
||||
private final File snapDir;
|
||||
|
||||
|
||||
|
||||
private TxnLog txnLog;
|
||||
|
||||
|
||||
|
||||
private SnapShot snapLog;
|
||||
|
||||
|
||||
|
||||
private final boolean autoCreateDB
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4>ServerCnxnFactory 类创建</h4>
|
||||
|
||||
<p>ZooKeeper 中客户端和服务端通过网络通信,其本质是通过 Java 的 IO 数据流的方式进行通信,但是传统的 IO 方式具有阻塞等待的问题,而 NIO 框架作为传统的 Java IO 框架的替代方案,在性能上大大优于前者。也正因如此,NIO 框架也被广泛应用于网络传输的解决方案中。而 ZooKeeper 最早也是使用自己实现的 NIO 框架,但是从 3.4.0 版本后,引入了第三方 Netty 等框架来满足不同使用情况的需求,而我们可以通过 ServerCnxnFactory 类来设置 ZooKeeper 服务器,从而在运行的时候使用我们指定的 NIO 框架。如代码中 ServerCnxnFactory 类通过</p>
|
||||
|
||||
<p>setServerCnxnFactory 函数来创建对应的工厂类。</p>
|
||||
|
||||
<pre><code class="language-java">package org.apache.zookeeper.server;
|
||||
|
||||
|
||||
|
||||
public abstract class ServerCnxnFactory {
|
||||
|
||||
|
||||
|
||||
final public void setZooKeeperServer(ZooKeeperServer zks) {
|
||||
|
||||
|
||||
|
||||
this.zkServer = zks;
|
||||
|
||||
|
||||
|
||||
if (zks != null) {
|
||||
|
||||
|
||||
|
||||
if (secure) {
|
||||
|
||||
|
||||
|
||||
zks.setSecureServerCnxnFactory(this);
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
|
||||
zks.setServerCnxnFactory(this);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>在通过 ServerCnxnFactory 类制定了具体的 NIO 框架类后。ZooKeeper 首先会创建一个线程
|
||||
|
||||
Thread 类作为 ServerCnxnFactory 类的启动主线程。之后 ZooKeeper 服务再初始化具体的 NIO 类。这里请你注意的是,虽然初始化完相关的 NIO 类 ,比如已经设置好了服务端的对外端口,客户端也能通过诸如 2181 端口等访问到服务端,但是此时 ZooKeeper 服务器还是无法处理客户端的请求操作。<strong>这是因为 ZooKeeper 启动后,还需要从本地的快照数据文件和事务日志文件中恢复数据</strong>。这之后才真正完成了 ZooKeeper 服务的启动。</p>
|
||||
|
||||
<h4>初始化请求处理链</h4>
|
||||
|
||||
<p>在完成了 ZooKeeper 服务的启动后,ZooKeeper 会初始化一个请求处理逻辑上的相关类。这个操作就是初始化请求处理链。所谓的请求处理链是一种责任链模式的实现方式,根据不同的客户端请求,在 ZooKeeper 服务器上会采用不同的处理逻辑。而为了更好地实现这种业务场景,ZooKeeper 中采用多个请求处理器类一次处理客户端请求中的不同逻辑部分。这种处理请求的逻辑方式就是责任链模式。而本课时主要说的是单机版服务器的处理逻辑,主要分为PrepRequestProcessor、SyncRequestProcessor、FinalRequestProcessor 3 个请求处理器,而在一个请求到达 ZooKeeper 服务端进行处理的过程,则是严格按照这个顺序分别调用这 3 个类处理请求中的对应逻辑,如下图所示。具体的内容,我们会在后面的课程中详细讲解。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl7ORDiAIMqzAABBGUvvhFU739.png" alt="image" /></p>
|
||||
|
||||
<h3>总结</h3>
|
||||
|
||||
<p>本课时是我们进阶篇阶段的第一课,在整个进阶篇中,我们主要从 ZooKeeper 服务内部的实现逻辑来学习 ZooKeeper 中的相关知识,而本课时从单机版服务器的启动,到对外提供服务的整个过程,逐步分析 ZooKeeper 实现的每个步骤,理解 ZooKeeper 服务器的初始化、配置解析、服务实例化等过程对我们日后在工作中分析排查 ZooKeeper 产生的相关问题以及提高 ZooKeeper 服务器的稳定性或性能都有很大的帮助。</p>
|
||||
|
||||
<p>通过本课时的学习我们知道了 ZooKeeper 服务单机版启动的关键步骤,下面我们来思考这个问题:在我们启动单机版服务器的时候,如果 ZooKeeper 服务通过 zoo.cfg 配置文件的相关参数,利用 FileTxnSnapLog 类来实现相关数据的本地化存储。那么我们在日常的开发维护中,如何才能知道当前存储 ZooKeeper 相关数据的磁盘容量应该设置多大的空间才能满足当前业务的发展?如何才能尽量减少磁盘空间的浪费?</p>
|
||||
|
||||
<p>我们可以通过 DatadirCleanupManager 类来对历史文件进行清理以避免太多的历史数据占据磁盘空间造成不必要的浪费。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">下一页</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":"709975d1cdc73cfa","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>
|
||||
|
||||
982
专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md.html
Normal file
@@ -0,0 +1,982 @@
|
||||
<!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>08 集群模式:服务器如何从初始化到对外提供服务?.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>08 集群模式:服务器如何从初始化到对外提供服务?</h1>
|
||||
|
||||
<p>通过上个课时的学习,我们知道了 ZooKeeper 在单机模式下从启动运行到对外提供服务的整个过程。而在日常工作中,无论是出于性能上的优势还是可靠性的考虑,单机模式都无法满足要求。因此,ZooKeeper 也采用集群的方式运行。本课时我们就来学习一下 ZooKeeper 集群模式下,启动过程的底层实现。</p>
|
||||
|
||||
<h3>什么是集群模式?</h3>
|
||||
|
||||
<p>为了解决单机模式下性能的瓶颈问题,以及出于对系统可靠性的高要求,集群模式的系统架构方式被业界普遍采用。那什么是集群模式呢?集群模式可以简单理解为将单机系统复制成几份,部署在不同主机或网络节点上,最终构成了一个由多台计算机组成的系统“集群”。而组成集群中的每个服务器叫作集群中的网络节点。</p>
|
||||
|
||||
<p>到现在我们对集群的组织架构形式有了大概的了解,那么你可能会产生一个问题:我们应该如何使用集群?当客户端发送一个请求到集群服务器的时候,究竟是哪个机器为我们提供服务呢?为了解决这个问题,我们先介绍一个概念名词“调度者”。调度者的工作职责就是在集群收到客户端请求后,根据当前集群中机器的使用情况,决定将此次客户端请求交给哪一台服务器或网络节点进行处理,例如我们都很熟悉的负载均衡服务器就是一种调度者的实现方式。</p>
|
||||
|
||||
<h3>ZooKeeper 集群模式的特点</h3>
|
||||
|
||||
<p>通过上面的介绍,我们知道了集群是由网络中不同机器组成的一个系统,集群工作是通过集群中调度者服务器来协同工作的。那么现在我们来看一下 ZooKeeper 中的集群模式,在 ZooKeeper 集群模式中,相比上面说到的集群架构方式,在 ZooKeeper 集群中将服务器分成 Leader 、Follow 、Observer 三种角色服务器,在集群运行期间这三种服务器所负责的工作各不相同:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>Leader 角色服务器负责管理集群中其他的服务器,是集群中工作的分配和调度者。</li>
|
||||
|
||||
<li>Follow 服务器的主要工作是选举出 Leader 服务器,在发生 Leader 服务器选举的时候,系统会从 Follow 服务器之间根据多数投票原则,选举出一个 Follow 服务器作为新的 Leader 服务器。</li>
|
||||
|
||||
<li>Observer 服务器则主要负责处理来自客户端的获取数据等请求,并不参与 Leader 服务器的选举操作,也不会作为候选者被选举为 Leader 服务器。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>接下来我们看看在 ZooKeeper 中集群是如何架构和实现的。</p>
|
||||
|
||||
<h3>底层实现原理</h3>
|
||||
|
||||
<p>到目前为止我们对 ZooKeeper 中集群相关的知识有了大体的了解,接下来我们就深入到 ZooKeeper 的底层,看看在服务端,集群模式是如何启动到对外提供服务的。</p>
|
||||
|
||||
<p>在上一课时中,我们已经对 ZooKeeper 单机版服务的启动过程做了详细的介绍。而集群中的启动过程和单机版的启动过程有很多地方是一样的。所以本次我们只对 ZooKeeper 集群中的特有实现方式做重点介绍。</p>
|
||||
|
||||
<h4>程序启动</h4>
|
||||
|
||||
<p>首先,在 ZooKeeper 服务启动后,系统会调用入口 QuorumPeerMain 类中的 main 函数。在 main 函数中的 initializeAndRun 方法中根据 zoo.cfg 配置文件,判断服务启动方式是集群模式还是单机模式。在函数中首先根据 arg 参数和 config.isDistributed() 来判断,如果配置参数中配置了相关的配置项,并且已经指定了集群模式运行,那么在服务启动的时候就会跳转到 runFromConfig 函数完成之后的集群模式的初始化工作。</p>
|
||||
|
||||
<pre><code class="language-java">protected void initializeAndRun(String[] args){
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
if (args.length == 1 && config.isDistributed()) {
|
||||
|
||||
|
||||
|
||||
runFromConfig(config);
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ZooKeeperServerMain.main(args);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4>QuorumPeer 类</h4>
|
||||
|
||||
<p>在 ZooKeeper 服务的集群模式启动过程中,一个最主要的核心类是 QuorumPeer 类。我们可以将每个 QuorumPeer 类的实例看作集群中的一台服务器。在 ZooKeeper 集群模式的运行中,一个 QuorumPeer 类的实例通常具有 3 种状态,分别是参与 Leader 节点的选举、作为 Follow 节点同步 Leader 节点的数据,以及作为 Leader 节点管理集群中的 Follow 节点。</p>
|
||||
|
||||
<p>介绍完 QuorumPeer 类后,下面我们看一下在 ZooKeeper 服务的启动过程中,针对 QuorumPeer 类都做了哪些工作。如下面的代码所示,在一个 ZooKeeper 服务的启动过程中,首先调用 runFromConfig 函数将服务运行过程中需要的核心工具类注册到 QuorumPeer 实例中去。</p>
|
||||
|
||||
<p>这些核心工具就是我们在上一节课单机版服务的启动中介绍的诸如 FileTxnSnapLog 数据持久化类、ServerCnxnFactory 类 NIO 工厂方法等。这之后还需要配置服务器地址列表、Leader 选举算法、会话超时时间等参数到 QuorumPeer 实例中。</p>
|
||||
|
||||
<pre><code class="language-java">public void runFromConfig(QuorumPeerConfig config){
|
||||
|
||||
|
||||
|
||||
ServerCnxnFactory cnxnFactory = null;
|
||||
|
||||
|
||||
|
||||
ServerCnxnFactory secureCnxnFactory = null;
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
quorumPeer = getQuorumPeer()
|
||||
|
||||
|
||||
|
||||
quorumPeer.setElectionType(config.getElectionAlg());
|
||||
|
||||
|
||||
|
||||
quorumPeer.setCnxnFactory(cnxnFactory);
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>与开篇中提到的一般构建集群的方式不同,ZooKeeper 将集群中的机器分为 Leader 、 Follow 、Obervser 三种角色,每种角色服务器在集群中起到的作用都各不相同。比如 Leader 角色服务器主要负责处理客户端发送的数据变更等事务性请求操作,并管理协调集群中的 Follow 角色服务器。而 Follow 服务器则主要处理客户端的获取数据等非事务性请求操作。Observer 角色服务器的功能和 Follow 服务器相似,唯一的不同就是不参与 Leader 头节点服务器的选举工作。</p>
|
||||
|
||||
<p>在 ZooKeeper 中的这三种角色服务器,在服务启动过程中也有各自的不同,下面我们就以 Leader 角色服务器的启动和 Follow 服务器服务的启动过程来看一下各自的底层实现原理。</p>
|
||||
|
||||
<h4>Leader 服务器启动过程</h4>
|
||||
|
||||
<p>在 ZooKeeper 集群中,Leader 服务器负责管理集群中其他角色服务器,以及处理客户端的数据变更请求。因此,在整个 ZooKeeper 服务器中,Leader 服务器非常重要。所以在整个 ZooKeeper 集群启动过程中,首先要先选举出集群中的 Leader 服务器。</p>
|
||||
|
||||
<p>在 ZooKeeper 集群选举 Leader 节点的过程中,首先会根据服务器自身的服务器 ID(SID)、最新的 ZXID、和当前的服务器 epoch (currentEpoch)这三个参数来生成一个选举标准。之后,ZooKeeper 服务会根据 zoo.cfg 配置文件中的参数,选择参数文件中规定的 Leader 选举算法,进行 Leader 头节点的选举操作。而在 ZooKeeper 中提供了三种 Leader 选举算法,分别是 LeaderElection 、AuthFastLeaderElection、FastLeaderElection。在我们日常开发过程中,可以通过在 zoo.cfg 配置文件中使用 electionAlg 参数属性来制定具体要使用的算法类型。具体的 Leader 选举算法我们会在之后的章节中展开讲解。</p>
|
||||
|
||||
<p>这里我们只需要知道,在 ZooKeeper 集群模式下服务启动后。首先会创建用来选举 Leader 节点的工具类 QuorumCnxManager 。下面这段代码给出了 QuorumCnxManager 在创建实例的时候首先要实例化 Listener 对象用于监听 Leader 选举端口。</p>
|
||||
|
||||
<pre><code class="language-java">package org.apache.zookeeper.server.quorum;
|
||||
|
||||
|
||||
|
||||
public class QuorumCnxManager {
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
public QuorumCnxManager(QuorumPeer self) {
|
||||
|
||||
|
||||
|
||||
String cnxToValue = System.getProperty("zookeeper.cnxTimeout")
|
||||
|
||||
|
||||
|
||||
listener = new Listener();
|
||||
|
||||
|
||||
|
||||
listener.setName("QuorumPeerListener");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>而在 ZooKeeper 中,Leader 选举的大概过程,总体说来就是在集群中的所有机器中直接进行一次选举投票,选举出一个最适合的机器作为 Leader 节点。而具体的评价标准就是我们上面提到的三种选举算法。而从 3.4.0 版本开始,ZooKeeper 只支持 FastLeaderElection 这一种选举算法。同时没有被选举为 Leader 节点的机器则作为 Follow 或 Observer 节点机器存在。</p>
|
||||
|
||||
<h4>Follow 服务器启动过程</h4>
|
||||
|
||||
<p>现在,我们已经选举出 ZooKeeper 集群模式下的 Leader 节点机器了。我们再看一下 Follow 节点机器在 ZooKeeper 集群模式下服务器的启动过程。</p>
|
||||
|
||||
<p>在服务器的启动过程中,Follow 机器的主要工作就是和 Leader 节点进行数据同步和交互。当 Leader 机器启动成功后,Follow 节点的机器会收到来自 Leader 节点的启动通知。而该通知则是通过 LearnerCnxAcceptor 类来实现的。该类就相当于一个接收器。专门用来接收来自集群中 Leader 节点的通知信息。下面这段代码中 LearnerCnxAcceptor 类首先初始化要监听的 Leader 服务器地址和设置收到监听的处理执行方法等操作 。</p>
|
||||
|
||||
<pre><code class="language-java">class LearnerCnxAcceptor extends ZooKeeperCriticalThread {
|
||||
|
||||
|
||||
|
||||
private volatile boolean stop = false;
|
||||
|
||||
|
||||
|
||||
public LearnerCnxAcceptor() {
|
||||
|
||||
|
||||
|
||||
super("LearnerCnxAcceptor-" + ss.getLocalSocketAddress(), zk
|
||||
|
||||
|
||||
|
||||
.getZooKeeperServerListener());
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>在接收到来自 Leader 服务器的通知后,Follow 服务器会创建一个 LearnerHandler 类的实例,用来处理与 Leader 服务器的数据同步等操作。</p>
|
||||
|
||||
<pre><code class="language-java">package org.apache.zookeeper.server.quorum;
|
||||
|
||||
|
||||
|
||||
public class LearnerHandler extends ZooKeeperThread {
|
||||
|
||||
|
||||
|
||||
protected final Socket sock;
|
||||
|
||||
|
||||
|
||||
final Leader leader;
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>在完成数据同步后,一个 ZooKeeper 服务的集群模式下启动的关键步骤就完成了,整个服务就处于运行状态,可以对外提供服务了。</p>
|
||||
|
||||
<h3>小结</h3>
|
||||
|
||||
<p>本课时我们主要学习了 ZooKeeper 集群模式的启动过程和底层实现。与一般的集群架构不同,ZooKeeper 集群模式把其中的机器分成 Leader 、Follow 、Obsever 三种角色。当作为 Leader 节点的机器失效时,系统会根据配置文件中的选举算法产生新的节点。这种方式避免了一般集群模式中产生的单点失效等问题。</p>
|
||||
|
||||
<p>现在,我们思考这样一个问题。在我们日常使用 ZooKeeper 集群服务器的时候,集群中的机器个数应该如何选择?</p>
|
||||
|
||||
<p>答案是最好使用奇数原则,最小的集群配置应该是三个服务器或者节点。而如果采用偶数,在 Leader 节点选举投票的过程中就不满足大多数原则,这时就产生“脑裂”这个问题。请你多注意。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">下一页</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":"709975d40a6f3cfa","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>
|
||||
|
||||
858
专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md.html
Normal file
@@ -0,0 +1,858 @@
|
||||
<!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>09 创建会话:避开日常开发的那些“坑”.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>09 创建会话:避开日常开发的那些“坑”</h1>
|
||||
|
||||
<p>会话是 ZooKeeper 中最核心的概念之一。客户端与服务端的交互操作中都离不开会话的相关的操作。在前几节课中我们学到的临时节点、Watch 通知机制等都和客户端会话有密不可分的关系。比如一次会话关闭时,服务端会自动删除该会话所创建的临时节点,或者当客户端会话退出时,通过 Watch 监控机制就可以向订阅了该事件的客户端发送响应的通知。接下来我们就从会话的应用层使用,到 ZooKeeper 底层的实现原理,一步步学习会话的相关知识。</p>
|
||||
|
||||
<h3>会话的创建</h3>
|
||||
|
||||
<p>ZooKeeper 的工作方式一般是通过客户端向服务端发送请求而实现的。而在一个请求的发送过程中,首先,客户端要与服务端进行连接,而一个连接就是一个会话。在 ZooKeeper 中,一个会话可以看作是一个用于表示客户端与服务器端连接的数据结构 Session。而这个数据结构由三个部分组成:分别是会话 ID(sessionID)、会话超时时间(TimeOut)、会话关闭状态(isClosing),如下图所示。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl7XUbeAH4RvAABLmAD-zt0526.png" alt="image" /></p>
|
||||
|
||||
<p>下面我们来分别介绍一下这三个部分:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>会话 ID:会话 ID 作为一个会话的标识符,当我们创建一次会话的时候,ZooKeeper 会自动为其分配一个唯一的 ID 编码。</li>
|
||||
|
||||
<li>会话超时时间:会话超时时间在我们之前的课程中也有涉及,一般来说,一个会话的超时时间就是指一次会话从发起后到被服务器关闭的时长。而设置会话超时时间后,服务器会参考设置的超时时间,最终计算一个服务端自己的超时时间。而这个超时时间则是最终真正用于 ZooKeeper 中服务端用户会话管理的超时时间。</li>
|
||||
|
||||
<li>会话关闭状态:会话关闭 isClosing 状态属性字段表示一个会话是否已经关闭。如果服务器检查到一个会话已经因为超时等原因失效时, ZooKeeper 会在该会话的 isClosing 属性值标记为关闭,再之后就不对该会话进行操作了。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h3>会话状态</h3>
|
||||
|
||||
<p>通过上面的学习,我们知道了 ZooKeeper 中一次会话的内部结构。下面我们就从系统运行的角度去分析,一次会话从创建到关闭的生命周期中都经历了哪些阶段。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl7XUcSAAoGmAAO6qmEJCC4477.png" alt="11.png" /></p>
|
||||
|
||||
<p>上面是来自 ZooKeeper 官网的一张图片。该图片详细完整地描述了一次会话的完整生命周期。而通过该图片我们可以知道,在 ZooKeeper 服务的运行过程中,会话会经历不同的状态变化。而这些状态包括:正在连接(CONNECTING)、已经连接(CONNECTIED)、正在重新连接(RECONNECTING)、已经重新连接(RECONNECTED)、会话关闭(CLOSE)等。</p>
|
||||
|
||||
<p>当客户端开始创建一个与服务端的会话操作时,它的会话状态就会变成 CONNECTING,之后客户端会根据服务器地址列表中的服务器 IP 地址分别尝试进行连接。如果遇到一个 IP 地址可以连接到服务器,那么客户端会话状态将变为 CONNECTIED。</p>
|
||||
|
||||
<p>而如果因为网络原因造成已经连接的客户端会话断开时,客户端会重新尝试连接服务端。而对应的客户端会话状态又变成 CONNECTING ,直到该会话连接到服务端最终又变成 CONNECTIED。</p>
|
||||
|
||||
<p>在 ZooKeeper 服务的整个运行过程中,会话状态经常会在 CONNECTING 与 CONNECTIED 之间进行切换。最后,当出现超时或者客户端主动退出程序等情况时,客户端会话状态则会变为 CLOSE 状态。</p>
|
||||
|
||||
<h3>会话底层实现</h3>
|
||||
|
||||
<p>一个会话可以看作是由四种不同的属性字段组成的一种数据结构。而在整个 ZooKeeper 服务的运行过程中,会话管理的本质就是围绕这个数据结构进行操作。</p>
|
||||
|
||||
<p>说到 ZooKeeper 中会话的底层实现,就不得不说 SessionTracker 类,该类可以说是 ZooKeeper 实现会话的核心类,用来实现会话管理和维护等相关操作。可以说,在 ZooKeeper 会话的整个生命周期中都离不开 SessionTracker 类的参与。</p>
|
||||
|
||||
<p>SessionTracker 是一个接口类型,其规定了 ZooKeeper 中会话管理的相关操作行为。而具体的实现方式则是通过 SessionTrackerImpl 类来完成的。</p>
|
||||
|
||||
<pre><code class="language-java">class SessionTrackerImpl implements SessionTracker{
|
||||
|
||||
|
||||
|
||||
ConcurrentHashMap<Long, SessionImpl> sessionsById;
|
||||
|
||||
|
||||
|
||||
ConcurrentMap<Long, Integer> sessionsWithTimeout;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>如上面的代码所示,SessionTrackerImpl 类实现了 SessionTracker 接口。在其中有两个属性字段,分别是:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>sessionsById,用于根据会话 ID 来管理具体的会话实体。</li>
|
||||
|
||||
<li>sessionsWithTimeout,根据不同的会话 ID 管理每个会话的超时时间。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>而在 SessionTrackerImpl 类初始化的时候,首先会调用 initializeNextSession 方法来生成一个会话 ID ,该会话 ID 会作为一个唯一的标识符,在 ZooKeeper 服务之后的运行中用来标记一个特定的会话。</p>
|
||||
|
||||
<pre><code class="language-java">public static long initializeNextSession(long id) {
|
||||
|
||||
|
||||
|
||||
long nextSid;
|
||||
|
||||
|
||||
|
||||
nextSid = (Time.currentElapsedTime() << 24) >>> 8;
|
||||
|
||||
|
||||
|
||||
nextSid = nextSid | (id <<56);
|
||||
|
||||
|
||||
|
||||
return nextSid;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>通过上面的代码,我们可以了解到生成会话 ID 的过程,首先以毫秒为单位获取系统的当前时间,之后将该值通过位运算方式向左移动 24 位,再向右移动 8 位。最后根据服务器的 SID 进行或运算,得到的最终结果就作为该会话的 ID 编码。</p>
|
||||
|
||||
<p>有了会话 ID 和超时时间等信息后,一个会话相关的所有数据就基本具备了,也就完成了一个会话的创建工作。接下来就是服务器接收到会话请求后的处理等操作了。</p>
|
||||
|
||||
<h3>会话异常</h3>
|
||||
|
||||
<p>在平时的开发工作中,我们最常遇到和处理的场景就是会话超时异常。在 ZooKeeper 中,会话的超时异常包括客户端 readtimeout 异常和服务器端 sessionTimeout 异常。在我们平时的开发中,要明确这两个异常的不同之处在于一个是发生在客户端,而另一个是发生在服务端。</p>
|
||||
|
||||
<p>而对于那些对 ZooKeeper 接触不深的开发人员来说,他们常常踩坑的地方在于,虽然设置了超时间,但是在实际服务运行的时候 ZooKeeper 并没有按照设置的超时时间来管理会话。</p>
|
||||
|
||||
<p><strong>这是因为 ZooKeeper 实际起作用的超时时间是通过客户端和服务端协商决定</strong>。 ZooKeeper 客户端在和服务端建立连接的时候,会提交一个客户端设置的会话超时时间,而该超时时间会和服务端设置的最大超时时间和最小超时时间进行比对,如果正好在其允许的范围内,则采用客户端的超时时间管理会话。如果大于或者小于服务端设置的超时时间,则采用服务端设置的值管理会话。</p>
|
||||
|
||||
<h3>结束</h3>
|
||||
|
||||
<p>本课时我们从服务的运行角度分析了一次客户端会话从创建到关闭的整个过程。在会话的整个生命周期中会经历正在连接、已经连接、关闭等不同状态的变化。通过本节课的学习,我们掌握了会话的底层实现原理,进而在日常工作中,面对会话的创建等问题更能得心应手,在出现问题的时候能够利用我们所学的知识很快的排查问题。</p>
|
||||
|
||||
<p>下面我们看一下这个问题:ZooKeeper 生成会话 ID 编码的算法看起来比较复杂,那么这个算法是否严谨呢?</p>
|
||||
|
||||
<p>该算法实现上看起比较复杂,但是其实并不完美。其最主要的问题在于其算法的基础是通过Java 语言获取系统的当前时间作为算法的基本参数。而在一些情况下,在位运算的过程中会产生负数等问题,不过这个问题在 3.4.6 版本后得到了解决,这里请你注意,在使用 ZooKeeper 的时候,如果是在维护旧有系统时要考虑这个问题。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">下一页</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":"709975d67fcb3cfa","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>
|
||||
|
||||
922
专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md.html
Normal file
@@ -0,0 +1,922 @@
|
||||
<!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>10 ClientCnxn:客户端核心工作类工作原理解析.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>10 ClientCnxn:客户端核心工作类工作原理解析</h1>
|
||||
|
||||
<p>今天我们开始学习客户端核心工作类的工作原理。</p>
|
||||
|
||||
<p>上个课时我们学习了会话的底层实现过程,我们知道会话是在 ZooKeeper 的客户端发起的,而在会话超异常等事件发生时,服务端也会通知给客户端。而我们之所以能够接收到服务端的通知,并向服务端发送请求等操作,是通过 ZooKeeper 客户端实现的。下面我们就深入学习一下客户端核心工作类的实现过程和底层原理。</p>
|
||||
|
||||
<h3>客户端核心类</h3>
|
||||
|
||||
<p>在 ZooKeeper 客户端的底层实现中,ClientCnxn 类是其核心类,所有的客户端操作都是围绕这个类进行的。ClientCnxn 类主要负责维护客户端与服务端的网络连接和信息交互。</p>
|
||||
|
||||
<p>在前面的课程中介绍过,向服务端发送创建数据节点或者添加 Watch 监控等操作时,都会先将请求信息封装成 Packet 对象。那么 Packet 是什么呢?其实** Packet 可以看作是一个 ZooKeeper 定义的,用来进行网络通信的数据结构**,其主要作用是封装了网络通信协议层的数据。而 Packet 内部的数据结构如下图所示:</p>
|
||||
|
||||
<p><img src="assets/CgqCHl7aDQyAEkoJAAB9K_a8-pA768.png" alt="image.png" /></p>
|
||||
|
||||
<p>在 Packet 类中具有一些请求协议的相关属性字段,这些请求字段中分别包括:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>请求头信息(RequestHeader)</li>
|
||||
|
||||
<li>响应头信息 (ReplyHeader)</li>
|
||||
|
||||
<li>请求信息体(Request)</li>
|
||||
|
||||
<li>响应信息体(Response)</li>
|
||||
|
||||
<li>节点路径(clientPath ServerPath)</li>
|
||||
|
||||
<li>Watch 监控信息等</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>而在 Packet 类中有一个 createBB 方法函数,该函数的作用主要是将 Packet 对象的数据进行序列化,以便之后用于网络传输。具体过程如下面这段代码所示:</p>
|
||||
|
||||
<pre><code class="language-java">public void createBB() {
|
||||
|
||||
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
|
||||
|
||||
BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos);
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
if (requestHeader != null) {
|
||||
|
||||
|
||||
|
||||
requestHeader.serialize(boa, "header");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (request instanceof ConnectRequest) {
|
||||
|
||||
|
||||
|
||||
request.serialize(boa, "connect");
|
||||
|
||||
|
||||
|
||||
// append "am-I-allowed-to-be-readonly" flag
|
||||
|
||||
|
||||
|
||||
boa.writeBool(readOnly, "readOnly");
|
||||
|
||||
|
||||
|
||||
} else if (request != null) {
|
||||
|
||||
|
||||
|
||||
request.serialize(boa, "request");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
baos.close();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>从代码中我们可以知道,在 createBB 进行序列化的时候,并不是将 Packet 类中的所有属性字段进行序列化。而是只对请求头信息(requestHeader)、请求体信息(request)、只读(readOnly)这三个属性字段进行序列化。而其余的属性字段则只是存储在客户端,用于之后的相关操作。</p>
|
||||
|
||||
<h3>请求队列</h3>
|
||||
|
||||
<p>在我们对请求信息进行封装和序列化后,ZooKeeper 不会立刻就将一个请求信息通过网络直接发送给服务端。而是通过将请求信息添加到队列中,之后通过 sendThread 线程类来处理相关的请求发送等操作。这种方式很像生产者和消费者模式,我们将请求信息准备好,并添加到队列中的操作相当于生成者,而 sendThread 线程从队列中取出要发送的请求信息,并发送给服务端相当于消费者操作。</p>
|
||||
|
||||
<p>而在 ZooKeeper 中,作为消费者的队列有两种,一种是客户端发送给服务端的发送队列 outgoingQueue 以及服务端响应客户端操作的响应队列 pendingQueue。如下面这段代码:</p>
|
||||
|
||||
<pre><code class="language-java"> List<Packet> pendingQueue;//响应队列
|
||||
|
||||
|
||||
|
||||
LinkedBlockingDeque<Packet> outgoingQueue;//发送队列
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h3>SendThread</h3>
|
||||
|
||||
<p>下面我们再看一下 SendThread 的底层实现,SendThread 类是一个线程类,其本质是一个 I/O 调度线程,它的作用就是用来管理操作客户端和服务端的网络 I/O 等。在 ZooKeeper 服务的运行过程中,SendThread 类的作用除了上面提到的负责将客户端的请求发送给服务端外,另一个作用是发送客户端是否存活的心跳检查,SendThread 类负责定期向服务端发送 PING 包来实现心跳检查。</p>
|
||||
|
||||
<pre><code class="language-java">private void sendPing() {
|
||||
|
||||
|
||||
|
||||
lastPingSentNs = System.nanoTime();
|
||||
|
||||
|
||||
|
||||
RequestHeader h = new RequestHeader(-2, OpCode.ping);
|
||||
|
||||
|
||||
|
||||
queuePacket(h, null, null, null, null, null, null, null, null);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>如上面的代码所示,SendThread 类中通过 sendPing 函数向 ZooKeeper 服务端发送心跳检查。而在 sendPing 函数的内部,首先以纳秒为单位获取系统当前时间 lastPingSentNs,之后设置请求头的操作类型为心跳检查操作 OpCode.ping,最后将请求信息封装成 Packet 对象发送给 ZooKeeper 服务端。</p>
|
||||
|
||||
<h3>EventThread</h3>
|
||||
|
||||
<p>SendThread 类的主要工作可以简单地理解为负责客户端向服务端发送请求等操作。而像我们之前学到的 Watch 监控机制,在事件触发后 ZooKeeper 服务端会发送通知给相关的客户端,那么在这个过程中,客户端是如何接收服务端的请求的呢?</p>
|
||||
|
||||
<p>ZooKeeper 是通过 EventThread 类来实现的,EventThread 类也是一个线程类,主要负责客户端的事件处理,比如在客户端接收 Watch 通知时,触发客户端的相关方法。在 EventThread 类中,如下面的代码所示,通过将要触发的事件对象存放在 waitingEvents 队列中,之后在接收到相应的事件通知时,会从该队列中取出对应的事件信息,之后调用 process 函数进行处理。</p>
|
||||
|
||||
<pre><code class="language-java">class EventThread extends ZooKeeperThread {
|
||||
|
||||
|
||||
|
||||
private final LinkedBlockingQueue<Object> waitingEvents
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
process();
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h3>结束</h3>
|
||||
|
||||
<p>通过本节课的学习,我们掌握了客户端核心类的工作原理,在客户端的底层实现中,ClientCnxn 类作为其最核心的实现类,当客户端要向服务端发送请求操作的时候,首先会将请求信息封装成 Packet 对象并加入请求队列中,之后通过 SendThread 网络 I/O 线将请求发送给服务端。</p>
|
||||
|
||||
<p>而在接收服务端响应时,客户端使用 EventThread 类处理接收响应事件以及触发客户端的回调方法。</p>
|
||||
|
||||
<p>这里留给你一道思考题:我们知道为了向服务端证明客户端是存活的,需要 ZooKeeper 客户端周期性的发送 Ping 操作给 ZooKeeper 服务端。而在 ZooKeeper 服务端收到 Ping 操作后,又做了什么操作呢?</p>
|
||||
|
||||
<p>答案是在 ZooKeeper 服务端收到 Ping 操作的请求时,会根据服务端的当前时间重置与客户端的 Session 时间,更新该会话的请求延迟时间等。进而保持客户端与服务端连接状态。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">下一页</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":"709975d8cce93cfa","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>
|
||||
|
||||
902
专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md.html
Normal file
@@ -0,0 +1,902 @@
|
||||
<!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>11 分桶策略:如何实现高效的会话管理?.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>11 分桶策略:如何实现高效的会话管理?</h1>
|
||||
|
||||
<p>前几个课时我们一直围绕会话这个主题进行讲解,今天这节课我们依然还要学习会话的相关知识,本节课我们从 ZooKeeper 会话管理的角度来深入探索一下 ZooKeeper 会话管理的方式。</p>
|
||||
|
||||
<p>我们知道 ZooKeeper 作为分布式系统的核心组件,在一个分布式系统运行环境中经常要处理大量的会话请求,而 ZooKeeper 之所以能够快速响应客户端操作,这与它自身的会话管理策略密不可分。</p>
|
||||
|
||||
<h3>会话管理策略</h3>
|
||||
|
||||
<p>通过前面的学习,我们知道在 ZooKeeper 中为了保证一个会话的存活状态,客户端需要向服务器周期性地发送心跳信息。而客户端所发送的心跳信息可以是一个 ping 请求,也可以是一个普通的业务请求。ZooKeeper 服务端接收请求后,会更新会话的过期时间,来保证会话的存活状态。从中也能看出,<strong>在 ZooKeeper 的会话管理中,最主要的工作就是管理会话的过期时间。</strong></p>
|
||||
|
||||
<p>ZooKeeper 中采用了独特的会话管理方式来管理会话的过期时间,网络上也给这种方式起了一个比较形象的名字:“分桶策略”。我将结合下图给你讲解“分桶策略”的原理。如下图所示,在 ZooKeeper 中,会话将按照不同的时间间隔进行划分,超时时间相近的会话将被放在同一个间隔区间中,这种方式避免了 ZooKeeper 对每一个会话进行检查,而是采用分批次的方式管理会话。这就降低了会话管理的难度,因为每次小批量的处理会话过期也提高了会话处理的效率。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F7gfSCAGDAIAABZCSkfyB0372.png" alt="image" /></p>
|
||||
|
||||
<p>通过上面的介绍,我们对 ZooKeeper 中的会话管理策略有了一个比较形象的理解。而为了能够在日常开发中使用好 ZooKeeper,面对高并发的客户端请求能够开发出更加高效稳定的服务,根据服务器日志判断客户端与服务端的会话异常等。下面我们从技术角度去说明 ZooKeeper 会话管理的策略,进一步加强对会话管理的理解。</p>
|
||||
|
||||
<h3>底层实现</h3>
|
||||
|
||||
<p>说到 ZooKeeper 底层实现的原理,核心的一点就是过期队列这个数据结构。所有会话过期的相关操作都是围绕这个队列进行的。可以说 ZooKeeper 底层就是采用这个队列结构来管理会话过期的。</p>
|
||||
|
||||
<p>而在讲解会话过期队列之前,我们首先要知道什么是 bucket。简单来说,一个会话过期队列是由若干个 bucket 组成的。而 bucket 是一个按照时间划分的区间。在 ZooKeeper 中,通常以 expirationInterval 为单位进行时间区间的划分,它是 ZooKeeper 分桶策略中用于划分时间区间的最小单位。</p>
|
||||
|
||||
<p>在 ZooKeeper 中,一个过期队列由不同的 bucket 组成。每个 bucket 中存放了在某一时间内过期的会话。将会话按照不同的过期时间段分别维护到过期队列之后,在 ZooKeeper 服务运行的过程中,具体的执行过程如下图所示。首先,ZooKeeper 服务会开启一个线程专门用来检索过期队列,找出要过期的 bucket,而 ZooKeeper 每次只会让一个 bucket 的会话过期,每当要进行会话过期操作时,ZooKeeper 会唤醒一个处于休眠状态的线程进行会话过期操作,之后会按照上面介绍的操作检索过期队列,取出过期的会话后会执行过期操作。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl7gfSqADJ72AAA2hG45T-M370.png" alt="image" /></p>
|
||||
|
||||
<p>下面我们再来看一下 ZooKeeper 底层代码是如何实现会话过期队列的,在 ZooKeeper 底层中,使用 ExpiryQueue 类来实现一个会话过期策略。如下面的代码所示,在 ExpiryQueue 类中具有一个 elemMap 属性字段。它是一个线程安全的 HaspMap 列表,用来根据不同的过期时间区间存储会话。而 ExpiryQueue 类中也实现了诸如 remove 删除、update 更新以及 poll 等队列的常规操作方法。</p>
|
||||
|
||||
<pre><code>public class ExpiryQueue<E> {
|
||||
|
||||
|
||||
|
||||
private final ConcurrentHashMap<E, Long> elemMap;
|
||||
|
||||
|
||||
|
||||
public Long remove(E elem) {...}
|
||||
|
||||
|
||||
|
||||
public Long update(E elem, int timeout) {...}
|
||||
|
||||
|
||||
|
||||
public Set<E> poll() {...}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>通过 ExpiryQueue 类实现一个用于管理 ZooKeeper 会话的过期队列之后,ZooKeeper 会将所有会话都加入 ExpiryQueue 列表中进行管理。接下来最主要的工作就是何时去检查该列表中的会话,并取出其中的过期会话进行操作了。一般来说,当一个会话即将过期时,就要对列表进行操作。而一个会话的过期时间 = 系统创建会话的时间 + 会话超时时间。而每个会话的创建时间又各不相同,ZooKeeper 服务没有时刻去监控每一个会话是否过期。而是通过 roundToNextInterval 函数将会话过期时间转化成心跳时间的整数倍,根据不同的过期时间段管理会话。</p>
|
||||
|
||||
<pre><code>private long roundToNextInterval(long time) {
|
||||
|
||||
|
||||
|
||||
return (time / expirationInterval + 1) * expirationInterval;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>如上面的代码所示,roundToNextInterval 函数的主要作用就是以向上取正的方式计算出每个会话的时间间隔,当会话的过期时间发生更新时,会根据函数计算的结果来决定它属于哪一个时间间隔。计算时间间隔公式是(time / ExpirationInterval + 1) ExpirationInterval,比如我们取 expirationInterval 的值为 2,会话的超时 time 为10,那么最终我们计算的 bucket 时间区间就是 12。</p>
|
||||
|
||||
<p>现在我们已经介绍了 ZooKeeper 会话管理的所有前期准备工作,当 ZooKeeper 服务进行会话超时检查的时候,会调用 SessionTrackerImpl 类专门负责此工作。在前面的课程中,我们介绍过 SessionTrackerImpl 是一个线程类。如下面的代码所示,在 run 方法中会首先获取会话过期的下一个时间点,之后通过 setSessionClosing 函数设置会话的关闭状态。最后调用 expire 方法进行会话清理工作。</p>
|
||||
|
||||
<pre><code>public void run() {
|
||||
|
||||
|
||||
|
||||
try {
|
||||
|
||||
|
||||
|
||||
while (running) {
|
||||
|
||||
|
||||
|
||||
long waitTime = sessionExpiryQueue.getWaitTime();
|
||||
|
||||
|
||||
|
||||
if (waitTime > 0) {
|
||||
|
||||
|
||||
|
||||
Thread.sleep(waitTime);
|
||||
|
||||
|
||||
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (SessionImpl s : sessionExpiryQueue.poll()) {
|
||||
|
||||
|
||||
|
||||
setSessionClosing(s.sessionId);
|
||||
|
||||
|
||||
|
||||
expirer.expire(s);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>接下来我们再深入到 expire 方法内部来看看 ZooKeeper 一次会话管理中的最后一步:会话的过期清理工作。 如下面的代码所示,在 expire 函数的内部,主要工作就是发起一次会话过期的请求操作。首先通过 close 函数向整个 ZooKeeper 服务器发起一次会话过期的请求操作。接收到请求后,ZooKeeper 就会执行诸如删除该会话的临时节点、发起 Watch 通知等操作。</p>
|
||||
|
||||
<pre><code>private void close(long sessionId) {
|
||||
|
||||
|
||||
|
||||
Request si = new Request(null,sessionId,0,OpCode.closeSession, null, null);
|
||||
|
||||
|
||||
|
||||
setLocalSessionFlag(si);
|
||||
|
||||
|
||||
|
||||
submitRequest(si);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>在完成了上面的会话相关操作后, ZooKeeper 最终会将过期会话从 SessionTracker 中删除最后关闭该条会话的连接。</p>
|
||||
|
||||
<h3>总结</h3>
|
||||
|
||||
<p>本课时我们学习了 ZooKeeper 会话中一个最重要的知识点,即会话管理策略。通过本节课的学习,我们知道了 ZooKeeper 在管理会话过期时,采用过期队列这种数据结构来管理会话,在 ZooKeeper 服务的运行过程中,通过唤醒一个线程来在过期队列中查询要过期的会话,并进行过期操作。经过本节课的学习后,我们再回头想一想,ZooKeeper 这种会话管理的好处,在实际生产中为什么它能提高服务的效率?</p>
|
||||
|
||||
<p>答案是 ZooKeeper 这种分段的会话管理策略大大提高了计算会话过期的效率,如果是在一个实际生产环境中,一个大型的分布式系统往往具有很高的访问量。而 ZooKeeper 作为其中的组件,对外提供服务往往要承担数千个客户端的访问,这其中就要对这几千个会话进行管理。在这种场景下,要想通过对每一个会话进行管理和检查并不合适,所以采用将同一个时间段的会话进行统一管理,这样就大大提高了服务的运行效率。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">下一页</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":"709975db29753cfa","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>
|
||||
|
||||
878
专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md.html
Normal file
@@ -0,0 +1,878 @@
|
||||
<!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>12 服务端是如何处理一次会话请求的?.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>12 服务端是如何处理一次会话请求的?</h1>
|
||||
|
||||
<p>在进阶篇中,我们主要学习的内容是 ZooKeeper 客户端与服务器端的通信机制,以及会话的底层实现原理。而本课时是 ZooKeeper 会话相关知识点的最后一节课,我们将重点讲解 ZooKeeper 服务端在收到一次会话请求时其内部的处理过程。</p>
|
||||
|
||||
<h3>服务端处理过程</h3>
|
||||
|
||||
<p>在之前的课程中,我们提过会话的创建过程,当客户端需要和 ZooKeeper 服务端进行相互协调通信时,首先要建立该客户端与服务端的连接会话,在会话成功创建后,ZooKeeper 服务端就可以接收来自客户端的请求操作了。</p>
|
||||
|
||||
<p>ZooKeeper 服务端在处理一次客户端发起的会话请求时,所采用的处理过程很像是一条工厂中的流水生产线。比如在一个毛绒玩具加工厂中,一条生产线上的工人可能只负责给玩具上色这一个具体的工作。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl7jHYCAACUBAABVIt47Rxg107.png" alt="image" /></p>
|
||||
|
||||
<p>ZooKeeper 处理会话请求的方式也像是一条流水线,在这条流水线上,主要参与工作的是三个“工人”,分别是 PrepRequestProcessor 、ProposalRequestProcessor 以及 FinalRequestProcessor。这三个“工人”会协同工作,最终完成一次会话的处理工作,而它的实现方式就是我们之前提到的责任链模式。</p>
|
||||
|
||||
<p>下面我将分别对这三个部分进行讲解:作为第一个处理会话请求的“工人”,PrepRequestProcessor 类主要负责请求处理的准备工作,比如判断请求是否是事务性相关的请求操作。在 PrepRequestProcessor 完成工作后,ProposalRequestProcessor 类承接接下来的工作,对会话请求是否执行询问 ZooKeeper 服务中的所有服务器之后,执行相关的会话请求操作,变更 ZooKeeper 数据库数据。最后所有请求就会走到 FinalRequestProcessor 类中完成踢出重复会话的操作。</p>
|
||||
|
||||
<h3>底层实现</h3>
|
||||
|
||||
<p>通过上面的介绍,我们对 ZooKeeper 服务端在处理一次会话请求的方法过程会有比较具体的了解。接下来我们再从底层实现的角度分析一下在代码层面的实现中,ZooKeeper 有哪些值得我们注意和学习的地方。</p>
|
||||
|
||||
<h4>请求预处理器</h4>
|
||||
|
||||
<p>在 ZooKeeper 服务端,第一个负责处理请求会话的类是 PrepRequestProcessor。它是 ZooKeeper 责任链处理模式上的第一个处理器。PrepRequestProcessor 实现了 RequestProcessor 接口,并继承了线程类 Thread,说明其可以通过多线程的方式调用。在 PrepRequestProcessor 类内部有一个 RequestProcessor 类型的 nextProcessor 属性字段,从名称上就可以看出该属性字段的作用是指向下一个处理器。</p>
|
||||
|
||||
<pre><code>public class PrepRequestProcessor extends Thread implements RequestProcessor {
|
||||
|
||||
|
||||
|
||||
RequestProcessor nextProcessor;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>PrepRequestProcessor 类的主要作用是分辨要处理的请求是否是事务性请求,比如创建节点、更新数据、删除节点、创建会话等,这些请求操作都是事务性请求,在执行成功后会对服务器上的数据造成影响。当 PrepRequestProcessor 类收到请求后,如果判断出该条请求操作是事务性请求,就会针对该条请求创建请求事务头、事务体、会话检查、ACL 检查和版本检查等一系列的预处理工作。如下面的代码所示,上述所有操作的逻辑都是在 PrepRequestProcessor 类中的 pRequest 函数实现的。</p>
|
||||
|
||||
<pre><code>protected void pRequest(Request request) throws RequestProcessorException {
|
||||
|
||||
|
||||
|
||||
switch (request.type) {
|
||||
|
||||
|
||||
|
||||
case OpCode.create:
|
||||
|
||||
|
||||
|
||||
CreateRequest createRequest = new CreateRequest();
|
||||
|
||||
|
||||
|
||||
pRequest2Txn(request.type, zks.getNextZxid(), request,createRequest, true);
|
||||
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case OpCode.delete:
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>在 pRequest 函数的内部,首先根据 OpCode.create 等字段值来判断请求操作的类型是否是事务操作,如果是事务操作,就调用 pRequest2Txn 函数进行预处理,这之后将该条请求交给 nextProcessor 字段指向的处理器进行处理。</p>
|
||||
|
||||
<h3>事物处理器</h3>
|
||||
|
||||
<p>PrepRequestProcessor 预处理器执行完工作后,就轮到 ProposalRequestProcessor 事物处理器上场了,ProposalRequestProcessor 是继 PrepRequestProcessor 后,责任链模式上的第二个处理器。其主要作用就是对事务性的请求操作进行处理,而从 ProposalRequestProcessor 处理器的名字中就能大概猜出,其具体的工作就是“提议”。所谓的“提议”是说,当处理一个事务性请求的时候,ZooKeeper 首先会在服务端发起一次投票流程,该投票的主要作用就是通知 ZooKeeper 服务端的各个机器进行事务性的操作了,避免因为某个机器出现问题而造成事物不一致等问题。在 ProposalRequestProcessor 处理器阶段,其内部又分成了三个子流程,分别是:Sync 流程、Proposal 流程、Commit 流程,下面我将分别对这几个流程进行讲解。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F7jHaCAXeh5AAAg5bIKooo221.png" alt="image" /></p>
|
||||
|
||||
<h4>Sync 流程</h4>
|
||||
|
||||
<p>首先我们看一下 Sync 流程,该流程的底层实现类是 SyncRequestProcess 类。SyncRequestProces 类的作用就是在处理事务性请求时,ZooKeeper 服务中的每台机器都将该条请求的操作日志记录下来,完成这个操作后,每一台机器都会向 ZooKeeper 服务中的 Leader 机器发送事物日志记录完成的通知。</p>
|
||||
|
||||
<h4>Proposal 流程</h4>
|
||||
|
||||
<p>在处理事务性请求的过程中,ZooKeeper 需要取得在集群中过半数机器的投票,只有在这种情况下才能真正地将数据改变。而 Proposal 流程的主要工作就是投票和统计投票结果。投票的方式大体上遵循多数原则,更详细的内容在之后的课程中会展开讲解。</p>
|
||||
|
||||
<h4>Commit 流程</h4>
|
||||
|
||||
<p><strong>请你注意,在完成 Proposal 流程后,ZooKeeper 服务器上的数据不会进行任何改变</strong>,成功通过 Proposal 流程只是说明 ZooKeeper 服务可以执行事务性的请求操作了,而要真正执行具体数据变更,需要在 Commit 流程中实现,这种实现方式很像是 MySQL 等数据库的操作方式。在 Commit 流程中,它的主要作用就是完成请求的执行。其底层实现是通过 CommitProcessor 实现的。如下面的代码所示,CommitProcessor 类的内部有一个 LinkedList 类型的 queuedRequests 队列,queuedRequests 队列的作用是,当 CommitProcessor 收到请求后,并不会立刻对该条请求进行处理,而是将其放在 queuedRequests 队列中。</p>
|
||||
|
||||
<pre><code>class CommitProcessor {
|
||||
|
||||
|
||||
|
||||
LinkedList queuedRequests
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>之后再调用 commit 方法取出 queuedRequests 队列中等待的请求进行处理,如下面的代码所示:</p>
|
||||
|
||||
<pre><code>synchronized public void commit(Request request) {
|
||||
|
||||
|
||||
|
||||
committedRequests.add(request);
|
||||
|
||||
|
||||
|
||||
notifyAll();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>到目前为止,我们就对 ProposalRequestProcessor 处理器的三个流程做了一个大体介绍,更详细的分析我们会在之后的课程进行讲解。</p>
|
||||
|
||||
<h3>最终处理器</h3>
|
||||
|
||||
<p>在经过了上面一系列的处理过程后,请求最终会到达责任链上的最后一个处理器:FinalRequestProcessor。该处理器的作用是检查请求的有效性。而所谓的有效性就是指当前 ZooKeeper 服务所处理的请求是否已经处理过了,如果处理过了,FinalRequestProcessor 处理器就会将该条请求删除;如果不这样操作,就会重复处理会话请求,这样就造成不必要的资源浪费。</p>
|
||||
|
||||
<h3>结束</h3>
|
||||
|
||||
<p>通过本课时的学习,我们掌握了 ZooKeeper 服务端在收到一次会话请求的内部处理过程。并进一步掌握了 ZooKeeper 处理会话请求时所采用的的责任链设计模式的实现原理。而在服务端处理会话请求时,涉及三个类分别是预处理器 PrepRequestProcessor、事物处理器 ProposalRequestProcessor,以及 final 最终处理器 FinalRequestProcessor。</p>
|
||||
|
||||
<p>本课时也是会话相关知识的最后一课,经过了前面几课时的学习。我们应该知道<strong>什么是会话、会话都有哪几种状态以及会话在服务端的处理过程是怎么实现的</strong>。掌握这些知识对我们接下来的学习至关重要,如果对我刚才提到的知识点还有疑问,建议你复习一下前面的内容,做到真正掌握会话的相关知识点。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">下一页</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":"709975dd7e363cfa","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>
|
||||
|
||||
1004
专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html
Normal file
916
专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md.html
Normal file
@@ -0,0 +1,916 @@
|
||||
<!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>14 Leader 选举:如何保证分布式数据的一致性?.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>14 Leader 选举:如何保证分布式数据的一致性?</h1>
|
||||
|
||||
<p>在前面的课程中,我们介绍了 ZooKeeper 集群服务的相关知识,我们知道在 ZooKeeper 集群中,服务器分为 Leader 服务器、 Follower 服务器以及 Observer 服务器。</p>
|
||||
|
||||
<p>可以这样认为,Leader 选举是一个过程,在这个过程中 ZooKeeper 主要做了两个重要工作,一个是数据同步,另一个是选举出新的 Leader 服务器。今天我们主要先介绍第一个工作,ZooKeeper 集群中的数据同步问题。</p>
|
||||
|
||||
<h3>Leader 的协调过程</h3>
|
||||
|
||||
<p>在分布式系统中有一个著名的 CAP 定理,是说一个分布式系统不能同时满足一致性、可用性,以及分区容错性。今天我们要讲的就是一致性。其实 ZooKeeper 中实现的一致性也不是强一致性,即集群中各个服务器上的数据每时每刻都是保持一致的特性。在 ZooKeeper 中,采用的是最终一致的特性,<strong>即经过一段时间后,ZooKeeper 集群服务器上的数据最终保持一致的特性</strong>。</p>
|
||||
|
||||
<p>在 ZooKeeper 集群中,Leader 服务器主要负责处理事物性的请求,而在接收到一个客户端的事务性请求操作时,Leader 服务器会先向集群中的各个机器针对该条会话发起投票询问。</p>
|
||||
|
||||
<p>要想实现 ZooKeeper 集群中的最终一致性,我们先要确定什么情况下会对 ZooKeeper 集群服务产生不一致的情况。如下图所示:</p>
|
||||
|
||||
<p><img src="assets/CgqCHl7siPqAEgUiAAAsZnldQKA321.png" alt="image.png" /></p>
|
||||
|
||||
<p>在集群初始化启动的时候,首先要同步集群中各个服务器上的数据。而在集群中 Leader 服务器崩溃时,需要选举出新的 Leader 而在这一过程中会导致各个服务器上数据的不一致,所以当选举出新的 Leader 服务器后需要进行数据的同步操作。</p>
|
||||
|
||||
<h3>底层实现</h3>
|
||||
|
||||
<p>与上面介绍的一样,我们的底层实现讲解主要围绕 ZooKeeper 集群中数据一致性的底层实现。ZooKeeper 在集群中采用的是多数原则方式,即<strong>当一个事务性的请求导致服务器上的数据发生改变时,ZooKeeper 只要保证集群上的多数机器的数据都正确变更了,就可以保证系统数据的一致性。</strong> 这是因为在一个 ZooKeeper 集群中,每一个 Follower 服务器都可以看作是 Leader 服务器的数据副本,需要保证集群中大多数机器数据是一致的,这样在集群中出现个别机器故障的时候,ZooKeeper 集群依然能够保证稳定运行。</p>
|
||||
|
||||
<p>在 ZooKeeper 集群服务的运行过程中,数据同步的过程如下图所示。当执行完数据变更的会话请求时,需要对集群中的服务器进行数据同步。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl7siSSAGaKOAABaTR2X0Wo408.png" alt="image" /></p>
|
||||
|
||||
<h3>广播模式</h3>
|
||||
|
||||
<p>ZooKeeper 在代码层的实现中定义了一个 HashSet 类型的变量,用来管理在集群中的 Follower 服务器,之后调用</p>
|
||||
|
||||
<p>getForwardingFollowers 函数获取在集群中的 Follower 服务器,如下面这段代码所示:</p>
|
||||
|
||||
<pre><code>public class Leader(){
|
||||
|
||||
|
||||
|
||||
HashSet<LearnerHandler> forwardingFollowers;
|
||||
|
||||
|
||||
|
||||
public List<LearnerHandler> getForwardingFollowers() {
|
||||
|
||||
|
||||
|
||||
synchronized (forwardingFollowers) {
|
||||
|
||||
|
||||
|
||||
return new ArrayList<LearnerHandler>(forwardingFollowers);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>在 ZooKeeper 集群服务器对一个事物性的请求操作进行投票并通过后,Leader 服务器执行
|
||||
|
||||
isQuorumSynced 方法判断该 ZooKeeper 集群中的 Follower 节点的连接状态,由于 isQuorumSynced 方法可以被多个线程进行调用,所以在进行操作的时候要通过forwardingFollowers 字段进行加锁操作。之后遍历集群中的 Follower 服务器,根据服务器 zxid、以及数据同步状态等条件判断服务器的执行逻辑是否成功。之后统计 Follower 服务器的 sid 并返回。</p>
|
||||
|
||||
<pre><code>public boolean isQuorumSynced(QuorumVerifier qv) {
|
||||
|
||||
|
||||
|
||||
synchronized (forwardingFollowers) {
|
||||
|
||||
|
||||
|
||||
for (LearnerHandler learnerHandler: forwardingFollowers){
|
||||
|
||||
|
||||
|
||||
if(learnerHandler.synced()){
|
||||
|
||||
|
||||
|
||||
ids.add(learnerHandler.getSid());
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>通过上面的介绍,Leader 服务器在集群中已经完成确定 Follower 服务器状态等同步数据前的准备工作,接下来 Leader 服务器会通过 request.setTxn 方法向集群中的 Follower 服务器发送数据变更的会话请求。这个过程中,我们可以把 Leader 服务器看作是 ZooKeeper 服务中的客户端,而其向集群中 Follower 服务器发送数据更新请求,集群中的 Follower 服务器收到请求后会处理该会话,之后进行数据变更操作。如下面的代码所示,在底层实现中,通过调用 request 请求对象的 setTxn 方法向 Follower 服务器发送请求,在 setTxn 函数中我们传入的参数有操作类型字段 CONFIG_NODE,表明该操作是数据同步操作。</p>
|
||||
|
||||
<pre><code>request.setTxn(new SetDataTxn(ZooDefs.CONFIG_NODE, request.qv.toString().getBytes(), -1));
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h3>恢复模式</h3>
|
||||
|
||||
<p>介绍完 Leader 节点如何管理 Follower 服务器进行数据同步后,接下来我们看一下当 Leader 服务器崩溃后 ZooKeeper 集群又是如何进行数据的恢复和同步的。</p>
|
||||
|
||||
<p>在前面的课程中我们介绍过,当 ZooKeeper 集群中一个 Leader 服务器失效时,会重新在 Follower 服务器中选举出一个新的服务器作为 Leader 服务器。而 ZooKeeper 服务往往处在高并发的使用场景中,如果在这个过程中有新的事务性请求操作,应该如何处理呢? 由于此时集群中不存在 Leader 服务器了,理论上 ZooKeeper 会直接丢失该条请求,会话不进行处理,但是这样做在实际的生产中显然是不行的,那么 ZooKeeper 具体是怎么做的呢?</p>
|
||||
|
||||
<p>在 ZooKeeper 中,重新选举 Leader 服务器会经历一段时间,因此理论上在 ZooKeeper 集群中会短暂的没有 Leader 服务器,在这种情况下接收到事务性请求操作的时候,ZooKeeper 服务会先将这个会话进行挂起操作,挂起的会话不会计算会话的超时时间,之后在 Leader 服务器产生后系统会同步执行这些会话操作。</p>
|
||||
|
||||
<p>到这里我们就对 ZooKeeper 中数据一致性的解决原理和底层实现都做了较为详细的介绍。我们总结一下,ZooKeeper 集群在处理一致性问题的时候基本采用了两种方式来协调集群中的服务器工作,分别是恢复模式和广播模式。</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><strong>恢复模式</strong>:当 ZooKeeper 集群中的 Leader 服务器崩溃后,ZooKeeper 集群就采用恢复模式的方式进行工作,在这个工程中,ZooKeeper 集群会首先进行 Leader 节点服务器的重新选择,之后在选举出 Leader 服务器后对系统中所有的服务器进行数据同步进而保证集群中服务器上的数据的一致性。</li>
|
||||
|
||||
<li><strong>广播模式</strong>:当 ZooKeeper 集群中具有 Leader 服务器,并且可以正常工作时,集群中又有新的 Follower 服务器加入 ZooKeeper 中参与工作,这种情况常常发生在系统性能到达瓶颈,进而对系统进行动态扩容的使用场景。在这种情况下,如果不做任何操作,那么新加入的服务器作为 Follower 服务器,其上的数据与 ZooKeeper 集群中其他服务器上的数据不一致。当有新的查询会话请求发送到 ZooKeeper 集群进行处理,而恰巧该请求实际被分发给这台新加入的 Follower 机器进行处理,就会导致明明在集群中存在的数据,在这台服务器上却查询不到,导致数据查询不一致的情况。因此,在当有新的 Follower 服务器加入 ZooKeeper 集群中的时候,该台服务器会在恢复模式下启动,并找到集群中的 Leader 节点服务器,并同该 Leader 服务器进行数据同步。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h3>LearnerHandler</h3>
|
||||
|
||||
<p>介绍完 ZooKeeper 集群中数据同步的理论方法,我们再来分析一下在代码层面是如何实现的。记得在前面的课程中,我们提到过一个 LearnerHandler 类, 当时我们只是简单地从服务器之间的通信和协同工作的角度去分析了该类的作用。而 LearnerHandler 类其实可以看作是所有 Learner 服务器内部工作的处理者,它所负责的工作有:进行 Follower、Observer 服务器与 Leader 服务器的数据同步、事务性会话请求的转发以及 Proposal 提议投票等功能。</p>
|
||||
|
||||
<p>LearnerHandler 是一个多线程的类,在 ZooKeeper 集群服务运行过程中,一个 Follower 或 Observer 服务器就对应一个 LearnerHandler 。在集群服务器彼此协调工作的过程中,Leader 服务器会与每一个 Learner 服务器维持一个长连接,并启动一个单独的 LearnerHandler 线程进行处理。</p>
|
||||
|
||||
<p>如下面的代码所示,在 LearnerHandler 线程类中,最核心的方法就是 run 方法,处理数据同步等功能都在该方法中进行调用。首先通过 syncFollower 函数判断数据同步的方式是否是快照方式。如果是快照方式,就将 Leader 服务器上的数据操作日志 dump 出来发送给 Follower 等服务器,在 Follower 等服务器接收到数据操作日志后,在本地执行该日志,最终完成数据的同步操作。</p>
|
||||
|
||||
<pre><code>public void run() {
|
||||
|
||||
|
||||
|
||||
boolean needSnap = syncFollower(peerLastZxid, leader.zk.getZKDatabase(), leader);
|
||||
|
||||
|
||||
|
||||
if(needSnap){
|
||||
|
||||
|
||||
|
||||
leader.zk.getZKDatabase().serializeSnapshot(oa);
|
||||
|
||||
|
||||
|
||||
oa.writeString("BenWasHere", "signature");
|
||||
|
||||
|
||||
|
||||
bufferedOutput.flush();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>通过操作日志的方式进行数据同步或备份的操作已经是行业中普遍采用的方式,比如我们都熟悉的 MySQL 、Redis 等数据库也是采用操作日志的方式。</p>
|
||||
|
||||
<h3>结束</h3>
|
||||
|
||||
<p>本课时我们学习了 ZooKeeper 集群是如何保证数据的一致性的,并且知道了 Leader 服务器在集群中起到协调和管理 ZooKeeper 集群中其他角色服务器的作用。</p>
|
||||
|
||||
<p>请你注意,在本课时的介绍中,为了更好地讲解 Leader 服务器在管理集群服务器中数据一致性的作用,我介绍了当集群中 Leader 服务器崩溃时,如何处理事务性会话请求的过程。现在我们知道了在这种情况下,ZooKeeper 会把事务性请求会话挂起,暂时不进行操作。可能有些同学会产生这样的问题:如果会话挂起过多,会不会对系统产生压力,当 Leader 服务器产生后,一下子要处理大量的会话请求,这样不会造成服务器高负荷吗?</p>
|
||||
|
||||
<p>这里请你放心,Leader 选举的过程非常快速,在这期间不会造成大量事务性请求的会话操作积压,并不会对集群性能产生大的影响。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">下一页</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":"709975e2590d3cfa","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>
|
||||
|
||||
1086
专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html
Normal file
@@ -0,0 +1,898 @@
|
||||
<!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>16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略</h1>
|
||||
|
||||
<p>在前面的课时中,我们已经对 ZooKeeper 集群中 Leader 服务器的选举等相关操作进行了详细介绍。本课时我们继续将焦点集中在 ZooKeeper 集群中的相关操作。在 Leader 节点选举后,还需要把 Leader 服务器和 Follow 服务器进行数据同步。在保证整个 ZooKeeper 集群中服务器数据一致的前提下,ZooKeeper 集群才能对外提供服务。</p>
|
||||
|
||||
<h3>为什么要进行同步</h3>
|
||||
|
||||
<p>接着上面介绍的内容,在我们介绍 ZooKeeper 集群数据同步之前,先要清楚为什么要进行数据同步。在 ZooKeeper 集群服务运行过程中,主要负责处理发送到 ZooKeeper 集群服务端的客户端会话请求。这些客户端的会话请求基本可以分为事务性的会话请求和非事务性的会话请求,而这两种会话的本质区别在于,执行会话请求后,ZooKeeper 集群服务器状态是否发生改变。</p>
|
||||
|
||||
<p>事物性会话请求最常用的操作类型有节点的创建、删除、更新等操作。而查询数据节点等会话请求操作就是非事务性的,因为查询不会造成 ZooKeeper 集群中服务器上数据状态的变更 。</p>
|
||||
|
||||
<p>我们之前介绍过,分布式环境下经常会出现 CAP 定义中的一致性问题。比如当一个 ZooKeeper 集群服务器中,Leader 节点处理了一个节点的创建会话操作后,该 Leader 服务器上就新增了一个数据节点。而如果不在 ZooKeeper 集群中进行数据同步,那么其他服务器上的数据则保持旧有的状态,新增加的节点在服务器上不存在。当 ZooKeeper 集群收到来自客户端的查询请求时,会出现该数据节点查询不到的情况,<strong>这就是典型的集群中服务器数据不一致的情况</strong>。为了避免这种情况的发生,在进行事务性请求的操作后,ZooKeeper 集群中的服务器要进行数据同步,而主要的数据同步是从 Learnning 服务器同步 Leader 服务器上的数据。</p>
|
||||
|
||||
<h3>同步方法</h3>
|
||||
|
||||
<p>在介绍了 ZooKeeper 集群服务器的同步作用后,接下来我们再学习一下 ZooKeeper 集群中数据同步的方法。<strong>我们主要通过三个方面来讲解 ZooKeeper 集群中的同步方法,分别是同步条件、同步过程、同步后的处理。</strong></p>
|
||||
|
||||
<h4>同步条件</h4>
|
||||
|
||||
<p>同步条件是指在 ZooKeeper 集群中何时触发数据同步的机制。与上一课时中 Leader 选举首先要判断集群中 Leader 服务器是否存在不同,<strong>要想进行集群中的数据同步,首先需要 ZooKeeper 集群中存在用来进行数据同步的 Learning 服务器。</strong> 也就是说,当 ZooKeeper 集群中选举出 Leader 节点后,除了被选举为 Leader 的服务器,其他服务器都作为 Learnning 服务器,并向 Leader 服务器注册。之后系统就进入到数据同步的过程中。</p>
|
||||
|
||||
<h4>同步过程</h4>
|
||||
|
||||
<p>在数据同步的过程中,ZooKeeper 集群的主要工作就是将那些没有在 Learnning 服务器上执行过的事务性请求同步到 Learning 服务器上。<strong>这里请你注意,事务性的会话请求会被同步,而像数据节点的查询等非事务性请求则不在数据同步的操作范围内。</strong> 而在具体实现数据同步的时候,ZooKeeper 集群又提供四种同步方式,如下图所示:</p>
|
||||
|
||||
<p><img src="assets/CgqCHl7zLYaASBk3AAA-I033owc988.png" alt="image" /></p>
|
||||
|
||||
<h4>DIFF 同步</h4>
|
||||
|
||||
<p>DIFF 同步即差异化同步的方式,在 ZooKeeper 集群中,Leader 服务器探测到 Learnning 服务器的存在后,首先会向该 Learnning 服务器发送一个 DIFF 不同指令。在收到该条指令后,Learnning 服务器会进行差异化方式的数据同步操作。在这个过程中,Leader 服务器会将一些 Proposal 发送给 Learnning 服务器。之后 Learnning 服务器在接收到来自 Leader 服务器的 commit 命令后执行数据持久化的操作。</p>
|
||||
|
||||
<h4>TRUNC+DIFF 同步</h4>
|
||||
|
||||
<p>TRUNC+DIFF 同步代表先回滚再执行差异化的同步,这种方式一般发生在 Learnning 服务器上存在一条事务性的操作日志,但在集群中的 Leader 服务器上并不存在的情况 。发生这种情况的原因可能是 Leader 服务器已经将事务记录到本地事务日志中,但没有成功发起 Proposal 流程。当这种问题产生的时候,ZooKeeper 集群会首先进行回滚操作,在 Learning 服务器上的数据回滚到与 Leader 服务器上的数据一致的状态后,再进行 DIFF 方式的数据同步操作。</p>
|
||||
|
||||
<h4>TRUNC 同步</h4>
|
||||
|
||||
<p>TRUNC 同步是指仅回滚操作,就是将 Learnning 服务器上的操作日志数据回滚到与 Leader 服务器上的操作日志数据一致的状态下。之后并不进行 DIFF 方式的数据同步操作。</p>
|
||||
|
||||
<h4>SNAP 同步</h4>
|
||||
|
||||
<p>SNAP 同步的意思是全量同步,是将 Leader 服务器内存中的数据全部同步给 Learnning 服务器。在进行全量同步的过程中,Leader 服务器首先会向 ZooKeeper 集群中的 Learning 服务器发送一个 SNAP 命令,在接收到 SNAP 命令后, ZooKeeper 集群中的 Learning 服务器开始进行全量同步的操作。随后,Leader 服务器会从内存数据库中获取到全量数据节点和会话超时时间记录器,将他们序列化后传输给 Learnning 服务器。Learnning 服务器接收到该全量数据后,会对其反序列化后载入到内存数据库中。</p>
|
||||
|
||||
<h4>同步后的处理</h4>
|
||||
|
||||
<p>数据同步的本质就是比对 Leader 服务器与 Learning 服务器,将 Leader 服务器上的数据增加到 Learnning 服务器,再将 Learnning 服务器上多余的事物日志回滚。前面的介绍已经完成了数据的对比与传递操作,接下来就在 Learning 服务器上执行接收到的事物日志,进行本地化的操作。</p>
|
||||
|
||||
<h3>底层实现</h3>
|
||||
|
||||
<p>到现在为止,我们已经学习了 ZooKeeper 集群中数据同步的方法,下面我们深入到代码层面来看一下 ZooKeeper 的底层是如何实现的。首先我们来看看 Learnning 服务器是如何接收和判断同步方式的。如下面的代码所示,ZooKeeper 底层实现了一个 Learner 类,该类可以看作是集群中 Learnning 服务器的实例对象,与集群中的 Learning 服务器是一一对应的。</p>
|
||||
|
||||
<pre><code>public class Learner {}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>而在 Learner 类的内部,主要通过 syncWithLeader 函数来处理来自 Leader 服务器的命令。在接收到来自 Leader 服务器的命令后,通过 qp.getType() 方法判断数据同步的方式。</p>
|
||||
|
||||
<pre><code>protected void syncWithLeader(long newLeaderZxid) throws Exception{
|
||||
|
||||
|
||||
|
||||
if (qp.getType() == Leader.DIFF) {
|
||||
|
||||
|
||||
|
||||
snapshotNeeded = false;
|
||||
|
||||
|
||||
|
||||
}else if (qp.getType() == Leader.TRUNC) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>在确定了数据同步的方式后,再调用 packetsCommitted.add(qp.getZxid()) 方法将事物操作同步到处理队列中,之后调用事物操作线程进行处理。</p>
|
||||
|
||||
<pre><code>if (pif.hdr.getZxid() == qp.getZxid() && qp.getType() == Leader.COMMITANDACTIVATE) {
|
||||
|
||||
|
||||
|
||||
QuorumVerifier qv = self.configFromString(new String(((SetDataTxn) pif.rec).getData()));
|
||||
|
||||
|
||||
|
||||
boolean majorChange = self.processReconfig(qv, ByteBuffer.wrap(qp.getData()).getLong(),
|
||||
|
||||
|
||||
|
||||
qp.getZxid(), true);
|
||||
|
||||
|
||||
|
||||
if (majorChange) {
|
||||
|
||||
|
||||
|
||||
throw new Exception("changes proposed in reconfig");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!writeToTxnLog) {
|
||||
|
||||
|
||||
|
||||
if (pif.hdr.getZxid() != qp.getZxid()) {
|
||||
|
||||
|
||||
|
||||
LOG.warn("Committing " + qp.getZxid() + ", but next proposal is " + pif.hdr.getZxid());
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
|
||||
zk.processTxn(pif.hdr, pif.rec);
|
||||
|
||||
|
||||
|
||||
packetsNotCommitted.remove();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
|
||||
packetsCommitted.add(qp.getZxid());
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h3>结束</h3>
|
||||
|
||||
<p>本课时我们学习了 ZooKeeper 集群中数据同步的相关知识。知道了 ZooKeeper 集群之所以进行数据同步,是为了避免在处理事务性会话请求时,服务器上的数据状态发生变化,最终导致在 ZooKeeper 集群中出现数据不一致的情况。因此,在处理新增数据节点等会话请求的时候,需要在 ZooKeeper 集群中进行数据同步。</p>
|
||||
|
||||
<p>而在 ZooKeeper 集群数据同步的过程中,一般采用四种同步方式,这里我们要注意的是 TRUNC+DIFF 这种同步方式,我们上面讲到过,这种同步方式是先回滚数据再同步数据。而回滚到的状态可以看作是删除在 Leader 服务器上不存在的事务性操作记录。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">下一页</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":"709975e70adb3cfa","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>
|
||||
|
||||
904
专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html
Normal file
@@ -0,0 +1,904 @@
|
||||
<!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>17 集群中 Leader 的作用:事务的请求处理与调度分析.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>17 集群中 Leader 的作用:事务的请求处理与调度分析</h1>
|
||||
|
||||
<p>本课时主要学习 Leader 在集群中的作用以及实现原理,在前面的课程中我们一直围绕 ZooKeeper 集群的功能来研究其底层实现原理,今天这节课我们还是围绕这个主题来进一步探究 Leader 服务器在 ZooKeeper 中的作用,即处理事务性的会话请求以及管理 ZooKeeper 集群中的其他角色服务器。而在接收到来自客户端的事务性会话请求后,ZooKeeper 集群内部又</p>
|
||||
|
||||
<p>是如何判断会话的请求类型,以及转发处理事务性请求的呢?带着这些问题我们继续本节课的学习。</p>
|
||||
|
||||
<h3>事务性请求处理</h3>
|
||||
|
||||
<p>在 ZooKeeper 集群接收到来自客户端的会话请求操作后,首先会判断该条请求是否是事务性的会话请求。对于事务性的会话请求,ZooKeeper 集群服务端会将该请求统一转发给 Leader 服务器进行操作。通过前面我们讲过的,Leader 服务器内部执行该条事务性的会话请求后,再将数据同步给其他角色服务器,从而保证事务性会话请求的执行顺序,进而保证整个 ZooKeeper 集群的数据一致性。</p>
|
||||
|
||||
<p>在 ZooKeeper 集群的内部实现中,是通过什么方法保证所有 ZooKeeper 集群接收到的事务性会话请求都能交给 Leader 服务器进行处理的呢?下面我们就带着这个问题继续学习。</p>
|
||||
|
||||
<p>在 ZooKeeper 集群内部,集群中除 Leader 服务器外的其他角色服务器接收到来自客户端的事务性会话请求后,必须将该条会话请求转发给 Leader 服务器进行处理。 ZooKeeper 集群中的 Follow 和 Observer 服务器,都会检查当前接收到的会话请求是否是事务性的请求,如果是事务性的请求,那么就将该请求以 REQUEST 消息类型转发给 Leader 服务器。</p>
|
||||
|
||||
<p>在 ZooKeeper集群中的服务器接收到该条消息后,会对该条消息进行解析。分析出该条消息所包含的原始客户端会话请求。之后将该条消息提交到自己的 Leader 服务器请求处理链中,开始进行事务性的会话请求操作。如果不是事务性请求,ZooKeeper 集群则交由 Follow 和 Observer 角色服务器处理该条会话请求,如查询数据节点信息。</p>
|
||||
|
||||
<h3>Leader 事务处理分析</h3>
|
||||
|
||||
<p>上面我们介绍了 ZooKeeper 集群在处理事务性会话请求时的内部原理。接下来我们就以客户端发起的创建节点请求 setData 为例,具体看看 ZooKeeper 集群的底层处理过程。</p>
|
||||
|
||||
<p>在 ZooKeeper 集群接收到来自客户端的一个 setData 会话请求后,其内部的处理逻辑基本可以分成四个部分。如下图所示,分别是预处理阶段、事务处理阶段、事务执行阶段、响应客户端。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl78TTKAVHIcAABFlvJZKxw198.png" alt="image.png" /></p>
|
||||
|
||||
<h4>预处理阶段:</h4>
|
||||
|
||||
<p>在预处理阶段,主要工作是通过网络 I/O 接收来自客户端的会话请求。判断该条会话请求的类型是否是事务性的会话请求,之后将该请求提交给</p>
|
||||
|
||||
<p>PrepRequestProcessor 处理器进行处理。封装请求事务头并检查会话是否过期,最后反序列化事务请求信息创建 setDataRequest 请求,在 setDataRequest 记录中包含了要创建数据的节点的路径、数据节点的内容信息以及数据节点的版本信息。最后将该请求存放在 outstandingChanges 队列中等待之后的处理。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl78TUKAfJjuAAB5F0dP0Yk087.png" alt="1.png" /></p>
|
||||
|
||||
<h4>事务处理阶段:</h4>
|
||||
|
||||
<p>在事务处理阶段,ZooKeeper 集群内部会将该条会话请求提交给 ProposalRequestProcessor 处理器进行处理。本阶段内部又分为提交、同步、统计三个步骤。其具体的处理过程我们在之前的课程中已经介绍过了,这里不再赘述。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl78TVGAK4VLAACDtg8BUbg363.png" alt="2.png" /></p>
|
||||
|
||||
<h4>事务执行阶段:</h4>
|
||||
|
||||
<p>在经过预处理阶段和事务会话的投票发起等操作后,一个事务性的会话请求都已经准备好了,接下来就是在 ZooKeeper 的数据库中执行该条会话的数据变更操作。</p>
|
||||
|
||||
<p>在处理数据变更的过程中,ZooKeeper 内部会将该请求会话的事务头和事务体信息直接交给内存数据库 ZKDatabase 进行事务性的持久化操作。之后返回 ProcessTxnResult 对象表明操作结果是否成功。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl78TWOAcPwoAABCnE5qDAc777.png" alt="3.png" /></p>
|
||||
|
||||
<h4>响应客户端:</h4>
|
||||
|
||||
<p>在 ZooKeeper 集群处理完客户端 setData 方法发送的数据节点创建请求后,会将处理结果发送给客户端。而在响应客户端的过程中,ZooKeeper 内部首先会创建一个 setDataResponse 响应体类型,该对象主要包括当前会话请求所创建的数据节点,以及其最新状态字段信息 stat。之后创建请求响应头信息,响应头作为客户端请求响应的重要信息,客户端在接收到 ZooKeeper 集群的响应后,通过解析响应头信息中的事务 ZXID 和请求结果标识符 err 来判断该条会话请求是否成功执行。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl78TXCAM65nAAB2mZDf_jw385.png" alt="4.png" /></p>
|
||||
|
||||
<h3>事务处理底层实现</h3>
|
||||
|
||||
<p>介绍完 ZooKeeper 集群处理事务性会话请求的理论方法和内部过程后。接下来我们从代码层面来进一步分析 ZooKeeper 在处理事务性请求时的底层核心代码实现。</p>
|
||||
|
||||
<p>首先,ZooKeeper 集群在收到客户端发送的事务性会话请求后,会对该请求进行预处理。在代码层面,ZooKeeper 通过调用 PrepRequestProcessor 类来实现预处理阶段的全部逻辑。可以这样理解:<strong>在处理客户端会话请求的时候,首先调用的就是 PrepRequestProcessor 类</strong>。而在 PrepRequestProcessor 内部,是通过 pRequest 方法判断客户端发送的会话请求类型。如果是诸如 setData 数据节点创建等事务性的会话请求,就调用 pRequest2Txn 方法进一步处理。</p>
|
||||
|
||||
<pre><code>protected void pRequest(Request request){
|
||||
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
||||
switch (request.type) {
|
||||
|
||||
|
||||
|
||||
case OpCode.setData:
|
||||
|
||||
|
||||
|
||||
SetDataRequest setDataRequest = new SetDataRequest();
|
||||
|
||||
|
||||
|
||||
pRequest2Txn(request.type, zks.getNextZxid(), request, setDataRequest, true);
|
||||
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>而在 pRequest2Txn 方法的内部,就实现了预处理阶段的主要逻辑。如下面的代码所示,首先通过 checkSession 方法检查该条会话请求是否有效(比如会话是否过期等),之后调用 checkACL 检查发起会话操作的客户端在 ZooKeeper 服务端是否具有相关操作的权限。最后将该条会话创建的相关信息,诸如 path 节点路径、data 节点数据信息、version 节点版本信息等字段封装成
|
||||
|
||||
setDataRequest 类型并传入到 setTxn 方法中,最后加入处理链中进行处理。</p>
|
||||
|
||||
<pre><code>case OpCode.setData:
|
||||
|
||||
|
||||
|
||||
zks.sessionTracker.checkSession(request.sessionId, request.getOwner());
|
||||
|
||||
|
||||
|
||||
SetDataRequest setDataRequest = (SetDataRequest)record;
|
||||
|
||||
|
||||
|
||||
if(deserialize)
|
||||
|
||||
|
||||
|
||||
ByteBufferInputStream.byteBuffer2Record(request.request, setDataRequest);
|
||||
|
||||
|
||||
|
||||
path = setDataRequest.getPath();
|
||||
|
||||
|
||||
|
||||
validatePath(path, request.sessionId);
|
||||
|
||||
|
||||
|
||||
nodeRecord = getRecordForPath(path);
|
||||
|
||||
|
||||
|
||||
checkACL(zks, request.cnxn, nodeRecord.acl, ZooDefs.Perms.WRITE, request.authInfo, path, null);
|
||||
|
||||
|
||||
|
||||
int newVersion = checkAndIncVersion(nodeRecord.stat.getVersion(), setDataRequest.getVersion(), path);
|
||||
|
||||
|
||||
|
||||
request.setTxn(new SetDataTxn(path, setDataRequest.getData(), newVersion));
|
||||
|
||||
|
||||
|
||||
nodeRecord = nodeRecord.duplicate(request.getHdr().getZxid());
|
||||
|
||||
|
||||
|
||||
nodeRecord.stat.setVersion(newVersion);
|
||||
|
||||
|
||||
|
||||
addChangeRecord(nodeRecord);
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h3>结束</h3>
|
||||
|
||||
<p>在本课时中,我们主要学习了 ZooKeeper 集群中 Leader 服务器是如何处理事务性的会话请求的,并且在处理完事务性的会话请求后,是如何通知其他角色服务器进行同步操作的。</p>
|
||||
|
||||
<p>可以说在 ZooKeeper 集群处理事务性的请过程中,Follow 和 Observer 服务器主要负责接收客户端的会话请求,并转发给 Leader 服务器。而真正处理该条会话请求的是 Leader 服务器。</p>
|
||||
|
||||
<p>这就会引发一个问题:当一个业务场景在查询操作多而创建删除等事务性操作少的情况下,ZooKeeper 集群的性能表现的就会很好。而如果是在极端情况下,ZooKeeper 集群只有事务性的会话请求而没有查询操作,那么 Follow 和 Observer 服务器就只能充当一个请求转发服务器的角色, 所有的会话的处理压力都在 Leader 服务器。在处理性能上整个集群服务器的瓶颈取决于 Leader 服务器的性能。ZooKeeper 集群的作用只能保证在 Leader 节点崩溃的时候,重新选举出 Leader 服务器保证系统的稳定性。这也是 ZooKeeper 设计的一个缺点。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">下一页</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":"709975e94f1f3cfa","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>
|
||||
|
||||
@@ -0,0 +1,990 @@
|
||||
<!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>18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析</h1>
|
||||
|
||||
<p>在上节课中,我们学习了 ZooKeeper 集群中 Leader 角色服务器的作用。在 ZooKeeper 集群中,Leader 服务器主要负责处理来自客户端的事务性会话请求,并在处理完事务性会话请求后,管理和协调 ZooKeeper 集群中 Follow 和 Observer 等角色服务器的数据同步。因此,在 ZooKeeper 集群中,Leader 服务器是最为核心的服务器,一个 ZooKeeper 服务在集群模式下运行,必须存在一个 Leader 服务器。而在 ZooKeeper 集群中,是通过崩溃选举的方式来保证 ZooKeeper 集群能够一直存在一个 Leader 服务器对外提供服务的。那么在 ZooKeeper 集群选举出 Leader 的过程中,Follow 服务器又做了哪些工作?</p>
|
||||
|
||||
<p>对这些问题的研究,有助于我们掌握整个 ZooKeeper 集群服务的运行过程,清楚不同状态下服务器的处理逻辑和相关操作。使我们在日常工作中,更好地开发 ZooKeeper 相关服务,并在运维过程中快速定位问题,搭建更加高效稳定的 ZooKeeper 服务器。</p>
|
||||
|
||||
<h3>非事务性请求处理过程</h3>
|
||||
|
||||
<p>在 ZooKeeper 集群接收到来自客户端的请求后,会首先判断该会话请求的类型,如是否是事务性请求。<strong>所谓事务性请求,是指 ZooKeeper 服务器执行完该条会话请求后,是否会导致执行该条会话请求的服务器的数据或状态发生改变,进而导致与其他集群中的服务器出现数据不一致的情况</strong>。</p>
|
||||
|
||||
<p>这里我们以客户端发起的数据节点查询请求为例,分析一下 ZooKeeper 在处理非事务性请求时的实现过程。</p>
|
||||
|
||||
<p>当 ZooKeeper 集群接收到来自客户端发送的查询会话请求后,会将该客户端请求分配给 Follow 服务器进行处理。而在 Follow 服务器的内部,也采用了责任链的处理模式来处理来自客户端的每一个会话请求。</p>
|
||||
|
||||
<p>在第 12 课时中,我们学习了 Leader 服务器的处理链过程,分别包含预处理器阶段、Proposal 提交处理器阶段以及 final 处理器阶段。与 Leader 处理流程不同的是,在 Follow 角色服务器的处理链执行过程中,FollowerRequestProcessor 作为第一个处理器,主要负责筛选该条会话请求是否是事务性的会话请求。如果是事务性的会话请求,则转发给 Leader 服务器进行操作。如果不是事务性的会话请求,则交由 Follow 服务器处理链上的下一个处理器进行处理。</p>
|
||||
|
||||
<p>而下一个处理器是 CommitProcessor ,该处理器的作用是对来自集群中其他服务器的事务性请求和本地服务器的提交请求操作进行匹配。匹配的方式是,将本地执行的 sumbit 提交请求,与集群中其他服务器接收到的 Commit 会话请求进行匹配,匹配完成后再交由 Follow 处理链上的下一个处理器进行处理。最终,当一个客户端会话经过 Final 处理器操作后,就完成了整个 Follow 服务器的会话处理过程,并将结果响应给客户端。</p>
|
||||
|
||||
<h4>底层实现</h4>
|
||||
|
||||
<p>简单介绍完 ZooKeeper 集群中 Follow 服务器在处理非事务性请求的过程后,接下来我们再从代码层面分析一下底层的逻辑实现是怎样的。</p>
|
||||
|
||||
<p>从代码实现的角度讲,ZooKeeper 集群在接收到来自客户端的请求后,会将请求交给 Follow 服务器进行处理。而 Follow 服务器内部首先调用的是 FollowerZooKeeperServer 类,<strong>该类的作用是封装 Follow 服务器的属性和行为,你可以把该类当作一台 Follow 服务器的代码抽象。</strong></p>
|
||||
|
||||
<p>如下图所示,该 FollowerZooKeeperServer 类继承了 LearnerZooKeeperServer 。在一个 FollowerZooKeeperServer 类内部,定义了一个核心的 ConcurrentLinkedQueue 类型的队列字段,用于存放接收到的会话请求。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F7_AqmAchKvAABHDmj-uIc721.png" alt="image" /></p>
|
||||
|
||||
<p>在定义了 FollowerZooKeeperServer 类之后,在该类的 setupRequestProcessors 函数中,定义了我们之前一直反复提到的处理责任链,指定了该处理链上的各个处理器。如下面的代码所示,分别按顺序定义了起始处理器 FollowerRequestProcessor 、提交处理器 CommitProcessor、同步处理器 SendAckRequestProcessor 以及最终处理器 FinalProcessor。</p>
|
||||
|
||||
<pre><code>protected void setupRequestProcessors() {
|
||||
|
||||
|
||||
|
||||
RequestProcessor finalProcessor = new FinalRequestProcessor(this);
|
||||
|
||||
|
||||
|
||||
commitProcessor = new CommitProcessor(finalProcessor,
|
||||
|
||||
|
||||
|
||||
Long.toString(getServerId()), true, getZooKeeperServerListener());
|
||||
|
||||
|
||||
|
||||
commitProcessor.start();
|
||||
|
||||
|
||||
|
||||
firstProcessor = new FollowerRequestProcessor(this, commitProcessor);
|
||||
|
||||
|
||||
|
||||
((FollowerRequestProcessor) firstProcessor).start();
|
||||
|
||||
|
||||
|
||||
syncProcessor = new SyncRequestProcessor(this,
|
||||
|
||||
|
||||
|
||||
new SendAckRequestProcessor((Learner)getFollower()));
|
||||
|
||||
|
||||
|
||||
syncProcessor.start();
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h3>选举过程</h3>
|
||||
|
||||
<p>介绍完 Follow 服务器处理非事务性请求的过程后,接下来我们再学习一下 Follow 服务器的另一个主要的功能:在 Leader 服务器崩溃的时候,重新选举出 Leader 服务器。</p>
|
||||
|
||||
<p><strong>ZooKeeper 集群重新选举 Leader 的过程本质上只有 Follow 服务器参与工作</strong>。而在 ZooKeeper 集群重新选举 Leader 节点的过程中,如下图所示。主要可以分为 Leader 失效发现、重新选举 Leader 、Follow 服务器角色变更、集群同步这几个步骤。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl7_ArmAD4KYAABMANi9AkA539.png" alt="image" /></p>
|
||||
|
||||
<h4>Leader 失效发现</h4>
|
||||
|
||||
<p>通过之前的介绍我们知道,在 ZooKeeper 集群中,当 Leader 服务器失效时,ZooKeeper 集群会重新选举出新的 Leader 服务器。也就是说,Leader 服务器的失效会触发 ZooKeeper 开始新 Leader 服务器的选举,那么在 ZooKeeper 集群中,又是如何发现 Leader 服务器失效的呢?</p>
|
||||
|
||||
<p>这里就要介绍到 Leader 失效发现。和我们之前介绍的保持客户端活跃性的方法,它是通过客户端定期向服务器发送 Ping 请求来实现的。在 ZooKeeper 集群中,探测 Leader 服务器是否存活的方式与保持客户端活跃性的方法非常相似。首先,Follow 服务器会定期向 Leader 服务器发送 网络请求,在接收到请求后,Leader 服务器会返回响应数据包给 Follow 服务器,而在 Follow 服务器接收到 Leader 服务器的响应后,如果判断 Leader 服务器运行正常,则继续进行数据同步和服务转发等工作,反之,则进行 Leader 服务器的重新选举操作。</p>
|
||||
|
||||
<h4>Leader 重新选举</h4>
|
||||
|
||||
<p>当 Follow 服务器向 Leader 服务器发送状态请求包后,如果没有得到 Leader 服务器的返回信息,这时,<strong>如果是集群中个别的 Follow 服务器发现返回错误,并不会导致 ZooKeeper 集群立刻重新选举 Leader 服务器,而是将该 Follow 服务器的状态变更为 LOOKING 状态,并向网络中发起投票,当 ZooKeeper 集群中有更多的机器发起投票,最后当投票结果满足多数原则的情况下。ZooKeeper 会重新选举出 Leader 服务器。</strong></p>
|
||||
|
||||
<h4>Follow 角色变更</h4>
|
||||
|
||||
<p>在 ZooKeeper 集群中,Follow 服务器作为 Leader 服务器的候选者,当被选举为 Leader 服务器之后,其在 ZooKeeper 集群中的 Follow 角色,也随之发生改变。也就是要转变为 Leader 服务器,并作为 ZooKeeper 集群中的 Leader 角色服务器对外提供服务。</p>
|
||||
|
||||
<h4>集群同步数据</h4>
|
||||
|
||||
<p>在 ZooKeeper 集群成功选举 Leader 服务器,并且候选 Follow 服务器的角色变更后。为避免在这期间导致的数据不一致问题,ZooKeeper 集群在对外提供服务之前,会通过 Leader 角色服务器管理同步其他角色服务器,具体的数据同步方法,我们在第14课时中已经详细的讲解过了,这里不再赘述。</p>
|
||||
|
||||
<h4>底层实现</h4>
|
||||
|
||||
<p>介绍完 ZooKeeper 集群重新选举 Leader 服务器的理论方法后,接下来我们再来分析代码层面上 ZooKeeper 的核心实现。</p>
|
||||
|
||||
<p>首先,ZooKeeper 集群会先判断 Leader 服务器是否失效,而判断的方式就是 Follow 服务器向 Leader 服务器发送请求包,之后 Follow 服务器接收到响应数据后,进行解析,如下面的代码所示,Follow 服务器会根据返回的数据,判断 Leader 服务器的运行状态,如果返回的是 LOOKING 关键字,表明与集群中 Leader 服务器无法正常通信。</p>
|
||||
|
||||
<pre><code>switch (rstate) {
|
||||
|
||||
|
||||
|
||||
case 0:
|
||||
|
||||
|
||||
|
||||
ackstate = QuorumPeer.ServerState.LOOKING;
|
||||
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 1:
|
||||
|
||||
|
||||
|
||||
ackstate = QuorumPeer.ServerState.FOLLOWING;
|
||||
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 2:
|
||||
|
||||
|
||||
|
||||
ackstate = QuorumPeer.ServerState.LEADING;
|
||||
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case 3:
|
||||
|
||||
|
||||
|
||||
ackstate = QuorumPeer.ServerState.OBSERVING;
|
||||
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
default:
|
||||
|
||||
|
||||
|
||||
continue;
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>之后,在 ZooKeeper 集群选举 Leader 服务器时,是通过 FastLeaderElection 类实现的。该类实现了 TCP 方式的通信连接,用于在 ZooKeeper 集群中与其他 Follow 服务器进行协调沟通。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F7_AtCAVRpLAABYUblVD80124.png" alt="image" /></p>
|
||||
|
||||
<p>如上图所示,FastLeaderElection 类继承了 Election 接口,定义其是用来进行选举的实现类。而在其内部,又定义了选举通信相关的一些配置参数,比如 finalizeWait 最终等待时间、最大通知间隔时间 maxNotificationInterval 等。</p>
|
||||
|
||||
<p>在选举的过程中,首先调用 ToSend 函数向 ZooKeeper 集群中的其他角色服务器发送本机的投票信息,其他服务器在接收投票信息后,会对投票信息进行有效性验证等操作,之后 ZooKeeper 集群统计投票信息,如果过半数的机器投票信息一致,则集群就重新选出新的 Leader 服务器。</p>
|
||||
|
||||
<pre><code>static public class ToSend {
|
||||
|
||||
|
||||
|
||||
static enum mType {crequest, challenge, notification, ack}
|
||||
|
||||
|
||||
|
||||
ToSend(mType type,
|
||||
|
||||
|
||||
|
||||
long leader,
|
||||
|
||||
|
||||
|
||||
long zxid,
|
||||
|
||||
|
||||
|
||||
long electionEpoch,
|
||||
|
||||
|
||||
|
||||
ServerState state,
|
||||
|
||||
|
||||
|
||||
long sid,
|
||||
|
||||
|
||||
|
||||
long peerEpoch,
|
||||
|
||||
|
||||
|
||||
byte[] configData) {
|
||||
|
||||
|
||||
|
||||
this.leader = leader;
|
||||
|
||||
|
||||
|
||||
this.zxid = zxid;
|
||||
|
||||
|
||||
|
||||
this.electionEpoch = electionEpoch;
|
||||
|
||||
|
||||
|
||||
this.state = state;
|
||||
|
||||
|
||||
|
||||
this.sid = sid;
|
||||
|
||||
|
||||
|
||||
this.peerEpoch = peerEpoch;
|
||||
|
||||
|
||||
|
||||
this.configData = configData;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h3>结束</h3>
|
||||
|
||||
<p>通过本课时的学习,我们知道在 ZooKeeper 集群中 Follow 服务器的功能和作用。Follow 服务器在 ZooKeeper 集群服务运行的过程中,负责处理来自客户端的查询等非事务性的请求操作。当 ZooKeeper 集群中旧的 Leader 服务器失效时,作为投票者重新选举出新的 Leader 服务器。</p>
|
||||
|
||||
<p>这里我们要注意一个问题,那就是在重新选举 Leader 服务器的过程中,ZooKeeper 集群理论上是无法进行事务性的请求处理的。因此,发送到 ZooKeeper 集群中的事务性会话会被挂起,暂时不执行,等到选举出新的 Leader 服务器后再进行操作。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">下一页</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":"709975eb8be43cfa","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>
|
||||
|
||||
1010
专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md.html
Normal file
1086
专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html
Normal file
816
专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md.html
Normal file
@@ -0,0 +1,816 @@
|
||||
<!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>21 ZooKeeper 分布式锁:实现和原理解析.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>21 ZooKeeper 分布式锁:实现和原理解析</h1>
|
||||
|
||||
<p>从本课时开始,我们就进入 ZooKeeper 专栏课程的实战篇。在实战篇中,我们主要介绍在实际生成环境中应该如何使用和设计 ZooKeeper 服务,并给出一些常见的问题以及解决方案。</p>
|
||||
|
||||
<p>在基础篇第 2 课时介绍 Watch 监控机制时,我为你介绍了一个利用 ZooKeeper 中的 Watch 机制实现一个简单的分布式锁的例子。这个例子当时是为了说明 Watch 机制的主要特点和作用。但在实际生产开发的过程中,这种分布式锁作</p>
|
||||
|
||||
<p>为商业系统分布式锁的解决方案,直接利用之前介绍的实现分布式锁的方式,显然过于简单,且其中也有不少缺陷。那么今天这节课就结合这段时间学习到的知识,开发一个商业级别的分布式锁。</p>
|
||||
|
||||
<h3>什么是分布式锁</h3>
|
||||
|
||||
<p>在开始着手开发商业级的分布式锁之前,我们首先要弄清楚什么是分布式锁,以及分布式锁在日常工作的使用场景。明确了这些,我们才能设计出一个安全稳定的分布式锁。</p>
|
||||
|
||||
<p>在日常开发中,我们最熟悉也常用的分布式锁场景是在开发多线程的时候。为了协调本地应用上多个线程对某一资源的访问,就要对该资源或数值变量进行加锁,以保证在多线程环境下系统能够正确地运行。在一台服务器上的程序内部,线程可以通过系统进行线程之间的通信,实现加锁等操作。而<strong>在分布式环境下,执行事务的线程存在于不同的网络服务器中,要想实现在分布式网络下的线程协同操作,就要用到分布式锁</strong>。</p>
|
||||
|
||||
<h3>分布式死锁</h3>
|
||||
|
||||
<p>在单机环境下,多线程之间会产生死锁问题。同样,在分布式系统环境下,也会产生分布式死锁的问题。</p>
|
||||
|
||||
<p>当死锁发生时,系统资源会一直被某一个线程占用,从而导致其他线程无法访问到该资源,最终使整个系统的业务处理或运行性能受到影响,严重的甚至可能导致服务器无法对外提供服务。</p>
|
||||
|
||||
<p>所以当我们在设计开发分布式系统的时候,要准备一些方案来面对可能会出现的死锁问题,当问题发生时,系统会根据我们预先设计的方案,避免死锁对整个系统的影响。<strong>常用的解决死锁问题的方法有超时方法和死锁检测</strong>。</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><strong>超时方法</strong>:在解决死锁问题时,超时方法可能是最简单的处理方式了。<strong>超时方式是在创建分布式线程的时候,对每个线程都设置一个超时时间</strong>。当该线程的超时时间到期后,无论该线程是否执行完毕,都要关闭该线程并释放该线程所占用的系统资源。之后其他线程就可以访问该线程释放的资源,这样就不会造成分布式死锁问题。但是这种设置超时时间的方法也有很多缺点,最主要的就是很难设置一个合适的超时时间。如果时间设置过短,可能造成线程未执行完相关的处理逻辑,就因为超时时间到期就被迫关闭,最终导致程序执行出错。</li>
|
||||
|
||||
<li><strong>死锁检测</strong>:死锁检测是处理死锁问题的另一种方法,它解决了超时方法的缺陷。与超时方法相比,死锁检测方法主动检测发现线程死锁,在控制死锁问题上更加灵活准确。<strong>你可以把死锁检测理解为一个运行在各个服务器系统上的线程或方法,该方法专门用来探索发现应用服务上的线程是否发生了死锁</strong>。如果发生死锁,就会触发相应的预设处理方案。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h3>锁的实现</h3>
|
||||
|
||||
<p>在介绍完分布式锁的基本性质和潜在问题后,接下来我们就通过 ZooKeeper 来实现两种比较常用的分布式锁。</p>
|
||||
|
||||
<h4>排他锁</h4>
|
||||
|
||||
<p>排他锁也叫作独占锁,从名字上就可以看出它的实现原理。当我们给某一个数据对象设置了排他锁后,<strong>只有具有该锁的事务线程可以访问该条数据对象,直到该条事务主动释放锁</strong>。否则,在这期间其他事务不能对该数据对象进行任何操作。在第二课时我们已经学习了利用 ZooKeeper 实现排他锁,这里不再赘述。</p>
|
||||
|
||||
<h4>共享锁</h4>
|
||||
|
||||
<p>另一种分布式锁的类型是共享锁。它在性能上要优于排他锁,这是因为在共享锁的实现中,只对数据对象的写操作加锁,而不为对象的读操作进行加锁。这样既保证了数据对象的完整性,也兼顾了多事务情况下的读取操作。可以说,共享锁是写入排他,而读取操作则没有限制。</p>
|
||||
|
||||
<p>接下来我就通过 ZooKeeper 来实现一个排他锁。</p>
|
||||
|
||||
<h4>创建锁</h4>
|
||||
|
||||
<p>首先,我们通过在 ZooKeeper 服务器上创建数据节点的方式来创建一个共享锁。其实无论是共享锁还是排他锁,在锁的实现方式上都是一样的。唯一的区别在于,<strong>共享锁为一个数据事务创建两个数据节点,来区分是写入操作还是读取操作</strong>。如下图所示,在 ZooKeeper 数据模型上的 Locks_shared 节点下创建临时顺序节点,临时顺序节点的名称中带有请求的操作类型分别是 R 读取操作、W 写入操作。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl8Oc56AEMuZAAAsuQwHWCY999.png" alt="image" /></p>
|
||||
|
||||
<h4>获取锁</h4>
|
||||
|
||||
<p>当某一个事务在访问共享数据时,首先需要获取锁。ZooKeeper 中的所有客户端会在 Locks_shared 节点下创建一个临时顺序节点。根据对数据对象的操作类型创建不同的数据节点,如果是读操作,就创建名称中带有 R 标志的顺序节点,如果是写入操作就创建带有 W 标志的顺序节点。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8Oc6aAH44DAAA1aVd9UXo732.png" alt="image" /></p>
|
||||
|
||||
<h4>释放锁</h4>
|
||||
|
||||
<p>事务逻辑执行完毕后,需要对事物线程占有的共享锁进行释放。我们可以利用 ZooKeeper 中数据节点的性质来实现主动释放锁和被动释放锁两种方式。</p>
|
||||
|
||||
<p>主动释放锁是当客户端的逻辑执行完毕,主动调用 delete 函数删除ZooKeeper 服务上的数据节点。而被动释放锁则利用临时节点的性质,在客户端因异常而退出时,ZooKeeper 服务端会直接删除该临时节点,即释放该共享锁。</p>
|
||||
|
||||
<p>这种实现方式正好和上面介绍的死锁的两种处理方式相对应。到目前为止,我们就利用 ZooKeeper 实现了一个比较完整的共享锁。如下图所示,在这个实现逻辑中,首先通过创建数据临时数据节点的方式实现获取锁的操作。创建数据节点分为两种,分别是读操作的数据节点和写操作的数据节点。当锁节点删除时,注册了该 Watch 监控的其他客户端也会收到通知,重新发起创建临时节点尝试获取锁。当事务逻辑执行完成,客户端会主动删除该临时节点释放锁。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl8O5rOADPbBAACVhsBN-NU550.png" alt="X.png" /></p>
|
||||
|
||||
<h3>总结</h3>
|
||||
|
||||
<p>通过本课时的学习,我们掌握了什么是分布式锁,以及分布式锁在实际生产环境中面临的问题和挑战。无论是单机上的加锁还是分布式环境下的分布式锁,都会出现死锁问题。面对死锁问题,如果我们不能很好地处理,会严重影响系统的运行。在本课时中,我为你讲解了两种处理死锁问题的方法,分别是超时设置和死锁监控。然后重点介绍了利用 ZooKeeper 实现一个共享锁。</p>
|
||||
|
||||
<p>在具体实现的过程中,我们利用 ZooKeeper 数据模型的临时顺序节点和 Watch 监控机制,在客户端通过创建数据节点的方式来获取锁,通过删除数据节点来释放锁。</p>
|
||||
|
||||
<p>这里我给你留一个问题,作为我们课后的作业:在分布式共享锁的实现中,获得锁的线程执行完释放锁后,其他等待资源的线程客户端收到 Watch 通知机制,会尝试获取锁。但是如果等待线程过多,那么频繁的 Watch 通知也会占用系统的网络资源和内存,有没有什么好的办法可以优化呢?在保证共享锁的实现下,减少 Watch 通知次数,这里留给你在本地进行优化。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">下一页</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":"709975f2aa9a3cfa","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>
|
||||
|
||||
820
专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html
Normal file
@@ -0,0 +1,820 @@
|
||||
<!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>22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器</h1>
|
||||
|
||||
<p>在上个课时中,我们讲解了如何利用 ZooKeeper 实现一个分布式锁,并解决在分布式环境下,网络中多线程之间的事务问题。今天我们利用 ZooKeeper 来解决分布式系统环境的另一个实际应用场景:使用 ZooKeeper 实现一个分布式 ID 生成器。</p>
|
||||
|
||||
<p>无论是单机环境还是分布式环境,都有使用唯一标识符标记某一资源的使用场景。比如在淘宝、京东等购物网站下单时,系统会自动生成订单编号,这个订单编号就是一个分布式 ID 的使用。</p>
|
||||
|
||||
<h3>什么是 ID 生成器</h3>
|
||||
|
||||
<p>我们先来介绍一下什么是 ID 生成器。<strong>分布式 ID 生成器就是通过分布式的方式,实现自动生成分配 ID 编码的程序或服务</strong>。在日常开发中,Java 语言中的 UUID 就是生成一个 32 位的 ID 编码生成器。根据日常使用场景,<strong>我们生成的 ID 编码一般具有唯一性、递增性、安全性、扩展性这几个特性</strong>。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl8RWyyAP_5_AACcEDVnsPc537.png" alt="1.png" /></p>
|
||||
|
||||
<p><strong>唯一性</strong>:ID 编码作为标记分布式系统重要资源的标识符,在整个分布式系统环境下,生成的 ID 编码应该具有全局唯一的特性。如果产生两个重复的 ID 编码,就无法通过 ID 编码准确找到对应的资源,这也是一个 ID 编码最基本的要求。</p>
|
||||
|
||||
<p><strong>递增性</strong>:递增性也可以说是 ID 编码的有序特性,它指一般的 ID 编码具有一定的顺序规则。比如 MySQL 数据表主键 ID,一般是一个递增的整数数字,按逐条加一的方式顺序增大。我们现在学习的 ZooKeeper 系统的 zxID 也具有递增的特性,这样在投票阶段就可以根据 zxID 的有序特性,对投票信息进行比对。</p>
|
||||
|
||||
<p><strong>安全性</strong>:有的业务场景对 ID 的安全性有很高的要求,但这里说的安全性是指,如果按照递增的方式生成 ID 编码,那么这种规律很容易被发现。比如淘宝的订单编码,如果被恶意的生成或使用,会严重影响系统的安全性,所以 ID 编码必须保证其安全性。</p>
|
||||
|
||||
<p><strong>扩展性</strong>:该特性是指 ID 编码规则要有一定的扩展性,按照规则生成的编码资源应该满足业务的要求。还是拿淘宝订单编码为例,假设淘宝订单的 ID 生成规则是:随机产生 4 位有效的整数组成编码,那么最多可以生成 6561 个订单编码,这显然是无法满足淘宝系统需求的。所以在设计 ID 编码的时候,要充分考虑扩展的需要,比如编码规则能够生成足够多的 ID,从而满足业务的要求,或者能够通过不同的前缀区分不同的产品或业务线 。</p>
|
||||
|
||||
<h3>生成策略</h3>
|
||||
|
||||
<h4>UUID 方式</h4>
|
||||
|
||||
<p>介绍完 ID 编码在整个应用系统的重要作用和 ID 编码自身的特性后。接下来我们看看几种在日常开发中常见的生成策略。</p>
|
||||
|
||||
<p>开发人员,尤其是 Java 程序员最为熟悉的编码生成方式就是 UUID。它是一种包含 16 个字节的数字编码。 <strong>UUID 会根据运行应用的计算机网卡 MAC 地址、时间戳、命令空间等元素,通过一定的随机算法产生</strong>。正因为 UUID 算法元素的复杂性,保证了 UUID 在一定范围内的随机性。</p>
|
||||
|
||||
<p>UUID 在本地应用中生成,速度比较快,不依赖于其他服务,网络的好坏对其没有任何影响。但从实现上来讲,使用 UUID 策略生成的代码耦合度大,不能作为单独的 ID 生成器使用。而且生成的编码不能满足递增的特性,没有任何有序性可言,在很多业务场景中都不合适。</p>
|
||||
|
||||
<h4>数据库序列方式</h4>
|
||||
|
||||
<p>生成 ID 编码的另一种方式是<strong>数据库序列</strong>。比如 MySQL 的自增主键就是一种有序的 ID 生成方式。随着数据变得越来越多,为了提升数据库的性能,就要对数据库进行分库分表等操作。在这种情况下,自增主键的方式不能满足系统处理海量数据的要求。</p>
|
||||
|
||||
<p>这里我给你介绍另一种性能更好的数据库序列生成方式:<strong>TDDL 中的序列化实现</strong>。TDDL 是 Taobao Distributed Data Layer 的缩写。是淘宝根据自己的业务特点开发的数据库中间件。主要应用于数据库分库分表的应用场景中。</p>
|
||||
|
||||
<p>TDDL 生成 ID 编码的大致过程如下图所示。首先,作为 ID 生成器的机器,数据库中会存在一张sequence 序列化表,用于记录当前已经被占用的 ID 最大值。之后每个需要 ID 编码的客户端在请求 ID 编码生成器后,编码服务器会返回给该客户端一段 ID 地址区间。并更新 sequence 表中的信息。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8RWzqAT1UwAACcwktuN0M073.png" alt="2.png" /></p>
|
||||
|
||||
<p>在接收一段 ID 编码后,客户端会将该编码存储在内存中。在本机需要使用 ID 编码时,会首先使用内存中的 ID 编码。如果内存中的 ID 编码已经完全被占用,则再重新向编码服务器获取。</p>
|
||||
|
||||
<p>在 TDDL 框架的内部实现中,通过分批获取 ID 编码的方式,减少了客户端访问服务器的频率,避免了网络波动所造成的影响,并减轻了服务器的内存压力。不过 TDDL 是高度依赖底层数据库的实现方式,不能作为一个独立的分布式 ID 生成器对外提供服务。</p>
|
||||
|
||||
<h3>实现方式</h3>
|
||||
|
||||
<p>上面介绍的几种策略,有的和底层编码耦合比较大,有的又局限在某一具体的使用场景下,并不满足作为分布式环境下一个公共 ID 生成器的要求。接下来我们就利用目前学到的 ZooKeeper 知识,动手实现一个真正的分布式 ID 生成器。</p>
|
||||
|
||||
<p>首先,我们通过 ZooKeeper 自身的客户端和服务器运行模式,来实现一个分布式网络环境下的 ID 请求和分发过程。<strong>每个需要 ID 编码的业务服务器可以看作是 ZooKeeper 的客户端</strong>。ID 编码生成器可以作为 ZooKeeper 的服务端。客户端通过发送请求到 ZooKeeper 服务器,来获取编码信息,服务端接收到请求后,发送 ID 编码给客户端。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl8RTBGAB7QNAAAvwu3rspw007.png" alt="Drawing 2.png" /></p>
|
||||
|
||||
<p>在代码层面的实现中,如上图所示。我们可以利用 ZooKeeper 数据模型中的顺序节点作为 ID 编码。客户端通过调用 create 函数创建顺序节点。服务器成功创建节点后,会响应客户端请求,把创建好的节点信息发送给客户端。客户端用数据节点名称作为 ID 编码,进行之后的本地业务操作。</p>
|
||||
|
||||
<p>通过上面的介绍,我们发现,使用 ZooKeeper 实现一个分布式环境下的公用 ID 编码生成器很容易。利用 ZooKeeper 中的顺序节点特性,很容易使我们创建的 ID 编码具有有序的特性。并且我们也可以通过客户端传递节点的名称,根据不同的业务编码区分不同的业务系统,从而使编码的扩展能力更强。</p>
|
||||
|
||||
<p>虽然使用 ZooKeeper 的实现方式有这么多优点,但也会有一些潜在的问题。其中最主要的是,<strong>在定义编码的规则上还是强烈依赖于程序员自身的能力和对业务的深入理解</strong>。很容易出现因为考虑不周,造成设置的规则在运行一段时间后,无法满足业务要求或者安全性不够等问题。为了解决这个问题,我们继续学习一个比较常用的编码算法——snowflake 算法。</p>
|
||||
|
||||
<h4>snowflake 算法</h4>
|
||||
|
||||
<p>snowflake 算法是 Twitter 公司开源的一种用来生成分布式 ID 编码的算法。如下图所示,通过 snowflake 算法生成的编码是一个 64 位的长整型值。在 snowflake 算法中,是通过毫秒数、机器 ID</p>
|
||||
|
||||
<p>毫秒流水号、符号位这几个元素生成最终的编码。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8RW0aAMpWeAACkxZm34vQ563.png" alt="3.png" /></p>
|
||||
|
||||
<p>在计算编码的过程中,首先获取机器的毫秒数,并存储为 41 位,之后查询机器的工作 ID,存储在后面的 10 位字节中。剩余的 12 字节就用来存储毫秒内的流水号和表示位符号值 0。</p>
|
||||
|
||||
<p>从图中可以看出,<strong>snowflake 算法最主要的实现手段就是对二进制数位的操作</strong>。从性能上说,这个算法理论上每秒可以生成 400 多万个 ID 编码,完全满足分布式环境下,对系统高并发的要求。因此,在平时的开发过程中,也尽量使用诸如 snowflake 这种业界普遍采用的分布式 ID 生成算法,避免自己闭门造车导致的性能或安全风险。</p>
|
||||
|
||||
<h3>总结</h3>
|
||||
|
||||
<p>通过本课时的学习,我们掌握了什么是分布式的 ID 生成器,以及如何利用 ZooKeeper 实现一个 ID 生成器。ID 编码作为一个标识符,具有全局唯一的特性。利用这种特性,我们可以在分布式系统下实现很多功能,比如数据库的分区符号以及商城订单编号等。注意,在分布式等复杂的应用环境中,要设计一个编码生成器,我们需要考虑生成编码的<strong>唯一性、安全性、递增性以及扩展性。</strong></p>
|
||||
|
||||
<p>在本节课中,我们通过 ZooKeeper 实现了一个编码生成器,其主要原理是利用数据模型中顺序节点的特性。在具体的实现中也比较简单,并没有使用特定的算法实现 ID 编码。</p>
|
||||
|
||||
<p>这里给你留一个作业:结合上面介绍的 snowflake 算法,实现一个更加高效的编码服务器。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">下一页</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":"709975f528073cfa","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>
|
||||
|
||||
954
专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md.html
Normal file
@@ -0,0 +1,954 @@
|
||||
<!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>23 使用 ZooKeeper 实现负载均衡服务器功能.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>23 使用 ZooKeeper 实现负载均衡服务器功能</h1>
|
||||
|
||||
<p>今天我们利用 ZooKeeper 的相关知识,学习如何解决分布式环境下常见的业务场景与需求。这个课时主要通过 ZooKeeper 的相关特性,实现一个负载均衡服务器。在分布式架构和集群服务器架构下,负载均衡可以提高网络的性能和可靠性。</p>
|
||||
|
||||
<h3>什么是负载均衡</h3>
|
||||
|
||||
<p>负载均衡可以理解为运行在网络中的服务器或软件,其主要作用是<strong>扩展网络服务器的带宽、提高服务器处理数据的吞吐量,提高网络的可用性</strong>。比如我们经常用到的网络服务器、邮件服务器以及很多商业系统的服务器,都采用负载均衡的方式来协调工作。</p>
|
||||
|
||||
<p>这些系统一般会采用集群的方式进行部署,由于这些服务器彼此所处的网络环境各不相同,在某一段时间内所接收并处理的数据有多有少,如果整个集群没有一个专门进行管理和协调的角色,随着网络请求越来越多,就会出现某一台服务器比较忙,而网络中其他服务器没有什么任务要处理的情况。</p>
|
||||
|
||||
<p>负载均衡通过监控网络中各个服务器的运行情况,对整个集群的计算资源进行合理地分配和调整,避免由于请求处理的无序性导致的短板,从而限制整个集群性能。</p>
|
||||
|
||||
<p>了解了负载均衡服务器在集群服务中的作用后,接下来再来介绍一下实现负载均衡的常用算法。</p>
|
||||
|
||||
<h3>负载均衡算法</h3>
|
||||
|
||||
<p>在我们平时的工作和面试中,也常被问及一些负载均衡的算法问题。常用的有轮询法、随机法、原地址哈希法、加权轮询法、加权随机法、最小连接数法,下面我来分别为你进行讲解。</p>
|
||||
|
||||
<h4>轮询法</h4>
|
||||
|
||||
<p>轮询法是最为简单的负载均衡算法,当接收到来自网络中的客户端请求后,负载均衡服务器会按顺序逐个分配给后端服务。比如集群中有 3 台服务器,分别是 server1、server2、server3,轮询法会按照 sever1、server2、server3 这个顺序依次分发会话请求给每个服务器。当第一次轮询结束后,会重新开始下一轮的循环。</p>
|
||||
|
||||
<h4>随机法</h4>
|
||||
|
||||
<p>随机算法是指负载均衡服务器在接收到来自客户端的请求后,会根据一定的随机算法选中后台集群中的一台服务器来处理这次会话请求。不过,当集群中备选机器变的越来越多时,通过统计学我们可以知道每台机器被抽中的概率基本相等,因此随机算法的实际效果越来越趋近轮询算法。</p>
|
||||
|
||||
<h4>原地址哈希法</h4>
|
||||
|
||||
<p>原地址哈希算法的核心思想是根据客户端的 IP 地址进行哈希计算,用计算结果进行取模后,根据最终结果选择服务器地址列表中的一台机器,处理该条会话请求。采用这种算法后,当同一 IP 的客户端再次访问服务端后,负载均衡服务器最终选举的还是上次处理该台机器会话请求的服务器,<strong>也就是每次都会分配同一台服务器给客户端</strong>。</p>
|
||||
|
||||
<h4>加权轮询法</h4>
|
||||
|
||||
<p>在实际的生成环境中,一个分布式或集群系统中的机器可能部署在不同的网络环境中,每台机器的配置性能也有优劣之分。因此,它们处理和响应客户端请求的能力也各不相同。采用上面几种负载均衡算法,都不太合适,这会造成能力强的服务器在处理完业务后过早进入限制状态,而性能差或网络环境不好的服务器,一直忙于处理请求,造成任务积压。</p>
|
||||
|
||||
<p>为了解决这个问题,我们可以采用加权轮询法,加权轮询的方式与轮询算法的方式很相似,唯一的不同在于选择机器的时候,不只是单纯按照顺序的方式选择,<strong>还根据机器的配置和性能高低有所侧重</strong>,配置性能好的机器往往首先分配。</p>
|
||||
|
||||
<h4>加权随机法</h4>
|
||||
|
||||
<p>加权随机法和我们上面提到的随机算法一样,在采用随机算法选举服务器的时候,会考虑系统性能作为权值条件。</p>
|
||||
|
||||
<h4>最小连接数法</h4>
|
||||
|
||||
<p>最小连接数算法是指,根据后台处理客户端的连接会话条数,计算应该把新会话分配给哪一台服务器。一般认为,连接数越少的机器,在网络带宽和计算性能上都有很大优势,会作为最优先分配的对象。</p>
|
||||
|
||||
<h3>利用 ZooKeeper 实现</h3>
|
||||
|
||||
<p>介绍完负载均衡的常用算法后,接下来我们利用 ZooKeeper 来实现一个分布式系统下的负载均衡服务器。从上面介绍的几种负载均衡算法中不难看出。一个负载均衡服务器的底层实现,<strong>关键在于找到网络集群中最适合处理该条会话请求的机器,并将该条会话请求分配给该台机器</strong>。因此探测和发现后台服务器的运行状态变得最为关键。</p>
|
||||
|
||||
<h4>状态收集</h4>
|
||||
|
||||
<p>首先我们来实现网络中服务器运行状态的收集功能,利用 ZooKeeper 中的临时节点作为标记网络中服务器的状态点位。在网络中服务器上线运行的时候,通过在 ZooKeeper 服务器中创建临时节点,向 ZooKeeper 的服务列表进行注册,表示本台服务器已经上线可以正常工作。通过删除临时节点或者在与 ZooKeeper 服务器断开连接后,删除该临时节点。</p>
|
||||
|
||||
<p>最后,通过统计临时节点的数量,来了解网络中服务器的运行情况。<strong>如下图所示,建立的 ZooKeeper 数据模型中 Severs 节点可以作为存储服务器列表的父节点</strong>。用于之后通过负载均衡算法在该列表中选择服务器。在它下面创建 servers_host1、servers_host2、servers_host3等临时节点来存储集群中的服务器运行状态信息。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8X5l-APWIjAAAsDI_4m_Q833.png" alt="Drawing 0.png" /></p>
|
||||
|
||||
<p>在代码层面的实现中,我们首先定义一个 BlanceSever 接口类。该类规定在 ZooKeeper 服务器启动后,向服务器地址列表中,注册或注销信息以及根据接收到的会话请求,动态更新负载均衡情况等功能。如下面的代码所示:</p>
|
||||
|
||||
<pre><code>public class BlanceSever{
|
||||
|
||||
|
||||
|
||||
public void register()
|
||||
|
||||
|
||||
|
||||
public void unregister()
|
||||
|
||||
|
||||
|
||||
public void addBlanceCount()
|
||||
|
||||
|
||||
|
||||
public void takeBlanceCount()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>之后我们创建 BlanceSever 接口的实现类 BlanceSeverImpl,在 BlanceSeverImpl 类中首先定义服务器运行的 Session 超时时间、会话连接超时时间、ZooKeeper 客户端地址、服务器地址列表节点 ‘/Severs’ 等基本参数。并通过构造函数,在类被引用时进行初始化 ZooKeeper 客户端对象实例。</p>
|
||||
|
||||
<pre><code>public class BlanceSeverImpl implements BlanceSever{
|
||||
|
||||
|
||||
|
||||
private static final Integer SESSION_TIME_OUT
|
||||
|
||||
|
||||
|
||||
private static final Integer CONNECTION_TIME_OUT
|
||||
|
||||
|
||||
|
||||
private final ZkClient zkclient
|
||||
|
||||
|
||||
|
||||
private static final SERVER_PATH="/Severs"
|
||||
|
||||
|
||||
|
||||
public BlanceSeverImpl(){
|
||||
|
||||
|
||||
|
||||
init...
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>接下来,在定义当服务器启动时,向服务器地址列表注册信息的 register 函数。在函数的内部,通过在 SERVER_PATH 路径下创建临时子节点的方式来注册服务器信息。如下面的代码所示,首先获取服务器的 ip 地址,利用 ip 地址作为临时节点的 path 来创建临时节点。</p>
|
||||
|
||||
<pre><code>public register() throws Exception{
|
||||
|
||||
|
||||
|
||||
InetAddress address = InetAddress.getLocalHost();
|
||||
|
||||
|
||||
|
||||
String serverIp=address.getHostAddress()
|
||||
|
||||
|
||||
|
||||
zkclient.createEphemeral(SERVER_PATH+serverIp)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>register 函数在服务器启动并注册服务器信息后,我们再来定义 unregister 方法,该方法是当服务器关机或由于其他原因不再对外提供服务时,通过调用 unregister 方法,注销该台服务器在服务器列表中的信息。</p>
|
||||
|
||||
<p>注销后的机器不会被负载均衡服务器分发处理会话。如下面的代码所示,在 unregister 函数的内部,我们主要通过删除 SERVER_PATH 路径下临时节点的方式注销服务器。</p>
|
||||
|
||||
<pre><code>public unregister() throws Exception{
|
||||
|
||||
|
||||
|
||||
zkclient.delete(SERVER_PATH+serverIp)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4>负载算法</h4>
|
||||
|
||||
<p>实现服务器列表后,接下来我们就进入负载均衡最核心的内容:如何选择服务器。这里我们通过采用“最小连接数”算法,来确定究竟如何均衡地分配网络会话请求给后台客户端。</p>
|
||||
|
||||
<p>整个实现的过程如下图所示。首先,在接收到客户端的请求后,通过 getData 方法获取服务端 Severs 节点下的服务器列表,其中每个节点信息都存储有当前服务器的连接数。通过判断选择最少的连接数作为当前会话的处理服务器,并通过 setData 方法将该节点连接数加 1。最后,当客户端执行完毕,再调用 setData 方法将该节点信息减 1。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8X5n6AGCBQAABSbLOIuWA010.png" alt="Drawing 1.png" /></p>
|
||||
|
||||
<p>首先,我们定义当服务器接收到会话请求后。在 ZooKeeper 服务端增加连接数的 addBlance 方法。如下面的代码所示,首先我们通过 readData 方法获取服务器最新的连接数,之后将该连接数加 1,再通过 writeData 方法将新的连接数信息写入到服务端对应节点信息中。</p>
|
||||
|
||||
<pre><code>public void addBlance() throws Exception{
|
||||
|
||||
|
||||
|
||||
InetAddress address = InetAddress.getLocalHost();
|
||||
|
||||
|
||||
|
||||
String serverIp=address.getHostAddress()
|
||||
|
||||
|
||||
|
||||
Integer con_count=zkClient.readData(SERVER_PATH+serverIp)
|
||||
|
||||
|
||||
|
||||
++con_count
|
||||
|
||||
|
||||
|
||||
zkClient.writeData(SERVER_PATH+serverIp,con_count)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>当服务器处理完该会话请求后,需要更新服务端相关节点的连接数。具体的操作与 addBlance 方法基本一样,只是对获取的连接信息进行减一操作,这里不再赘述。</p>
|
||||
|
||||
<h3>结束</h3>
|
||||
|
||||
<p>本课时我们介绍了如何利用 ZooKeeper 实现一个负载均衡服务器,了解了随机、轮询、哈希等常用的负载均衡算法,并在本课的结尾利用 ZooKeeper 来创建一个负载均衡服务器的具体实现过程。</p>
|
||||
|
||||
<p>这里请你注意:我们日常用到的负载均衡器主要是选择后台处理的服务器,并给其分发请求。而通过 ZooKeeper 实现的服务器,只提供了服务器的筛选工作。在请求分发的过程中,还是通过负载算法计算出要访问的服务器,之后客户端自己连接该服务器,完成请求操作。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">下一页</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":"709975f78d723cfa","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>
|
||||
|
||||
@@ -0,0 +1,936 @@
|
||||
<!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>24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析</h1>
|
||||
|
||||
<p>在前面的课程中,我们学习了如何使用 ZooKeeper 实现分布式 ID 生成器,以及负载均衡的分布式环境下常用的解决方案。为了更进一步地提高用 ZooKeeper 解决问题的能力,我们再来分析一下在主流开源框架中如何使用 ZooKeeper。本节课主要选择业界最为流行的两个框架,一个是 RPC 框架 Dubbo,另一个是分布式发布订阅消息系统 Kafka。下面我们先来分析这两个框架都分别利用 ZooKeeper 解决了哪些问题。</p>
|
||||
|
||||
<h3>Dubbo 与 ZooKeeper</h3>
|
||||
|
||||
<h4>Dubbo 实现过程</h4>
|
||||
|
||||
<p>Dubbo 是阿里巴巴开发的一套开源的技术框架,<strong>是一款高性能、轻量级的开源 Java RPC 框架</strong>。它提供了三大核心能力:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>面向接口的远程方法调用</li>
|
||||
|
||||
<li>智能容错和负载均衡</li>
|
||||
|
||||
<li>服务自动注册和发现</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>其中,远程方法调用是 Dubbo 最为核心的功能点。因为一个分布式系统是由分布在不同网络区间或节点上的计算机或服务,通过彼此之间的信息传递进行协调工作的系统。因此<strong>跨机器或网络区间的通信是实现分布式系统的核心</strong>。而 Dubbo 框架可以让我们像调用本地方法一样,调用不同机器或网络服务上的线程方法。</p>
|
||||
|
||||
<p>下图展示了整个 Dubbo 服务的连通过程。整个服务的调用过程主要分为<strong>服务的消费端</strong>和<strong>服务的提供方</strong>。首先,服务的提供方向 Registry 注册中心注册所能提供的服务信息,接着服务的消费端会向 Registry 注册中心订阅该服务,注册中心再将服务提供者地址列表返回给消费者。如果有变更,注册中心将基于长连接将变更数据推送给消费者,从而通过服务的注册机制实现远程过程调用。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8ah0qASFC-AAELMLv7sPQ672.png" alt="1.png" /></p>
|
||||
|
||||
<h4>ZooKeeper 注册中心</h4>
|
||||
|
||||
<p>通过上面的介绍,我们不难发现在整个 Dubbo 框架的实现过程中,<strong>注册中心是其中最为关键的一点,它保证了整个 PRC 过程中服务对外的透明性</strong>。而 Dubbo 的注册中心也是通过 ZooKeeper 来实现的。</p>
|
||||
|
||||
<p>如下图所示,在整个 Dubbo 服务的启动过程中,服务提供者会在启动时向 /dubbo/com.foo.BarService/providers 目录写入自己的 URL 地址,这个操作可以看作是一个 ZooKeeper 客户端在 ZooKeeper 服务器的数据模型上创建一个数据节点。服务消费者在启动时订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址,并向 /dubbo/com.foo.BarService/consumers 目录写入自己的 URL 地址。该操作是通过 ZooKeeper 服务器在 /consumers 节点路径下创建一个子数据节点,然后再在请求会话中发起对 /providers 节点的 watch 监控。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8ah1WAW9KEAAIW9hNPw3Y360.png" alt="2.png" /></p>
|
||||
|
||||
<h3>Kafka 与 ZooKeeper</h3>
|
||||
|
||||
<p>接下来我们再看一下 ZooKeeper 在另一个开源框架 Kafka 中的应用。Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据,经常用来解决大量数据日志的实时收集以及 Web 网站上用户 PV 数统计和访问记录等。我们可以把 Kafka 看作是一个数据的高速公路,利用这条公路,数据可以低延迟、高效地从一个地点到达另一个地点。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl8ah16Ac117AAKJOqYuJ28381.png" alt="3.png" /></p>
|
||||
|
||||
<h4>Kafka 实现过程</h4>
|
||||
|
||||
<p>在介绍 ZooKeeper 在 Kafka 中如何使用之前,我们先来简单地了解一下 Kafka 的一些关键概念,以便之后的学习。如下图所示,整个 Kafka 的系统架构主要由 Broker、Topic、Partition、Producer、Consumer、Consumer Group 这几个核心概念组成,下面我们来分别进行介绍。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8ah26APMkMAAH5xDJ2qz0508.png" alt="4.png" /></p>
|
||||
|
||||
<p><strong>Broker</strong></p>
|
||||
|
||||
<p>Kafka 也是一个分布式的系统架构,因此在整个系统中存在多台机器,它将每台机器定义为一个 Broker。</p>
|
||||
|
||||
<p><strong>Topic</strong></p>
|
||||
|
||||
<p>Kafka 的主要功能是发送和接收消息,作为一个高效的消息管道,它存在于不同的系统中。Kafka 内部,将接收到的无论何种类型的消息统一定义为 Topic 类,可以将 Topic 看作是消息的容器。</p>
|
||||
|
||||
<p><strong>Partition</strong></p>
|
||||
|
||||
<p>Partition 是分区的意思,与 Topic 概念相似,它也是存放消息的容器。不过 Partition 主要是物理上的分区,而 Topic 表示消息的逻辑分区。</p>
|
||||
|
||||
<p><strong>Producer</strong></p>
|
||||
|
||||
<p>Producer 是消息的生产者,整个 Kafka 系统遵循的是生产者和消费者模式,消息会从生产者流通到接收者。</p>
|
||||
|
||||
<p><strong>Consumer 和 Consumer Group</strong></p>
|
||||
|
||||
<p>Consumer 即消费者,是 Kafka 框架内部对信息对接收方的定义。Consumer Group 会将消费者分组,然后按照不同的种类进行管理。</p>
|
||||
|
||||
<p>在整个 Kafka 服务的运行过程中,信息首先通过 producer 生产者提交给 Kafka 服务器上的 Topics 消息容器。在消息容器的内部,又会根据当前系统磁盘情况选择对应的物理分区进行存储,而每台服务分区可能对应一台或多台 Broker 服务器,之后 Broker 服务器再将信息推送给 Consumer。</p>
|
||||
|
||||
<h4>Zookeeper 的作用</h4>
|
||||
|
||||
<p>介绍完 Kafka 的相关概念和服务运行原理后,接下来我们学习 ZooKeeper 在 Kafka 框架下的应用。在 Kafka 中 ZooKeeper 几乎存在于每个方面,如下图所示,Kafka 会将我们上面介绍的流程架构存储为一个 ZooKeeper 上的数据模型。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl8ah3iASZ2-AAEGN0oprwQ428.png" alt="5.png" /></p>
|
||||
|
||||
<p>由于 Broker 服务器采用分布式集群的方式工作,那么在服务的运行过程中,难免出现某台机器因异常而关闭的状况。为了保证整个 Kafka 集群的可用性,需要在系统中监控整个机器的运行情况。而 Kafka 可以通过 ZooKeeper 中的数据节点,将网络中机器的运行统计存储在数据模型中的 brokers 节点下。</p>
|
||||
|
||||
<p>在 Kafka 的 Topic 信息注册中也需要使用到 ZooKeeper ,在 Kafka 中同一个Topic 消息容器可以分成多个不同片,而这些分区既可以存在于一台 Broker 服务器中,也可以存在于不同的 Broker 服务器中。</p>
|
||||
|
||||
<p>而在 Kafka 集群中,每台 Broker 服务器又相对独立。为了能够读取这些以分布式方式存储的分区信息,Kafka 会将这些分区信息在 Broker 服务器中的对应关系存储在 ZooKeeper 数据模型的 topic 节点上,每一个 topic 在 ZooKeeper 数据节点上都会以 /brokers/topics/[topic] 的形式存在。当 Broker 服务器启动的时候,会首先在 /brokers/topics 节点下创建自己的 Broker_id 节点,并将该服务器上的分区数量存储在该数据节点的信息中。之后 ,在系统运行的过程中,通过统计 /brokers/topics 下的节点信息,就能知道对应的 Broker 分区情况。</p>
|
||||
|
||||
<h3>整合 ZooKeeper 到自己的系统</h3>
|
||||
|
||||
<h4>在 Java 中使用 ZooKeeper</h4>
|
||||
|
||||
<p>通过上面的介绍,我们大致了解了比较流行的开源框架是如何利用 ZooKeeper 解决自身问题的。接下来我们学习如何在自己的项目中使用 ZooKeeper。这里我们以比较流行的 springboot 框架为例。如下面代码所示,要想在 SpringBoot 框架中使用 ZooKeeper,首先要在工程的 pom 文件中引入对应的包。</p>
|
||||
|
||||
<p>我们在 dependency 引用配置中添加了版本为 3.6.1 的 org.apache.zookeeper 开发包。通过这个配置,我们的工程就可以使用 ZooKeeper 的相关功能了。</p>
|
||||
|
||||
<pre><code><dependency>
|
||||
|
||||
|
||||
|
||||
<groupId>org.apache.zookeeper</groupId>
|
||||
|
||||
|
||||
|
||||
<artifactId>zookeeper</artifactId>
|
||||
|
||||
|
||||
|
||||
<version>3.6.1</version>
|
||||
|
||||
|
||||
|
||||
</dependency>
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>在项目开发 ZooKeeper 服务的时候,往往都会编写大量 ZooKeeper 客户端代码,去请求 ZooKeeper 服务端来完成相关的业务处理。而 ZooKeeper 自带的客户端使用起来不是很便利。之前我们学习过 Curator 框架,该框架被誉为 ZooKeeper 客户端中的瑞士军刀。利用该框架可以大大提高我们开发 ZooKeeper 服务的效率,因此,在项目开发中也推荐你来使用。</p>
|
||||
|
||||
<p>与上面介绍的相同,要想在项目中使用 Curator,首先需要将 Curator 引入到项目中,如下图所示,我们通过在 pom 文件中添加 dependency 来完成。首先,在配置中添加 4.0.0 版本的 org.apache.curator 包,其中包含了 Curator 的基础功能。之后添加的 curator-recipes 包,其中包括了重入锁、读写锁、Leader 选举设置等高级操作功能。把这三个包引用到工程后,整个 springboot 工程就可以利用 ZooKeeper 进行开发了。</p>
|
||||
|
||||
<pre><code> <dependency>
|
||||
|
||||
|
||||
|
||||
<groupId>org.apache.curator</groupId>
|
||||
|
||||
|
||||
|
||||
<artifactId>curator-framework</artifactId>
|
||||
|
||||
|
||||
|
||||
<version>4.0.0</version>
|
||||
|
||||
|
||||
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<dependency>
|
||||
|
||||
|
||||
|
||||
<dependency>
|
||||
|
||||
|
||||
|
||||
<groupId>org.apache.curator</groupId>
|
||||
|
||||
|
||||
|
||||
<artifactId>curator-recipes</artifactId>
|
||||
|
||||
|
||||
|
||||
<version>4.0.0</version>
|
||||
|
||||
|
||||
|
||||
</dependency>
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4>在 Python 中使用 ZooKeeper</h4>
|
||||
|
||||
<p>Python 作为一门跨平台开发语言,在数据科学、微服务、服务端开发中都有很广泛的应用。可能很多开发者是使用 Python 来进行业务开发的,下面我们介绍一下在 Python 项目中如何使用 ZooKeeper 服务。要想在 Python 中使用 ZooKeeper 服务,我们首先要在 Python 的运行环境中安装 kazoo 包。</p>
|
||||
|
||||
<pre><code>pip install kazoo
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4>连接 ZooKeeper 服务器</h4>
|
||||
|
||||
<p>安装完 kazoo 包后,我们可以着手在 Python 项目中使用 ZooKeeper 服务了。首先连接 ZooKeeper 服务器,如下面的代码所示,在代码中引入 KazooClient 包,它的作用和上面介绍的 Curator 一样,主要提供 ZooKeeper 客户端的操作功能。之后调用 KazooClient 函数并传入服务器地址来创建服务器连接会话,再调用 create 函数来创建数据节点。</p>
|
||||
|
||||
<pre><code>from kazoo.client import KazooClient
|
||||
|
||||
|
||||
|
||||
zk = KazooClient(hosts='127.0.0.1:2181')
|
||||
|
||||
|
||||
|
||||
zk.start()
|
||||
|
||||
|
||||
|
||||
zk.create("/pyzk/study/node", b"a value")
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>在日常开发工作中,无论是在 SpringBoot 框架下使用 Java 语言开发项目,还是使用 Pyhton 语言进行开发,使用 ZooKeeper 服务的方式基本一样,都是先要引入相关的 ZooKeeper 包,之后调用相关的客户端函数来完成业务相关的创建工作。</p>
|
||||
|
||||
<h3>总结</h3>
|
||||
|
||||
<p>本课时我们主要介绍了 ZooKeeper 在开源框架中的使用情况,其中,我们重点讲解了 Kafka 框架和 Dubbo 框架。Kafka 作为一个开源的分布式消息服务,会利用 ZooKeeper 实现对集群 Broker 服务器的运行情况统计等。而 Dubbo 则会利用 ZooKeeper 实现一个注册机制,以保证服务的透明性。</p>
|
||||
|
||||
<p>在实际的生产中 ZooKeeper 还能解决很多其他的问题,而这些问题本质上都是围绕分布式环境下一致性、可用性和分区容错性这三个分布式环境问题产生的。</p>
|
||||
|
||||
<p>这里给你留一个作业:试着使用 ZooKeeper 来解决在你在工作中遇到的问题,并尝试提升系统的安全性和稳定性。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">下一页</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":"709975f9da4e3cfa","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>
|
||||
|
||||
1050
专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html
Normal file
@@ -0,0 +1,860 @@
|
||||
<!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>27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式</h1>
|
||||
|
||||
<p>本节课,我们主要学习对线上 ZooKeeper 服务器日志进行维护的操作,主要维护方式是备份和清理。几乎所有的生产系统都会产生日志文件,用来记录服务的运行状态,在服务发生异常的时候,可以用来作为分析问题原因的依据。ZooKeeper 作为分布式系统下的重要组件,在分布式网络中会处理大量的客户端请求,因此也会产生大量的日志文件,对这些问题的维护关系到整个 ZooKeeper 服务的运行质量。接下来我们就来学习如何维护这些日志文件。</p>
|
||||
|
||||
<h3>日志类型</h3>
|
||||
|
||||
<p>首先,我们先来介绍线上生产环境中的 ZooKeeper 集群在对外提供服务的过程中,都会产生哪些日志类型。我们在之前的课程中也介绍过了,在 ZooKeeper 服务运行的时候,一般会产生数据快照和日志文件,数据快照用于集群服务中的数据同步,而数据日志则记录了 ZooKeeper 服务运行的相关状态信息。其中,数据日志是我们在生产环境中需要定期维护和管理的文件。</p>
|
||||
|
||||
<h3>清理方案</h3>
|
||||
|
||||
<p>如上面所介绍的,面对生产系统中产生的日志,一般的维护操作是备份和清理。备份是为了之后对系统的运行情况进行排查和优化,而清理主要因为随着系统日志的增加,日志会逐渐占用系统的存储空间,如果一直不进行清理,可能耗尽系统的磁盘存储空间,并最终影响服务的运行。但在实际工作中,我们不能 24 小时监控系统日志情况,因此这里我们介绍一种定时任务,可以自动清理和备份 ZooKeeper 服务运行产生的相关日志。</p>
|
||||
|
||||
<h3>清理工具</h3>
|
||||
|
||||
<h4>corntab</h4>
|
||||
|
||||
<p>首先,我们介绍的是 Linux corntab ,它是 Linux 系统下的软件,可以自动地按照我们设定的时间,周期性地执行我们编写的相关脚本。下面我们就用它来写一个定时任务,实现每周定期清理 ZooKeeper 服务日志。</p>
|
||||
|
||||
<h4>创建脚本</h4>
|
||||
|
||||
<p>我们通过 Linux 系统下的 Vim 文本编辑器,来创建一个叫作 “ logsCleanWeek ” 的定时脚本,该脚本是一个 shell 格式的可执行文件。如下面的代码所示,我们在 usr/bin/ 文件夹下创建该文件,该脚本的主要内容是设定 ZooKeeper 快照和数据日志的对应文件夹路径,并通过 shell 脚本和管道和 find 命令 查询对应的日志下的日志文件,这里我们保留最新的 10 条数据日志,其余的全部清理。</p>
|
||||
|
||||
<pre><code>#!/bin/bash
|
||||
|
||||
|
||||
|
||||
dataDir=/home/zk/zk_data/version-2
|
||||
|
||||
|
||||
|
||||
dataLogDir=/home/zk/zk_log/version-2
|
||||
|
||||
|
||||
|
||||
ls -t $dataLogDir/log.* | tail -n +$count | xargs rm -f
|
||||
|
||||
|
||||
|
||||
ls -t $dataDir/snapshot.* | tail -n +$count | xargs rm -f
|
||||
|
||||
|
||||
|
||||
ls -t $logDir/zookeeper.log.* | tail -n +$count | xargs rm -f
|
||||
|
||||
|
||||
|
||||
find /home/home/zk/zk_data/version-2 -name "snap*" -mtime +1 | xargs rm -f
|
||||
|
||||
|
||||
|
||||
find /home/home/zk/zk_data/version-2 -name "snap*" -mtime +1 | xargs rm -f
|
||||
|
||||
|
||||
|
||||
find /home/home/zk/zk_data/logs/ -name "zookeeper.log.*" -mtime +1 | xargs rm –f
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4>创建定时任务</h4>
|
||||
|
||||
<p>创建完定时脚本后,我们接下来就利用 corntab 来设置脚本的启动时间,如下面的代码所示。corntab 命令的语法比较简单,其中 -u 表示设定指定的用户,因为 Linux 系统是一个多用户操作系统,而 crontab 的本质就是根据使用系统的用户来设定程序执行的时间计划表。因此当命令的执行者具有管理员 root 账号的权限时,可以通过 -u 为特定用户设定某一个程序的具体执行时间。</p>
|
||||
|
||||
<pre><code>crontab [ -u user ] { -l | -r | -e }
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>接下来我们打开系统的控制台,并输入 crontab -e 命令,开启定时任务的编辑功能。如下图所示,系统会显示出当前已有的定时任务列表。整个 crontab 界面的操作逻辑和 Vim 相同,为了新建一个定时任务,我们首先将光标移动到文件的最后一行,并敲击 i 键来开启编辑模式。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl8qlt2ALC7CAABlifm7LHs902.png" alt="Drawing 0.png" /></p>
|
||||
|
||||
<p>这个 crontab 定时脚本由两部分组成,第一部分是定时时间,第二部分是要执行的脚本。如下代码所示,脚本的执行时间是按照 f1 分、 f2 小时、f3 日、f4 月、f5 一个星期中的第几天这种固定顺序格式编写的。</p>
|
||||
|
||||
<pre><code>f1 f2 f3 f4 f5 program
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>当对应的时间位上为 * 时,表示每间隔一段时间都要执行。例如,当 f1 分上设定的是 * 时,表示每分钟都要执行对应的脚本。而如果我们想在每天的特定时间执行对应的脚本,则可以通过在对应的时间位置设定一个时间段实现,以下代码所演示的就是将脚本清理时间设定为每天早上的 6 点到 8 点。</p>
|
||||
|
||||
<pre><code>0 6-8 * * * /usr/bin/logsCleanWeek.sh>/dev/null 2>&1
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4>查看定时任务</h4>
|
||||
|
||||
<p>当我们设定完定时任务后,就可以打开控制台,并输入 crontab -l 命令查询系统当前的定时任务。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl8qlu-AW-xZAAA50ErYH4s391.png" alt="Drawing 1.png" /></p>
|
||||
|
||||
<p>到目前为止我们就完成了用 crontab 创建定时任务来自动清理和维护 ZooKeeper 服务产生的相关日志和数据的过程。</p>
|
||||
|
||||
<p>crontab 定时脚本的方式相对灵活,可以按照我们的业务需求来设置处理日志的维护方式,比如这里我们希望定期清除 ZooKeeper 服务运行的日志,而不想清除数据快照的文件,则可以通过脚本设置,达到只对数据日志文件进行清理的目的。</p>
|
||||
|
||||
<h3>PurgeTxnLog</h3>
|
||||
|
||||
<p>除了上面所介绍的,通过编写 crontab 脚本定时清理 ZooKeeper 服务的相关日志外, ZooKeeper 自身还提供了 PurgeTxnLog 工具类,用来清理 snapshot 数据快照文件和系统日志。</p>
|
||||
|
||||
<p>PurgeTxnLog 清理方式和我们上面介绍的方式十分相似,也是通过定时脚本执行任务,唯一的不同是,上面提到在编写日志清除 logsCleanWeek 的时候 ,我们使用的是原生 shell 脚本自己手动编写的数据日志清理逻辑,而使用 PurgeTxnLog 则可以在编写清除脚本的时候调用 ZooKeeper 为我们提供的工具类完成日志清理工作。</p>
|
||||
|
||||
<p>如下面的代码所示,首先,我们在 /usr/bin 目录下创建一个 PurgeLogsClean 脚本。注意这里的脚本也是一个 shell 文件。在脚本中我们只需要编写 PurgeTxnLog 类的调用程序,系统就会自动通过 PurgeTxnLog 工具类为我们完成对应日志文件的清理工作。</p>
|
||||
|
||||
<pre><code>#!/bin/sh
|
||||
|
||||
|
||||
|
||||
java -cp "$CLASSPATH" org.apache.zookeeper.server.PurgeTxnLog
|
||||
|
||||
|
||||
|
||||
echo "清理完成"
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>PurgeTxnLog 方式与 crontab 相比,使用起来更加容易而且也更加稳定安全,不过 crontab 方式更加灵活,我们可以根据不同的业务需求编写自己的清理逻辑。</p>
|
||||
|
||||
<h3>结束</h3>
|
||||
|
||||
<p>本节课我们介绍了线上 ZooKeeper 服务日志和数据快照的清理和维护工作,可以通过 crontab 和 PurgeTxnLog 两种方式实现。这两种方式唯一的不同在清理日志脚本的实现方式上,crontab 是通过我们自己手动编写的 shell 脚本实现的,在执行上需要考虑脚本权限相关的问题,而 PurgeTxnLog 则是 ZooKeeper 提供的专门用来处理日志清除相关的工具类,使用起来更加容易,开发人员不用考虑底层的实现细节。这里希望你结合自身工作中的生产环境来选择一种适合自己的 ZooKeeper 数据维护方式。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">下一页</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":"70997600a8ea3cfa","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>
|
||||
|
||||
806
专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md.html
Normal file
@@ -0,0 +1,806 @@
|
||||
<!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>28 彻底掌握二阶段提交三阶段提交算法原理.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>28 彻底掌握二阶段提交三阶段提交算法原理</h1>
|
||||
|
||||
<p>在本节课的开篇中,我们已经提到过 ZooKeeper 在分布式系统环境中主要解决的是分布式一致性问题。而为什么会发生数据不一致的问题呢?是因为当网络集群处理来自客户端的请求时,其中的事务性会导致服务器上数据状态的变更。</p>
|
||||
|
||||
<p>为了保证数据变更请求在整个分布式环境下正确地执行,不会发生异常中断,从而导致请求在某一台服务器执行失败而在集群中其他服务器上执行成功,在整个分布式系统处理数据变更请求的过程中,引入了分布式事务的概念。</p>
|
||||
|
||||
<h3>分布式事务</h3>
|
||||
|
||||
<p>对于事务操作我们并不陌生,最为熟悉的就是数据库事务操作。当多个线程对数据库中的同一个信息进行修改的时候,为保证数据的原子性、一致性、隔离性、持久性,需要进行本地事务性操作。而在分布式的网络环境下,也会面临多个客户端的数据请求服务。在处理数据变更的时候,需要保证在分布式环境下的数据的正确完整,因此在分布式环境下也引入了分布式事务。</p>
|
||||
|
||||
<h3>二阶段提交</h3>
|
||||
|
||||
<p>二阶段提交(Two-phase Commit)简称 2PC ,它是一种实现分布式事务的算法。二阶段提交算法可以保证分布在不同网络节点上的程序或服务按照事务性的方式进行调用。</p>
|
||||
|
||||
<h4>底层实现</h4>
|
||||
|
||||
<p>正如算法的名字一样,二阶段提交的底层实现主要分成两个阶段,分别是<strong>询问阶段</strong>和<strong>提交阶段</strong>。具体过程如下图所示:</p>
|
||||
|
||||
<p>整个集群服务器被分成一台协调服务器,集群中的其他服务器是被协调的服务器。在二阶段算法的询问阶段,分布式集群服务在接收到来自客户端的请求的时候,首先会通过协调者服务器,针对本次请求能否正常执行向集群中参与处理的服务器发起询问请求。集群服务器在接收到请求的时候,会在本地机器上执行会话操作,并记录执行的相关日志信息,最后将结果返回给协调服务器。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8tFY-AdQ5KAACfHFJeUlA350.png" alt="image.png" /></p>
|
||||
|
||||
<p>在协调服务器接收到来自集群中其他服务器的反馈信息后,会对信息进行统计。如果集群中的全部机器都能正确执行客户端发送的会话请求,那么协调者服务器就会再次向这些服务器发送提交命令。在集群服务器接收到协调服务器的提交指令后,会根据之前处理该条会话操作的日志记录在本地提交操作,并最终完成数据的修改。</p>
|
||||
|
||||
<p>虽然二阶段提交可以有效地保证客户端会话在分布式集群中的事务性,但是<strong>该算法自身也有很多问题</strong>,主要可以归纳为以下几点:效率问题、单点故障、异常中断。</p>
|
||||
|
||||
<h4>性能问题</h4>
|
||||
|
||||
<p>首先,我们先来介绍一下性能问题。如我们上面介绍的二阶段算法,在数据提交的过程中,所有参与处理的服务器都处于阻塞状态,如果其他线程想访问临界区的资源,需要等待该条会话请求在本地执行完成后释放临界区资源。因此,采用二阶段提交算法也会降低程序并发执行的效率。</p>
|
||||
|
||||
<h4>单点问题</h4>
|
||||
|
||||
<p>此外,还会发生单点问题。单点问题也叫作单点服务器故障问题,它指的是当作为分布式集群系统的调度服务器发生故障时,整个集群因为缺少协调者而无法进行二阶段提交算法。单点问题也是二阶段提交最大的缺点,因此使用二阶段提交算法的时候通常都会进行一些改良,以满足对系统稳定性的要求。</p>
|
||||
|
||||
<h4>异常中断</h4>
|
||||
|
||||
<p>异常中断问题指的是当统计集群中的服务器可以进行事务操作时,协调服务器会向这些处理事务操作的服务器发送 commit 提交请求。如果在这个过程中,其中的一台或几台服务器发生网络故障,无法接收到来自协调服务器的提交请求,导致这些服务器无法完成最终的数据变更,就会造成整个分布式集群出现数据不一致的情况。</p>
|
||||
|
||||
<p>由于以上种种问题,在实际操作中,我更推荐使用另一种分布式事务的算法——三阶段提交算法。</p>
|
||||
|
||||
<h3>三阶段提交</h3>
|
||||
|
||||
<p>三阶段提交(Three-phase commit)简称 3PC , 其实是在二阶段算法的基础上进行了优化和改进。如下图所示,在整个三阶段提交的过程中,相比二阶段提交,<strong>增加了预提交阶段</strong>。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8tFZuAZgJHAADHKaM9oZI445.png" alt="image" /></p>
|
||||
|
||||
<h4>底层实现</h4>
|
||||
|
||||
<p><strong>预提交阶段</strong></p>
|
||||
|
||||
<p>为了保证事务性操作的稳定性,同时避免二阶段提交中因为网络原因造成数据不一致等问题,完成提交准备阶段后,集群中的服务器已经为请求操作做好了准备,协调服务器会向参与的服务器发送预提交请求。集群服务器在接收到预提交请求后,在本地执行事务操作,并将执行结果存储到本地事务日志中,并对该条事务日志进行锁定处理。</p>
|
||||
|
||||
<p><strong>提交阶段</strong></p>
|
||||
|
||||
<p>在处理完预提交阶段后,集群服务器会返回执行结果到协调服务器,最终,协调服务器会根据返回的结果来判断是否继续执行操作。如果所有参与者服务器返回的都是可以执行事务操作,协调者服务器就会再次发送提交请求到参与者服务器。参与者服务器在接收到来自协调者服务器的提交请求后,在本地正式提交该条事务操作,并在完成事务操作后关闭该条会话处理线程、释放系统资源。当参与者服务器执行完相关的操作时,会再次向协调服务器发送执行结果信息。</p>
|
||||
|
||||
<p>协调者服务器在接收到返回的状态信息后会进行处理,如果全部参与者服务器都正确执行,并返回 yes 等状态信息,整个事务性会话请求在服务端的操作就结束了。如果在接收到的信息中,有参与者服务器没有正确执行,则协调者服务器会再次向参与者服务器发送 rollback 回滚事务操作请求,整个集群就退回到之前的状态,这样就避免了数据不一致的问题。</p>
|
||||
|
||||
<h3>结束</h3>
|
||||
|
||||
<p>本节课我们主要学习了分布式系统下的分布式事务问题。由于分布式系统架构的特点,组成整个系统的网络服务可能分布在不同的网络节点或服务器上,因此在调用这些网络服务的过程中,会面临网络异常中断等不确定的问题,最终导致集群中出现数据不一致的情况。</p>
|
||||
|
||||
<p>为了保证数据的有一致性,我们引入了二阶段提交和三阶段提交算法。这两种算法都会将整个事务处理过程分成<strong>准备、执行、确认</strong>提交这几个阶段。不同的是,二阶段提交会因为网络原因造成数据不一致的问题,而三阶段提交通过增加预加载阶段将执行的事务数据保存到本地,当整个网络中的参与者服务器都能进行事务操作后,协调服务器会发送最终提交请求给参与者服务器,并最终完成事务操作的数据的修改。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">下一页</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":"709976030e8a3cfa","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>
|
||||
|
||||
814
专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md.html
Normal file
@@ -0,0 +1,814 @@
|
||||
<!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>29 ZAB 协议算法:崩溃恢复和消息广播.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>29 ZAB 协议算法:崩溃恢复和消息广播</h1>
|
||||
|
||||
<p>在之前的课程中我们曾谈到当 Leader 节点发生崩溃的时候,在 ZooKeeper 集群中会重新选举出新的 Leader 节点服务器,以保证 ZooKeeper 集群的可用性。那么从 Leader 节点发生崩溃到重新恢复中间经历了哪些过程,又是采用什么算法恢复集群服务的?带着这些问题我们来学习本节课的内容。</p>
|
||||
|
||||
<h3>ZAB 协议算法</h3>
|
||||
|
||||
<p>ZooKeeper 最核心的作用就是保证分布式系统的数据一致性,而无论是处理来自客户端的会话请求时,还是集群 Leader 节点发生重新选举时,都会产生数据不一致的情况。为了解决这个问题,ZooKeeper 采用了 ZAB 协议算法。</p>
|
||||
|
||||
<p><strong>ZAB 协议算法</strong>(Zookeeper Atomic Broadcast ,Zookeeper 原子广播协议)是 ZooKeeper 专门设计用来解决集群最终一致性问题的算法,它的两个核心功能点是<strong>崩溃恢复</strong>和<strong>原子广播协议</strong>。</p>
|
||||
|
||||
<p>在整个 ZAB 协议的底层实现中,ZooKeeper 集群主要采用<strong>主从模式</strong>的系统架构方式来保证 ZooKeeper 集群系统的一致性。整个实现过程如下图所示,当接收到来自客户端的事务性会话请求后,系统集群采用主服务器来处理该条会话请求,经过主服务器处理的结果会通过网络发送给集群中其他从节点服务器进行数据同步操作。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8zqzWAY5J1AAAlia-QXZo498.png" alt="Drawing 0.png" /></p>
|
||||
|
||||
<p>以 ZooKeeper 集群为例,这个操作过程可以概括为:当 ZooKeeper 集群接收到来自客户端的事务性的会话请求后,集群中的其他 Follow 角色服务器会将该请求转发给 Leader 角色服务器进行处理。当 Leader 节点服务器在处理完该条会话请求后,会将结果通过操作日志的方式同步给集群中的 Follow 角色服务器。然后 Follow 角色服务器根据接收到的操作日志,在本地执行相关的数据处理操作,最终完成整个 ZooKeeper 集群对客户端会话的处理工作。</p>
|
||||
|
||||
<h4>崩溃恢复</h4>
|
||||
|
||||
<p>在介绍完 ZAB 协议在架构层面的实现逻辑后,我们不难看出整个 ZooKeeper 集群处理客户端会话的核心点<strong>在一台 Leader 服务器上</strong>。所有的业务处理和数据同步操作都要靠 Leader 服务器完成。结合我们在“ 28 | 彻底掌握二阶段提交/三阶段提交算法原理” 中学习到的二阶段提交知识,会发现就目前介绍的 ZooKeeper 架构方式而言,<strong>极易产生单点问题</strong>,即当集群中的 Leader 发生故障的时候,整个集群就会因为缺少 Leader 服务器而无法处理来自客户端的事务性的会话请求。因此,为了解决这个问题。在 ZAB 协议中也设置了处理该问题的崩溃恢复机制。</p>
|
||||
|
||||
<p>崩溃恢复机制是保证 ZooKeeper 集群服务高可用的关键。触发 ZooKeeper 集群执行崩溃恢复的事件是集群中的 Leader 节点服务器发生了异常而无法工作,于是 Follow 服务器会通过投票来决定是否选出新的 Leader 节点服务器。</p>
|
||||
|
||||
<p><strong>投票过程如下</strong>:当崩溃恢复机制开始的时候,整个 ZooKeeper 集群的每台 Follow 服务器会发起投票,并同步给集群中的其他 Follow 服务器。在接收到来自集群中的其他 Follow 服务器的投票信息后,集群中的每个 Follow 服务器都会与自身的投票信息进行对比,如果判断新的投票信息更合适,则采用新的投票信息作为自己的投票信息。在集群中的投票信息还没有达到超过半数原则的情况下,再进行新一轮的投票,最终当整个 ZooKeeper 集群中的 Follow 服务器超过半数投出的结果相同的时候,就会产生新的 Leader 服务器。</p>
|
||||
|
||||
<h4>选票结构</h4>
|
||||
|
||||
<p>介绍完整个选举 Leader 节点的过程后,我们来看一下整个投票阶段中的投票信息具有怎样的结构。以 Fast Leader Election 选举的实现方式来讲,如下图所示,一个选票的整体结果可以分为一下六个部分:</p>
|
||||
|
||||
<p><img src="assets/CgqCHl8zq1SABNDvAAB3WcgZ-2s055.png" alt="Drawing 1.png" /></p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><strong>logicClock</strong>:用来记录服务器的投票轮次。logicClock 会从 1 开始计数,每当该台服务经过一轮投票后,logicClock 的数值就会加 1 。</li>
|
||||
|
||||
<li><strong>state</strong>:用来标记当前服务器的状态。在 ZooKeeper 集群中一台服务器具有 LOOKING、FOLLOWING、LEADERING、OBSERVING 这四种状态。</li>
|
||||
|
||||
<li><strong>self_id</strong>:用来表示当前服务器的 ID 信息,该字段在 ZooKeeper 集群中主要用来作为服务器的身份标识符。</li>
|
||||
|
||||
<li><strong>self_zxid</strong>: 当前服务器上所保存的数据的最大事务 ID ,从 0 开始计数。</li>
|
||||
|
||||
<li><strong>vote_id</strong>:投票要被推举的服务器的唯一 ID 。</li>
|
||||
|
||||
<li><strong>vote_zxid</strong>:被推举的服务器上所保存的数据的最大事务 ID ,从 0 开始计数。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>当 ZooKeeper 集群需要重新选举出新的 Leader 服务器的时候,就会根据上面介绍的投票信息内容进行对比,以找出最适合的服务器。</p>
|
||||
|
||||
<h4>选票筛选</h4>
|
||||
|
||||
<p>接下来我们再来看一下,当一台 Follow 服务器接收到网络中的其他 Follow 服务器的投票信息后,是如何进行对比来更新自己的投票信息的。Follow 服务器进行选票对比的过程,如下图所示。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8zq2CAABxRAACKTM36L6o309.png" alt="2.png" /></p>
|
||||
|
||||
<p>首先,会对比 logicClock 服务器的投票轮次,当 logicClock 相同时,表明两张选票处于相同的投票阶段,并进入下一阶段,否则跳过。接下来再对比 vote_zxid 被选举的服务器 ID 信息,若接收到的外部投票信息中的 vote_zxid 字段较大,则将自己的票中的 vote_zxid 与 vote_myid 更新为收到的票中的 vote_zxid 与 vote_myid ,并广播出去。要是对比的结果相同,则继续对比 vote_myid 被选举服务器上所保存的最大事务 ID ,若外部投票的 vote_myid 比较大,则将自己的票中的 vote_myid 更新为收到的票中的 vote_myid 。 经过这些对比和替换后,最终该台 Follow 服务器会产生新的投票信息,并在下一轮的投票中发送到 ZooKeeper 集群中。</p>
|
||||
|
||||
<h4>消息广播</h4>
|
||||
|
||||
<p>在 Leader 节点服务器处理请求后,需要通知集群中的其他角色服务器进行数据同步。ZooKeeper 集群采用消息广播的方式发送通知。</p>
|
||||
|
||||
<p>ZooKeeper 集群使用原子广播协议进行消息发送,该协议的底层实现过程与我们在“ 28 | 彻底掌握二阶段提交/三阶段提交算法原理” 的二阶段提交过程非常相似,如下图所示。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8zq2qAXTCIAAB2wqjPhfI084.png" alt="Drawing 3.png" /></p>
|
||||
|
||||
<p>当要在集群中的其他角色服务器进行数据同步的时候,Leader 服务器将该操作过程封装成一个 Proposal 提交事务,并将其发送给集群中其他需要进行数据同步的服务器。当这些服务器接收到 Leader 服务器的数据同步事务后,会将该条事务能否在本地正常执行的结果反馈给 Leader 服务器,Leader 服务器在接收到其他 Follow 服务器的反馈信息后进行统计,判断是否在集群中执行本次事务操作。</p>
|
||||
|
||||
<p>这里请大家注意 ,与我们“ 28 | 彻底掌握二阶段提交/三阶段提交算法原理” 中提到的二阶段提交过程不同(即需要集群中所有服务器都反馈可以执行事务操作后,主服务器再次发送 commit 提交请求执行数据变更) ,ZAB 协议算法省去了中断的逻辑,当 ZooKeeper 集群中有超过一般的 Follow 服务器能够正常执行事务操作后,整个 ZooKeeper 集群就可以提交 Proposal 事务了。</p>
|
||||
|
||||
<h3>总结</h3>
|
||||
|
||||
<p>本节课我们主要介绍了 ZooKeeper 中的 ZAB 协议算法。 ZAB 协议算法能够保证 ZooKeeper 集群服务在处理事务性请求后的数据一致性 ,当集群中的 Leader 服务器发生崩溃的时候,ZAB 协议算法可以在 ZooKeeper 集群中重新选举 Leader 并进行数据的同步恢复。其中值得注意的是消息广播的底层实现过程虽然与二阶段提交非常相似,但是与二阶段提交相比,并没有事务丢弃的过程。在 ZooKeeper 集群的消息广播中,只要满足整个集群中超过半数的 Follow 服务器可以执行本次事务操作,Leader 就可以向集群中发送提交事务操作,最终完成数据的变更。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">下一页</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":"709976055bfa3cfa","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>
|
||||
|
||||
804
专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md.html
Normal file
@@ -0,0 +1,804 @@
|
||||
<!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>30 ZAB 与 Paxos 算法的联系与区别.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>30 ZAB 与 Paxos 算法的联系与区别</h1>
|
||||
|
||||
<p>在之前的课程中,我们一直围绕 ZooKeeper 的一致性协议算法 ZAB 协议算法来研究其底层实现原理,而为了能够更加全面地掌握分布式一致性的解决方法,在掌握 ZAB 协议的情况下,我们再进一步学习另一种算法: Paxos 算法。我们会通过研究 Paxos 算法的实现原理,来分析它与 ZAB 协议有什么不同,及它们各自的优缺点。</p>
|
||||
|
||||
<h3>Paxos 算法</h3>
|
||||
|
||||
<p>在分布式一致性问题的解决方案中,Paxos 算法可以说是<strong>目前最为优秀</strong>的。很多方案,包括我们学习的 ZooKeeper 的 ZAB 协议算法都是在其基础上改进和演变过来的。</p>
|
||||
|
||||
<p>Paxos 算法是基于消息传递的分布式一致性算法,很多大型的网络技术公司和开源框架都采用 Paxos 算法作为其各自的底层解决方案,比如 Chubby 、 Megastore 以及 MySQL Group Replication 。 Paxos 算法运行在服务器发生宕机故障的时候,能够保证数据的完整性,不要求可靠的消息传递,可容忍消息丢失、延迟、乱序以及重复,保证服务的高可用性。</p>
|
||||
|
||||
<h3>底层实现</h3>
|
||||
|
||||
<p>介绍完 Paxos 算法能够解决哪些问题后,接下来我们继续学习 Paxos 算法的底层实现过程。保证分布式系统下数据的一致性操作,本质是协调运行在不同的网络服务器上的线程服务,使这些服务就某一个特定的数据执行一致性的变更操作。在整个 Paxos 算法的实现过程中,将参与算法的集群中的全部服务器,分成三种角色:提议者(Proposer)、决策者(Acceptor)、决策学习者(Learner)。</p>
|
||||
|
||||
<h4>三种角色</h4>
|
||||
|
||||
<p>先来看看三种角色的具体分工。</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><strong>提议者(Proposer)</strong>:提出提案(Proposal)。Proposal 信息包括提案编号(Proposal ID)和提议的值(Value)。</li>
|
||||
|
||||
<li><strong>决策者(Acceptor)</strong>:参与决策,回应 Proposers 的提案。收到 Proposal 后可以接受提案,若 Proposal 获得超过半数 Acceptors 的许可,则称该 Proposal 被批准。</li>
|
||||
|
||||
<li><strong>决策学习者</strong>:不参与决策,从 Proposers/Acceptors 学习最新达成一致的提案(Value)。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>经过我们之前对 ZooKeeper 的学习,相信对 Paxos 算法的集群角色划分并不陌生。而与 ZAB 协议算法<strong>不同的是</strong>,在 Paxos 算法中,当处理来自客户端的事务性会话请求的过程时,首先会触发一个或多个服务器进程,就本次会话的处理发起提案。当该提案通过网络发送到集群中的其他角色服务器后,这些服务器会就该会话在本地的执行情况反馈给发起提案的服务器。发起提案的服务器会在接收到这些反馈信息后进行统计,当集群中超过半数的服务器认可该条事务性的客户端会话操作后,认为该客户端会话可以在本地执行操作。</p>
|
||||
|
||||
<p>上面介绍的 Paxos 算法针对事务性会话的处理投票过程与 ZAB 协议十分相似,但不同的是,对于采用 ZAB 协议的 ZooKeeper 集群中发起投票的机器,所采用的是在集群中运行的一台 Leader 角色服务器。而 Paxos 算法则采用多副本的处理方式,即存在多个副本,每个副本分别包含提案者、决策者以及学习者。下图演示了三种角色的服务器之间的关系。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F82RbqAHPmCAAFicrAh8y0060.png" alt="11.png" /></p>
|
||||
|
||||
<h4>事务处理过程</h4>
|
||||
|
||||
<p>介绍完 Paxos 算法中的服务器角色和投票的处理过程后,接下来我们再来看一下 Paxos 针对一次提案是如何处理的。如下图所示,整个提案的处理过程可以分为三个阶段,分别是提案准备阶段、事务处理阶段、数据同步阶段。我们分别介绍一下这三个阶段的底层处理逻辑。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F82Q0yAQd1UAAB9Sb1EaFg098.png" alt="Drawing 1.png" /></p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><strong>提案准备阶段</strong>:该阶段是整个 Paxos 算法的最初阶段,所有接收到的来自客户端的事务性会话在执行之前,整个集群中的 Proposer 角色服务器或者节点,需要将会话发送给 Acceptor 决策者服务器。在 Acceptor 服务器接收到该条询问信息后,需要返回 Promise ,承诺可以执行操作信息给 Proposer 角色服务器。</li>
|
||||
|
||||
<li><strong>事务处理阶段</strong>:在经过提案准备阶段,确认该条事务性的会话操作可以在集群中正常执行后,Proposer 提案服务器会再次向 Acceptor 决策者服务器发送 propose 提交请求。Acceptor 决策者服务器在接收到该 propose 请求后,在本地执行该条事务性的会话操作。</li>
|
||||
|
||||
<li><strong>数据同步阶段</strong>:在完成了事务处理阶段的操作后,整个集群中对该条事务性会话的数据变更已经在 Acceptor 决策者服务器上执行完成,当整个集群中有超过半数的 Acceptor 决策者服务器都成功执行后,Paxos 算法将针对本次执行结果形成一个决议,并发送给 Learner 服务器。当 Learner 服务器接收到该条决议信息后,会同步 Acceptor 决策者服务器上的数据信息,最终完成该条事务性会话在整个集群中的处理。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h3>Paxos PK ZAB</h3>
|
||||
|
||||
<p>经过上面的介绍我们对 Paxos 算法所能解决的问题,以及底层的实现原理都有了一个详细的了解。现在结合我们之前学习的 ZooKeeper 相关知识,来看看 Paxos 算法与 ZAB 算法的相同及不同之处。</p>
|
||||
|
||||
<p><strong>相同之处是</strong>,在执行事务行会话的处理中,两种算法最开始都需要一台服务器或者线程针对该会话,在集群中发起提案或是投票。只有当集群中的过半数服务器对该提案投票通过后,才能执行接下来的处理。</p>
|
||||
|
||||
<p>而 Paxos 算法与 ZAB 协议<strong>不同的是</strong>,Paxos 算法的发起者可以是一个或多个。当集群中的 Acceptor 服务器中的大多数可以执行会话请求后,提议者服务器只负责发送提交指令,事务的执行实际发生在 Acceptor 服务器。这与 ZooKeeper 服务器上事务的执行发生在 Leader 服务器上不同。Paxos 算法在数据同步阶段,是多台 Acceptor 服务器作为数据源同步给集群中的多台 Learner 服务器,而 ZooKeeper 则是单台 Leader 服务器作为数据源同步给集群中的其他角色服务器。</p>
|
||||
|
||||
<h3>总结</h3>
|
||||
|
||||
<p>本节课我们主要介绍了 Paxos 算法,该算法在解决分布式一致性问题上被广泛采用。Paxos 算法将集群中的服务器或网络节点分为提议者(Proposer)、决策者(Acceptor)、决策学习者(Learner),在处理事务性会话请求的时候,会针对该会话操作在集群中通过提议者(Proposer)服务器发起询问操作,之后由决策者(Acceptor)服务器决定是否执行。在集群中多数服务器都正确执行会话操作后,决策学习者(Learner)会同步(Acceptor)服务器上的数据,并完成最终的操作。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">下一页</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":"70997607a9003cfa","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>
|
||||
|
||||
940
专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md.html
Normal file
@@ -0,0 +1,940 @@
|
||||
<!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>31 ZooKeeper 中二阶段提交算法的实现分析.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>31 ZooKeeper 中二阶段提交算法的实现分析</h1>
|
||||
|
||||
<p>前几节课中,我们一直围绕在分布式系统环境下,如何解决一致性问题来进行讨论,并分别介绍了在分布式环境中比较常见的二阶段提交、三阶段提交算法,之后又对比介绍了 ZooKeeper 所采用的 ZAB 协议算法和 Paxos 算法的优缺点。</p>
|
||||
|
||||
<p>在学习 ZAB 协议和 Paxos 算法的过程中,我们曾提到在处理来自客户端的事务性请求时,为了保证整个集群的数据一致性,其各自的底层实现与二阶段算法都有相似之处。但我们知道,二阶段提交算法自身有一些缺点,比如容易发生单点故障,比如在并发性能上有一些瓶颈,那么今天就深入 ZooKeeper 的底层,来看一下 ZooKeeper 是如何克服这些问题,并实现自己特有的二阶段提交算法的。希望通过本节课的学习,帮助你进一步提高解决分布式一致性问题的能力。</p>
|
||||
|
||||
<h3>提交请求</h3>
|
||||
|
||||
<p>前面我们学到,二阶段提交的本质是协调和处理 ZooKeeper 集群中的服务器,使它们在处理事务性会话请求的过程中能保证数据一致性。如果把执行在 ZooKeeper 集群中各个服务器上的事务会话处理操作分别看作不同的函数,那么整个一致性的处理逻辑就相当于包裹这些函数的事务。而在单机环境中处理事务的逻辑是,包含在事务中的所有函数要么全部成功执行,要么全部都不执行。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F889pCADWF1AABRdrcwWog707.png" alt="image.png" /></p>
|
||||
|
||||
<p>不同的是,在分布式环境中,处理事务请求的各个函数是分布在不同的网络服务器上的线程,无法像在单机环境下一样,做到当事务中的某一个环节发生异常的时候,回滚包裹在整个事务中的操作。因此,分布式环境中处理事务操作的时候,一般的算法不会要求全部集群中的机器都成功执行操作,如果有其中一个函数执行异常,那么整个事务就会把所有函数的执行结果回滚到执行前的状态,也就是无论是正确执行的函数,还是执行异常的函数,各自所做的对数据和程序状态的变更都将被删除。</p>
|
||||
|
||||
<h3>执行请求</h3>
|
||||
|
||||
<p>看完提交请求的处理过程后,我们再来看一下在执行请求时 ZooKeeper 的底层实现过程。</p>
|
||||
|
||||
<p>ZooKeeper 集群中的 Leader 服务器对该条事务性会话操作是否能够在 Follow 服务器上执行,向集群中的 Follow 服务器发起 Proposal 请求。</p>
|
||||
|
||||
<p>这里请你注意,与我们之前介绍的二阶段提交不同的是,在 ZooKeeper 的实现中并没有中断提交的逻辑。集群中的 Follow 服务器在接收到上述 Proposal 请求后,只有两种处理情况:</p>
|
||||
|
||||
<p>第一种情况:ZooKeeper 集群中的 Follow 服务器能够正确执行操作,并向 ZooKeeper 集群中的 Leader 反馈执行结果。</p>
|
||||
|
||||
<p>第二种情况:无法正确执行该条 Proposal 操作,直接抛弃该条请求。</p>
|
||||
|
||||
<p>ZooKeeper 集群的这种执行逻辑,最终导致无须等 待所有服务器都执行完成并反馈,集群中的 Leader 服务器只需要接收到集群中过半数的 Follow 服务器成功执行的反馈信息, ZooKeeper 集群中的 Leader 服务器最终会统计 Follow 服务器反馈的信息,当超过半数以上服务器可以正确执行操作后,整个 ZooKeeper 集群就可以进入执行事务提交操作。</p>
|
||||
|
||||
<h3>底层实现</h3>
|
||||
|
||||
<p>介绍完 ZooKeeper 实现二阶段提交算法的原理后,接下来我们深入代码层面看看 ZooKeeper 是如何设计架构的。</p>
|
||||
|
||||
<p>从源码层面来讲,ZooKeeper 在实现整个二阶段提交算法的过程中,可以分为 Leader 服务器端的发起 Proposal 操作和 Follow 服务器端的执行反馈操作。</p>
|
||||
|
||||
<p>我们先来看看,在 ZooKeeper 集群中的 Leader 是如何向其他 Follow 服务器发送 Proposal 请求的呢?</p>
|
||||
|
||||
<p>如下面的代码所示, ZooKeeper 通过 SendAckRequestProcessor 类发送 Proposal 来提交请求。这个类首先继承了 RequestProcessor 类,但是它不是处理来自客户端的请求信息,而是用来处理向 Follow 服务器发送的 Proposal 请求信息。它在内部通过 processRequest 函数来判断,责任链中传递请求操作是否是数据同步操作:如果判断是 OpCode.sync 操作(也就是数据同步操作),就通过 learner.writePacket 方法把 Proposal 请求向集群中的所有 Follow 服务器进行发送。</p>
|
||||
|
||||
<pre><code>public class SendAckRequestProcessor implements RequestProcessor, Flushable {
|
||||
|
||||
|
||||
|
||||
public void processRequest(Request si) {
|
||||
|
||||
|
||||
|
||||
if(si.type != OpCode.sync){
|
||||
|
||||
|
||||
|
||||
QuorumPacket qp = new QuorumPacket(Leader.ACK, si.getHdr().getZxid(), null,
|
||||
|
||||
|
||||
|
||||
null);
|
||||
|
||||
|
||||
|
||||
try {
|
||||
|
||||
|
||||
|
||||
learner.writePacket(qp, false);
|
||||
|
||||
|
||||
|
||||
} catch (IOException e) {
|
||||
|
||||
|
||||
|
||||
LOG.warn("Closing connection to leader, exception during packet send", e);
|
||||
|
||||
|
||||
|
||||
try {
|
||||
|
||||
|
||||
|
||||
if (!learner.sock.isClosed()) {
|
||||
|
||||
|
||||
|
||||
learner.sock.close();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch (IOException e1) {
|
||||
|
||||
|
||||
|
||||
// Nothing to do, we are shutting things down, so an exception here is irrelevant
|
||||
|
||||
|
||||
|
||||
LOG.debug("Ignoring error closing the connection", e1);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>在介绍完 ZooKeeper 集群中的 Leader 服务器发送 Proposal 的底层实现过程后,接下来我们再来学习一下 Follow 服务端在接收到 Leader 服务器发送的 Proposal 后的整个处理逻辑。</p>
|
||||
|
||||
<p>如下面的代码所示,这在 Follow 服务器端是通过 ProposalRequestProcessor 来完成处理的。ProposalRequestProcessor 构造函数中首先初始化了 Leader 服务器、下一个请求处理器,以及负责反馈执行结果给 Leader 服务器的 AckRequestProcessor 处理器。</p>
|
||||
|
||||
<pre><code>public ProposalRequestProcessor(LeaderZooKeeperServer zks,
|
||||
|
||||
|
||||
|
||||
RequestProcessor nextProcessor) {
|
||||
|
||||
|
||||
|
||||
this.zks = zks;
|
||||
|
||||
|
||||
|
||||
this.nextProcessor = nextProcessor;
|
||||
|
||||
|
||||
|
||||
AckRequestProcessor ackProcessor = new AckRequestProcessor(zks.getLeader());
|
||||
|
||||
|
||||
|
||||
syncProcessor = new SyncRequestProcessor(zks, ackProcessor);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>接下来,我们进入到 AckRequestProcessor 函数的内部,来看一下 Follow 服务器是如何反馈处理结果给 Leader 服务器的。</p>
|
||||
|
||||
<p>如下面的代码所示, AckRequestProcessor 类同样也继承了 RequestProcessor,从中可以看出在 ZooKeeper 中处理 Leader 服务器的 Proposal 时,是将该 Proposal 请求当作网络中的一条会话请求来处理的。整个处理的逻辑实现也是按照处理链模式设计实现的,在 AckRequestProcessor 类的内部通过 processRequest 函数,来向集群中的 Leader 服务器发送 ack 反馈信息。</p>
|
||||
|
||||
<pre><code>class AckRequestProcessor implements RequestProcessor {
|
||||
|
||||
|
||||
|
||||
public void processRequest(Request request) {
|
||||
|
||||
|
||||
|
||||
QuorumPeer self = leader.self;
|
||||
|
||||
|
||||
|
||||
if(self != null)
|
||||
|
||||
|
||||
|
||||
leader.processAck(self.getId(), request.zxid, null);
|
||||
|
||||
|
||||
|
||||
else
|
||||
|
||||
|
||||
|
||||
LOG.error("Null QuorumPeer");
|
||||
|
||||
|
||||
|
||||
}}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h3>总结</h3>
|
||||
|
||||
<p>本节课我们主要介绍了,二阶段提交算法在 ZooKeeper 中的应用,并深入底层分析了 ZooKeeper 实现二阶段提交的具体过程。虽然二阶段提交自身有一些问题,不过还是一个比较好的解决分布式环境下一致性问题的算法,因此 ZooKeeper 在实现的过程中也借鉴了它,并通过自身的崩溃恢复机制来解决二阶段提交算法中的单点故障等问题。</p>
|
||||
|
||||
<p>从前几个课时到现在可以发现,无论是 Paxos 算法还是 ZAB 协议,都在原始的二阶段提交算法的基础上针对自身要解决的问题进行了修改和优化。面对复杂的网络环境,再优秀的算法也不是万能的。我们在学习算法的同时,更应该深入掌握其中的核心原理以及算法设计的目的,以便在日常工作中更好地解决遇到的问题。</p>
|
||||
|
||||
<p>这里给大家一道<strong>思考题</strong>:在二阶段提交算法中,不可缺少的一个环节就是,向集群中的服务器发送提案询问操作,当收到提案操作后,需要集群中的服务器给予反馈,这本质上就是一次客户端和服务器端的会话处理过程。而当会话的超时时间过长,虽然能够保证服务器的响应,但又会降低整个集群的处理性能。可是过短的超时时间,又可能会因为网络延迟或事务处理耗时,导致本可以正常执行的反馈信息并没有发送回提案服务器。那么如果是你,应该如何设置这个会话的超时时间呢?</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">下一页</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":"7099760a0e403cfa","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>
|
||||
|
||||
888
专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md.html
Normal file
@@ -0,0 +1,888 @@
|
||||
<!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>32 ZooKeeper 数据存储底层实现解析.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>32 ZooKeeper 数据存储底层实现解析</h1>
|
||||
|
||||
<p>在之前的“27 课| crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式”中,我们介绍了线上日志的清理方式,并讲解了 ZooKeeper 服务在运行的过程中产生的数据日志等文件。本节课我们将继续学习 ZooKeeper 文件存储和管理的相关知识,深入分析 ZooKeeper 文件系统的布局方式和不同文件的内部结构格式。</p>
|
||||
|
||||
<h3>文件系统布局</h3>
|
||||
|
||||
<p>无论是 ZooKeeper 服务在运行时候产生的数据日志,还是在集群中进行数据同步的时候所用到的数据快照,都可以被看作一种文件系统。而文件系统的两个功能就是对文件的存储和对不同文件格式的解析。ZooKeeper 中的数据存储,可以分为两种类型:数据日志文件和快照文件,接下来我们就分别介绍这两种文件的结构信息和底层实现。</p>
|
||||
|
||||
<h3>数据日志</h3>
|
||||
|
||||
<p>在 ZooKeeper 服务运行的过程中,数据日志是用来记录 ZooKeeper 服务运行状态的数据文件。通过这个文件我们不但能统计 ZooKeeper 服务的运行情况,更可以在 ZooKeeper 服务发生异常的情况下,根据日志文件记录的内容来进行分析,定位问题产生的原因并找到解决异常错误的方法。</p>
|
||||
|
||||
<p>如何找到日志文件呢?在 ZooKeeper 的 zoo.cfg 配置文件中的 dataLogDir 属性字段,所指定的文件地址就是当前 ZooKeeper 服务的日志文件的存储地址。</p>
|
||||
|
||||
<p>在了解了 ZooKeeper 服务在运行的过程中所产生的日志文件的存放位置,以及日志文件的格式结构后,接下来我们就深入到 ZooKeeper 服务的底层,来看一下它是如何实现日志的搜集以及存储的。</p>
|
||||
|
||||
<h4>搜集日志</h4>
|
||||
|
||||
<p>我们先来看一下 ,ZooKeeper 是如何搜集程序的运行信息的。在统计操作情况的日志信息中,ZooKeeper 通过第三方开源日志服务框架 SLF4J 来实现的。</p>
|
||||
|
||||
<p>SLF4J 是一个<strong>采用门面设计模式(Facade)</strong> 的日志框架。如下图所示,门面模式也叫作外观模式,采用这种设计模式的主要作用是,对外隐藏系统内部的复杂性,并向外部调用的客户端或程序提供统一的接口。门面模式通常以接口的方式实现,可以被程序中的方法引用。</p>
|
||||
|
||||
<p>在下图中,我们用门面模式创建了一个绘制几何图形的小功能。首先,定义了一个 Shape 接口类,并分别创建了三个类 Circle、Square、Rectangle ,以继承 Shape 接口。其次,我们再来创建一个画笔类 ShapeMaker ,在该类中我定义了 shape 形状字段以及绘画函数 drawCircle等。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8_iESARR3eAAB5fX25Qrk279.png" alt="Drawing 0.png" /></p>
|
||||
|
||||
<p>之后,当我们在本地项目中需要调用实现的会话功能时,直接调用 ShapeMaker 类,并传入我们要绘制的图形信息,就可以实现图形的绘制功能了。它使用起来非常简单,不必关心其底层是如何实现绘制操作的,只要将我们需要绘制的图形信息传入到接口函数中即可。</p>
|
||||
|
||||
<p>而在 ZooKeeper 中使用 SLF4J 日志框架也同样简单,如下面的代码所示,首先在类中通过工厂函数创建日志工具类 LOG,然后在需要搜集的操作流程处引入日志搜集函数 LOG.info 即可。</p>
|
||||
|
||||
<pre><code>protected static final Logger LOG = LoggerFactory.getLogger(Learner.class);
|
||||
|
||||
|
||||
|
||||
LOG.info("Revalidating client: 0x" + Long.toHexString(clientId));
|
||||
|
||||
|
||||
|
||||
LOG.warn("Couldn't find the leader with id = "
|
||||
|
||||
|
||||
|
||||
+ current.getId());
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4>存储日志</h4>
|
||||
|
||||
<p>接下来我们看一下搜集完的日志是什么样子的。在开头我们已经说过,系统日志的存放位置,在 zoo.cfg 文件中。假设我们的日志路径为dataDir=/var/lib/zookeeper,打开系统命令行,进入到该文件夹,就会看到如下图所示的样子,所有系统日志文件都放在了该文件夹下。</p>
|
||||
|
||||
<p><img src="assets/CgqCHl8_iFWAQwfvAAAfvGjcfow495.png" alt="Drawing 1.png" /></p>
|
||||
|
||||
<h3>快照文件</h3>
|
||||
|
||||
<p>除了上面介绍的记录系统操作日志的文件外,ZooKeeper 中另一种十分重要的文件数据是快照日志文件。快照日志文件主要用来存储 ZooKeeper 服务中的事务性操作日志,并通过数据快照文件实现集群之间服务器的数据同步功能。</p>
|
||||
|
||||
<h4>快照创建</h4>
|
||||
|
||||
<p>接下来我们来介绍,在 ZooKeeper 的底层实现中,一个快照文件是如何创建的。</p>
|
||||
|
||||
<p>如下面的代码所示,在 ZooKeeper 的源码中定义了一个 SnapShot 接口类,在该接口中描述了 ZooKeeper 服务的相关属性和方法。其中 serialize 函数是用来将内存中的快照文件转存到本地磁盘中时的序列化操作。而 deserialize 的作用正好与其相反,是把快照文件从本地磁盘中加载到内存中时的反序列化操作。无论是序列化还是反序列化,整个快照所操作的数据对象是 ZooKeeper 数据模型,也就是由 Znode 组成的结构树。</p>
|
||||
|
||||
<pre><code>public interface SnapShot {
|
||||
|
||||
|
||||
|
||||
long deserialize(DataTree dt, Map<Long, Integer> sessions)
|
||||
|
||||
|
||||
|
||||
throws IOException;
|
||||
|
||||
|
||||
|
||||
void serialize(DataTree dt, Map<Long, Integer> sessions,
|
||||
|
||||
|
||||
|
||||
File name, boolean fsync)
|
||||
|
||||
|
||||
|
||||
throws IOException;
|
||||
|
||||
|
||||
|
||||
File findMostRecentSnapshot() throws IOException;
|
||||
|
||||
|
||||
|
||||
void close() throws IOException;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4>快照存储</h4>
|
||||
|
||||
<p>创建完 ZooKeeper 服务的数据快照文件后,接下来就要对数据文件进行持久化的存储操作了。其实在整个 ZooKeeper 中,随着服务的不同阶段变化,数据快照存放文件的位置也随之变化。存储位置的变化,主要是内存和本地磁盘之间的转变。当 ZooKeeper 集群处理来自客户端的事务性的会话请求的时候,会首先在服务器内存中针对本次会话生成数据快照。当整个集群可以执行该条事务会话请求后,提交该请求操作,就会将数据快照持久化到本地磁盘中,如下图所示。</p>
|
||||
|
||||
<p><img src="assets/Ciqc1F8_iGKAU3N-AABQvy8biHk445.png" alt="zk.png" /></p>
|
||||
|
||||
<p>存储到本地磁盘中的数据快照文件,是经过 ZooKeeper 序列化后的二进制格式文件,通常我们无法直接查看,但如果想要查看,也可以通过 ZooKeeper 自带的 SnapshotFormatter 类来实现。如下图所示,在 SnapshotFormatter 类的内部用来查看快照文件的几种函数分别是: printDetails 函数,用来打印日志中的数据节点和 Session 会话信息;printZnodeDetails 函数,用来查看日志文件中节点的详细信息,包括节点 id 编码、state 状态信息、version 节点版本信息等。</p>
|
||||
|
||||
<pre><code>public class SnapshotFormatter {
|
||||
|
||||
|
||||
|
||||
private void printDetails(DataTree dataTree, Map<Long, Integer> sessions)
|
||||
|
||||
|
||||
|
||||
private void printZnodeDetails(DataTree dataTree)
|
||||
|
||||
|
||||
|
||||
private void printZnode(DataTree dataTree, String name)
|
||||
|
||||
|
||||
|
||||
private void printSessionDetails(DataTree dataTree, Map<Long, Integer> sessions)
|
||||
|
||||
|
||||
|
||||
private void printStat(StatPersisted stat)
|
||||
|
||||
|
||||
|
||||
private void printHex(String prefix, long value)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>虽然 ZooKeeper 提供了 SnapshotFormatter 类,但其实现的查询功能比较单一,我们可以通过本节课的学习,按照自己的业务需求,编写自己的快照文件查看器。</p>
|
||||
|
||||
<p>到目前位置,我们对 ZooKeeper 服务相关的数据文件都做了讲解。无论是数据日志文件,还是数据快照文件,最终都会存储在本地磁盘中。而从文件的生成方式来看,两种日志文件的不同是:数据日志文件实施性更高,相对的产生的日志文件也不断变化,只要 ZooKeeper 服务一直运行,就会产生新的操作日志数据;而数据快照并非实时产生,它是当集群中数据发生变化后,先在内存中生成数据快照文件,经过序列化后再存储到本地磁盘中。</p>
|
||||
|
||||
<h3>总结</h3>
|
||||
|
||||
<p>本节课我们讲解了在 ZooKeeper 服务运行过程中所产生的两种主要数据文件:数据日志文件和数据快照文件的结构信息和底层实现,以便加强你对它们的理解。而数据日志文件如何监控并解决异常情况,数据快照文件如何实现数据同步等内容,我们在之前的课程中已经提到,希望你回顾之前的内容,并结合本节课的内容,对 ZooKeeper 的文件系统有更深的理解。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">下一页</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":"7099760c4b313cfa","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>
|
||||
|
||||
770
专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html
Normal file
@@ -0,0 +1,770 @@
|
||||
<!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>33 结束语 分布技术发展与 ZooKeeper 应用前景.md</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="/专栏/ZooKeeper源码分析与实战-完/00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md">00 开篇词:选择 ZooKeeper,一步到位掌握分布式开发.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/01 ZooKeeper 数据模型:节点的特性与应用.md">01 ZooKeeper 数据模型:节点的特性与应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md">02 发布订阅模式:如何使用 Watch 机制实现分布式通知.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/03 ACL 权限控制:如何避免未经授权的访问?.md">03 ACL 权限控制:如何避免未经授权的访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/04 ZooKeeper 如何进行序列化?.md">04 ZooKeeper 如何进行序列化?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/05 深入分析 Jute 的底层实现原理.md">05 深入分析 Jute 的底层实现原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/06 ZooKeeper 的网络通信协议详解.md">06 ZooKeeper 的网络通信协议详解.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/07 单机模式:服务器如何从初始化到对外提供服务?.md">07 单机模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/08 集群模式:服务器如何从初始化到对外提供服务?.md">08 集群模式:服务器如何从初始化到对外提供服务?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/09 创建会话:避开日常开发的那些“坑”.md">09 创建会话:避开日常开发的那些“坑”.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/10 ClientCnxn:客户端核心工作类工作原理解析.md">10 ClientCnxn:客户端核心工作类工作原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/11 分桶策略:如何实现高效的会话管理?.md">11 分桶策略:如何实现高效的会话管理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/12 服务端是如何处理一次会话请求的?.md">12 服务端是如何处理一次会话请求的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/13 Curator:如何降低 ZooKeeper 使用的复杂性?.md">13 Curator:如何降低 ZooKeeper 使用的复杂性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/14 Leader 选举:如何保证分布式数据的一致性?.md">14 Leader 选举:如何保证分布式数据的一致性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/15 ZooKeeper 究竟是怎么选中 Leader 的?.md">15 ZooKeeper 究竟是怎么选中 Leader 的?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md">16 ZooKeeper 集群中 Leader 与 Follower 的数据同步策略.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/17 集群中 Leader 的作用:事务的请求处理与调度分析.md">17 集群中 Leader 的作用:事务的请求处理与调度分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md">18 集群中 Follow 的作用:非事务请求的处理与 Leader 的选举分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/19 Observer 的作用与 Follow 有哪些不同?.md">19 Observer 的作用与 Follow 有哪些不同?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md">20 一个运行中的 ZooKeeper 服务会产生哪些数据和文件?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/21 ZooKeeper 分布式锁:实现和原理解析.md">21 ZooKeeper 分布式锁:实现和原理解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md">22 基于 ZooKeeper 命名服务的应用:分布式 ID 生成器.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/23 使用 ZooKeeper 实现负载均衡服务器功能.md">23 使用 ZooKeeper 实现负载均衡服务器功能.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md">24 ZooKeeper 在 Kafka 和 Dubbo 中的工业级实现案例分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/25 如何搭建一个高可用的 ZooKeeper 生产环境?.md">25 如何搭建一个高可用的 ZooKeeper 生产环境?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md">26 JConsole 与四字母命令:如何监控服务器上 ZooKeeper 的运行状态?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md">27 crontab 与 PurgeTxnLog:线上系统日志清理的最佳时间和方式.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/28 彻底掌握二阶段提交三阶段提交算法原理.md">28 彻底掌握二阶段提交三阶段提交算法原理.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/29 ZAB 协议算法:崩溃恢复和消息广播.md">29 ZAB 协议算法:崩溃恢复和消息广播.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/30 ZAB 与 Paxos 算法的联系与区别.md">30 ZAB 与 Paxos 算法的联系与区别.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/31 ZooKeeper 中二阶段提交算法的实现分析.md">31 ZooKeeper 中二阶段提交算法的实现分析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">32 ZooKeeper 数据存储底层实现解析.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/ZooKeeper源码分析与实战-完/33 结束语 分布技术发展与 ZooKeeper 应用前景.md">33 结束语 分布技术发展与 ZooKeeper 应用前景.md.html</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>33 结束语 分布技术发展与 ZooKeeper 应用前景</h1>
|
||||
|
||||
<p>到目前为止,本专栏的所有课程已经结束了。在这个专栏中,我们主要介绍了分布式系统架构最核心的问题:如何解决分布式环境一致性。围绕这个问题,我为你引入了分布式一致性问题的工业解决方案——开源框架 ZooKeeper。</p>
|
||||
|
||||
<p>通过本专栏的学习,可以帮助你掌握实现分布式一致性的各种算法,包括:二阶段提交 、三阶段提交、 ZooKeeper 的 ZAB 协议算法以及 Paxos 算法。掌握了这些算法的理论知识后,我们又进一步分析了,代码层面的底层架构设计思想和实现过程。实现一个分布式系统会面临很多的挑战,在明确问题的原因后,更应该灵活运用学到的知识,找到问题的最优解。</p>
|
||||
|
||||
<p>在结束语中,我打算<strong>添加一个小彩蛋</strong>,向你介绍除了已经掌握的 ZooKeeper 框架的 ZAB 协议算法之外,解决分布式一致性问题的另一种方法:Raft 算法。</p>
|
||||
|
||||
<h3>彩蛋:Raft 算法</h3>
|
||||
|
||||
<p>相比之前学到的 ZAB 协议算法和 Paxos 算法,Raft 算法的实现逻辑则更为简单。Raft 算法将分布式一致性问题的解决,分解为一个个更加细小简单的问题。</p>
|
||||
|
||||
<h3>实现过程</h3>
|
||||
|
||||
<p>它的实现过程与 Paoxs 等一致性协议算法有相似的地方,但也有其自身的特点。其中强领导者、领导选举、成员关系调整是其特有的概念。</p>
|
||||
|
||||
<h4>强领导者</h4>
|
||||
|
||||
<p>在 ZAB 协议算法中,集群中会有一个 Leader 服务器,同样,Raft 算法也需要在集群中创建一个领导者服务。与 Leader 服务器相比,Raft 算法中的领导者服务器具有更强的功能,比如在数据的同步方式上,它只通过领导者服务发送给集群中的其他服务器。</p>
|
||||
|
||||
<h4>领导选举</h4>
|
||||
|
||||
<p>与我们之前介绍的一致性协议不同,在领导者服务的选举方式上,Raft 算法只采用随机技术器的方式。在该随机计数器产生的超时时间内,集群中的服务器各自向网络中广播投票信息,当某一台服务器收到超过集群中一半服务器的响应后,该台服务器就被选举为新的领导者。</p>
|
||||
|
||||
<h4>成员关系</h4>
|
||||
|
||||
<p>在处理事务性的回来请求时,Raft 算法中的领导者服务器会执行该条会话操作,但并不提交,只是将该操作写入到日志中,再发送给集群中的其他服务器。当接收到超过一半的服务能够正常操作的反馈信息后,领导者服务器才最终提交本次会话请求操作,并向集群中的其他服务器发送提交请求。</p>
|
||||
|
||||
<h3>总结</h3>
|
||||
|
||||
<p>只要采用分布式系统架构,都避免不了一致性的问题。本专栏我们主要学习了 Paxos 算法和 ZAB 协议算法。本节课又介绍了一种一致性协议算法——Raft。Paoxs 算法是解决一致性问题的最完善的算法,但对其底层实现的细节并没有给出更详细的解释。整个算法的实现难度较大。ZAB 协议和 Raft 算法都可以说是在 Paxos 算法的基础上,做了各自的优化和改进,给我们提供了一致性协议的工业化解决方案。你可以深入研究 Paxos 算法的理论,并结合 ZooKeeper 框架的设计思路,设计开发自己的一致性协议框架。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/ZooKeeper源码分析与实战-完/32 ZooKeeper 数据存储底层实现解析.md">上一页</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":"7099760e982f3cfa","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>
|
||||
|
||||
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl6-a8mAOP1YAABW8fO1GAM913.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl61INaAJeAEAAA8lZ8lpbE688.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl67tw2AbgggAACW3WWz4D4066.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl67twGANQ9kAABTpMHtYI0138.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl67txWALBicAABysKoJmFg484.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl67tyGAJKDxAABxRTWnmZs166.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl6yL-SAb0zaAABQBLohKvo019.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl6yL9uAbpHYAABF_GHyGNc950.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl6yL_6AOIONAAB3daUjikw147.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl6yL_WAAnymAAB32xbrhxQ973.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl6yLsaAJf-3AACcfiC01UY046.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl6yMBKAZzwGAABPrrtajyI575.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl78TTKAVHIcAABFlvJZKxw198.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl78TUKAfJjuAAB5F0dP0Yk087.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl78TVGAK4VLAACDtg8BUbg363.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl78TWOAcPwoAABCnE5qDAc777.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl78TXCAM65nAAB2mZDf_jw385.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl7ORB-AdNM1AABRbvNSlEE886.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl7ORDiAIMqzAABBGUvvhFU739.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl7XUbeAH4RvAABLmAD-zt0526.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl7XUcSAAoGmAAO6qmEJCC4477.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl7_ArmAD4KYAABMANi9AkA539.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl7aDQyAEkoJAAB9K_a8-pA768.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl7gfSqADJ72AAA2hG45T-M370.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl7jHYCAACUBAABVIt47Rxg107.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl7siPqAEgUiAAAsZnldQKA321.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl7siSSAGaKOAABaTR2X0Wo408.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl7zKRuARwOdAACqX-dZDEQ790.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl7zLYaASBk3AAA-I033owc988.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl8O5rOADPbBAACVhsBN-NU550.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl8Oc56AEMuZAAAsuQwHWCY999.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl8RTBGAB7QNAAAvwu3rspw007.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl8RWyyAP_5_AACcEDVnsPc537.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl8_iFWAQwfvAAAfvGjcfow495.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl8ah16Ac117AAKJOqYuJ28381.png
Normal file
|
After Width: | Height: | Size: 162 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl8ah3iASZ2-AAEGN0oprwQ428.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl8kAy2ANt38AADolBOTa2s256.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl8qlt2ALC7CAABlifm7LHs902.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl8qlu-AW-xZAAA50ErYH4s391.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/CgqCHl8zq1SABNDvAAB3WcgZ-2s055.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F6-a8CAUVfhAABcAV_NNXw402.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F61IL-AEQuUAABdpaAsy2k628.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F61ILaAb7sQAAC6T3wMHDU651.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F61IMWAbWW9AABzXk9xuOs953.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F6yL-yAKn9QAABsJSpQkFI688.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F6yL9OAUUguAAAtBwGI74E989.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F6zbwWAVkt5AAC_yMQVCFo712.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F7OQ_uAU5yzAABbrnOyALM516.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F7_AqmAchKvAABHDmj-uIc721.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F7_AtCAVRpLAABYUblVD80124.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F7gfSCAGDAIAABZCSkfyB0372.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F7jHaCAXeh5AAAg5bIKooo221.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F7pw-CAZMcMAABbJUFbJOY244.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F7pxGCAPkfEAAAxSw3ff2o475.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F7zKQyAK4wsAADnGMCxArI126.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F82Q0yAQd1UAAB9Sb1EaFg098.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F82RbqAHPmCAAFicrAh8y0060.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F889pCADWF1AABRdrcwWog707.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F8FnKWAQEJJAADU9xFvIIU685.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F8FnLGAKhD0AAE5oGBLTTQ439.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F8IC-uAcS1bAABJoZ4awKg473.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F8Oc6aAH44DAAA1aVd9UXo732.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F8RW0aAMpWeAACkxZm34vQ563.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F8RWzqAT1UwAACcwktuN0M073.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F8X5l-APWIjAAAsDI_4m_Q833.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
专栏/ZooKeeper源码分析与实战-完/assets/Ciqc1F8X5n6AGCBQAABSbLOIuWA010.png
Normal file
|
After Width: | Height: | Size: 21 KiB |