hadoop知识点总结

hadoop

大数据概述

大数据内涵

大数据的概念

  • 维克托·迈尔-舍恩伯格及肯尼斯·库克耶:大数据是不用随机分析法(抽样调查)这样捷径,而采用所有数据进行分析处理。
  • 大数据研究机构Gartner:大数据是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力来适应海量、高增长率和多样化的信息资产。
  • 麦肯锡全球研究所:一种规模大到在获取、存储、管理、分析方面大大超出了传统数据库软件工具能力范围的数据集合,具有海量的数据规模、快速的数据流转、多样的数据类型和价值密度低四大特征。
  • 大数据是大规模数据的集合体,是数据对象、数据集成技术、数据分析应用、商业模式、思维创新的统一体,也是一门捕捉、管理和处理数据的技术,它代表着一种全新的思维方式。

大数据的内涵

  • 从对象角度来看,大数据是数据规模超出传统数据库处理能力的数据集合
  • 从技术角度来看,大数据是从海量数据中快速获得有价值信息的技术。
  • 从应用角度来看,大数据是对特定数据集合应用相关技术获得价值的行为。
  • 从商业模式角度来看,大数据是企业获得商业价值的业务创新方向
  • 从思维方式来看,大数据是从第三范式中分离出来的一种科研范式。

大数据的4V特征

  • Volume:大数据的体量非常大,PB级别将是常态,且增长速度较快。
  • Variety:大数据种类繁多,一般包括结构化、半结构化和非结构化等多种类型的数据。
  • Velocity:数据的快速流动和处理是大数据区分于传统数据挖掘的显著特征。
  • Value:大数据价值密度的高低与数据总量大小成反比,单条数据本身并无太多价值,但庞大的数据量累积并隐藏了巨大的财富。其价值具备稀疏性、多样性和不确定性等特点。

大数据关键技术

  • 大数据采集技术:大数据智能感知层:主要包括数据传感体系、网络通信体系、传感适配体系、智能识别体系及软硬件资源接入系统。基础支撑层:提供大数据服务平台所需的虚拟服务器,结构化、半结构化及非结构化数据的数据库及物联网络资源等基础支撑环境。
  • 大数据预处理技术
  • 大数据存储及管理技术
  • 大数据分析及挖掘技术:大数据分析指对规模巨大的数据用适当的统计方法进行分析,提取有用的信息和形成结论。数据挖掘就是从大量的、不完全的、有噪声的、模糊的、随机的实际应用数据中,提取隐含在其中的、人们事先不知道的、但又潜在有用的信息和知识的过程。
  • 大数据展现与应用技术

大数据产业

​ 大数据产业指以数据生产、采集、存储、加工、分析、服务为主的相关经济活动。比如:数据资源建设;大数据软硬件产品的开发;销售和租赁活动;相关信息技术服务

大数据与物联网、云计算、人工智能、5G的关系

​ 物联网、云计算和5G是大数据的底层架构,大数据依赖云计算来处理大数据,人工智能是大数据的场景应用。

​ 大数据是物联网、Web和传统信息系统发展的必然结果,大数据在技术体系上与云计算重点都是分布式存储和分布式计算,云计算注重服务,大数据则注重数据的价值化操作。

​ 人工智能(Artificial Intelligence,AI)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。人工智能其实就是大数据、云计算的一个应用场景。人工智能则包含了机器学习,从被动到主动,从模式化实行指令,到自主判断根据情况实行不同的指令。

hadoop简述

hadoop概述

  • Apache Hadoop于2008年1月成为Apache顶级项目。Hadoop是一个开源的、可运行于大规模集群上的分布式存储和计算的软件框架,它具有高可靠弹性可扩展等特点,非常适合处理海量数据。Hadoop实现了分布式文件系统HDFS和分布式计算框架MapReduce等功能,被公认为行业大数据标准软件,在业内得到了广泛应用。
  • Hadoop旨在从单一服务器扩展到成千上万台机器,每台机器都提供本地计算和存储,且将数据备份在多个节点上,由此来提升集群的高可用性,而不是通过硬件提升,当一台机器宕机时,其它节点依然可以提供数据和计算服务。

hadoop简介

  • 第一代Hadoop(即Hadoop 1.0)的核心由分布式文件系统HDFS和分布式计算框架MapReduce组成,为了克服Hadoop1.0中HDFS和MapReduce的架构设计和应用性能方面的各种问题,提出了第二代Hadoop(即Hadoop 2.0),Hadoop 2.0的核心包括分布式文件系统HDFS、统一资源管理和调度框架YARN和分布式计算框架MapReduce

  • HDFS是谷歌文件系统GFS的开源实现,是面向普通硬件环境的分布式文件系统,适用于大数据场景的数据存储,提供了高可靠、高扩展、高吞吐率的数据存储服务。

  • MapReduce是谷歌MapReduce的开源实现,是一种简化的分布式应用程序开发的编程模型,允许开发人员在不了解分布式系统底层细节和缺少并行应用开发经验的情况下,能快速轻松地编写出分布式并行程序,将其运行于计算机集群上,完成对大规模数据集的存储和计算。

  • YARN是将MapReduce 1.0中JobTracker的资源管理功能单独剥离出来而形成,它是一个纯粹的资源管理和调度框架,并解决了Hadoop 1.0中只能运行MapReduce框架的限制,可在YARN上运行各种不同类型计算框架包括MapReduce、Spark、Storm等。

hadoop发展简史

  • Hadoop这个名字不是单词缩写,Hadoop之父道格•卡丁(Doug Cutting)曾这样解释Hadoop名字的由来:“这个名字是我的孩子给一个棕黄色的大象玩具的取的名字。我的命名标准就是简短,容易发音和拼写,并且不会被用于别处。小孩子恰恰是这方面的高手。

  • Hadoop起源于开源的网络搜索引擎Apache Nutch,它本身是Lucence项目的一部分。Nutch项目开始于2002年,一个可以代替当时主流搜索产品的开源搜索引擎。但后来,它的创造者Doug Cutting和Mike Cafarella遇到了棘手难题,该搜索引擎框架只能支持几亿数据的抓取、索引和搜索,无法扩展到拥有数十亿网页的网络。

  • 2003年,Google发表了论文“The Google File System”,可以解决大规模数据存储的问题。于是在2004年,Nutch项目借鉴谷歌GFS使用Java语言开发了自己的分布式文件系统,即Nutch分布式文件系统NDFS,也就是HDFS的前身。

  • 2004年,Google又发表了一篇具有深远影响的论文“MapReduce: Simplifed Data Processing on Large Clusters”,阐述了MapReduce分布式编程思想。Nutch开发者们发现Google MapReduce所解决的大规模搜索引擎数据处理问题,正是他们当时面临并亟待解决的难题。于是,Nutch开发者们模仿Google MapReduce框架设计思路,使用Java语言设计并2005年初开源实现了MapReduce。

  • 2006年2月,Nutch中的NDFS和MapReduce独立出来,形成Lucence的子项目,并命名为Hadoop,同时Doug Cutting进入雅虎,雅虎为此组织了专门的团队和资源,致力于将Hadoop发展成为能够处理海量Web数据的分布式系统。

  • 2007年,纽约时报把存档报纸扫描版的4TB文件在100台亚马逊虚拟机服务器上使用Hadoop转换为PDF格式,整个过程所花时间不到24小时,这一事件更加深了人们对Hadoop的印象。

  • 2008年,Google工程师Christophe Bisciglia发现把当时的Hadoop放到任意一个集群中去运行是一件很困难的事,所以与好友Facebook的Jeff Hammerbacher、雅虎的Amr Awadallah、Oracle的Mike Olson成立了专门商业化Hadoop的公司Cloudera。

  • 2008年1月,Hadoop成为Apache顶级项目。

  • 2008年4月,Hadoop打破世界纪录,成为最快的TB级数据排序系统。在一个910节点的集群上,Hadoop在209秒内完成了对1TB数据的排序,击败前一年的297秒冠军。

  • 2009年4月,Hadoop对1TB数据进行排序只花了62秒。

  • 2011年,雅虎将Hadoop团队独立出来,由雅虎主导Hadoop开发的副总裁Eric Bladeschweiler带领二十几个核心成员成立子公司Hortonworks,专门提供Hadoop相关服务。成立3年就上市。同年12月,发布1.0.0版本,标志着Hadoop已经初具生产规模。

  • 2012年,Hortonworks推出YARN框架第一版本,从此Hadoop的研究进入一个新层面。

  • 2013年10月,发布2.2.0版本,Hadoop正式进入2.x时代。

  • 2014年,Hadoop 2.X更新速度非常快,先后发布2.3.0、2.4.0、2.5.0和2.6.0,极大完善了YARN框架和整个集群的功能,很多Hadoop研发公司如Cloudera、Hortonworks都与其他企业合作共同开发Hadoop新功能。

  • 2015年4月,发布2.7.0版本。

  • 2016年,Hadoop及其生态圈组件Spark等在各行各业落地并得到广泛应用,YARN持续发展以支持更多计算框架。同年9月,发布Hadoop 3.0.0-alpha1版本,预示着Hadoop 3.x时代的到来。

  • 2018年11月,发布Hadoop 2.9.2,同年10月发布Ozone第一版0.2.1-alpha,Ozone是Hadoop的子项目,该项目提供了分布式对象存储,建立在Hadoop分布式数据存储HDDS上。

  • 2019年1月,发布Hadoop 3.2.0,发布Submarine第一版0.1.0,Submarine是Hadoop的子项目,该项目旨在资源管理平台如YARN上运行深度学习应用程序如TensorFlow、PyTorch等。

hadoop的特点

  • 高可靠性:采用冗余数据存储方式,即使一个副本发生故障,其它副本也可以保证正常对外提供服务。

  • 高扩展性:Hadoop设计目标是可以高效稳定地运行在廉价的计算机集群上,可以方便添加机器节点,扩展到数以千计的计算机节点上

  • 高效性:作为分布式计算平台,能够高效地处理PB级数据。

  • 高容错性:采用冗余数据存储方式,自动保存数据的多个副本,当读取该文档出错或者某一台机器宕机,系统会调用其它节点上的备份文件,保证程序顺利运行。

  • 低成本:Hadoop是开源的,即不需要支付任何费用即可下载安装使用。另外,Hadoop集群可以部署在普通机器上,而不需要部署在价格昂贵的小型机上,能够大大减少公司的运营成本

  • 支持多种平台:Hadoop支持Windows和GNU/Linux两类运行平台,Hadoop是基于Java语言开发的,因此其最佳运行环境无疑是Linux,Linux的发行版本众多,常见的有CentOS、Ubuntu、Red Hat、Debian、Fedora、SUSE、openSUSE等。

  • 支持多种编程语言:Hadoop上的应用程序可以使用Java、C++编写。

hadoop的版本

​ Hadoop的发行版本有两类,一类是由社区维护的免费开源的Apache Hadoop,另一类是一些商业公司如Cloudera、Hortonworks、MapR等推出的Hadoop商业版。

hadoop的生态系统

​ Hadoop 2.0主要由三部分构成:分布式文件系统HDFS、统一资源管理和调度框架YARN、分布式计算框架MapReduce;但广义上来讲,Hadoop是指以Hadoop为基础的生态系统,是一个庞大体系,Hadoop仅是其中最基础、最重要的部分,生态系统中每个组件只负责解决某一特定问题。

hadoop组件

  • Hadoop Common是Hadoop体系中最底层的一个模块,为Hadoop各子项目提供各种工具,如系统配置工具Configuration、远程过程调用RPC、序列化机制和日志操作,是其他模块的基础
  • **HDFS(Hadoop Distributed File System)**是Hadoop分布式文件系统,是Hadoop三大核心之一,是针对谷歌文件系统GFS(Google File System)的开源实现(The Google File System, 2003)。HDFS是一个具有高容错性的文件系统,适合部署在廉价的机器上,HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。大数据处理框架如MapReduce、Spark等要处理的数据源大部分都存储在HDFS上,Hive、HBase等框架的数据通常也存储在HDFS上。简而言之,HDFS为大数据的存储提供了保障。
  • **YARN(Yet Another Resource Negotiator)**是统一资源管理和调度框架,它解决了Hadoop 1.0资源利用率低和不能兼容异构计算框架等多种问题,它提供了资源隔离方案和双调度器的实现,可在YARN上运行各种不同类型计算框架包括MapReduce、Spark、Storm、Tez等。
  • Hadoop MapReduce是一个分布式的、并行处理的编程模型,是针对Google MapReduce的开源实现(MapReduce: Simplified Data Processing on Large Clusters, 2004)。开发人员可以在不了解分布式系统底层设计原理和缺少并行应用开发经验的情况下,就能使用MapReduce计算框架 快速轻松地编写出分布式并行程序,完成对大规模数据集(大于1TB)的并行计算。MapReduce利用函数式编程思想,将复杂的、运行于大规模集群上的并行计算过程高度抽象为两个函数:Map和Reduce,其中Map是对可以并行处理的小数据集进行本地计算并输出中间结果,Reduce是对各个Map的输出结果进行汇总计算得到最终结果。
  • Spark是加州伯克利大学AMP实验室开发的新一代计算框架,对迭代计算很有优势,和MapReduce计算框架相比性能提升明显,并且都可以与YARN进行集成。
  • HBase是一个分布式的、面向列族的开源数据库,一般采用HDFS作为底层存储。HBase是针对Google Bigtable的开源实现(Bigtable: A Distributed Storage System for Structured Data, 2006),二者采用相同数据模型,具有强大的非结构化数据存储能力。HBase使用ZooKeeper进行管理,它保障查询速度的一个关键因素就是RowKey的设计是否合理。
  • ZooKeeper是Google Chubby的开源实现,是一个分布式的、开放源码的分布式应用程序协调框架,为大型分布式系统提供了高效且可靠的分布式协调服务,提供了诸如统一命名服务、配置管理、分布式锁等分布式基础服务,并广泛应用于大型分布式系统如Hadoop、HBase、Kafka等开源系统,例如HDFS NameNode HA自动切换、HBase高可用、Spark Standalone模式下Master HA机制都是通过ZooKeeper来实现的。
  • Hive是一个基于Hadoop的数据仓库工具,最早由Facebook开发并使用。Hive让不熟悉MapReduce的开发人员直接编写SQL语句来实现对大规模数据的统计分析操作,Hive可以将SQL语句转换为MapReduce作业,并提交到Hadoop集群上运行。Hive大大降低了学习门槛,同时也提升了开发效率。
  • Pig与Hive类似,也是对大型数据集进行分析和评估的工具,不过与Hive提供SQL接口不同的是,它提供了一种高层的、面向领域的抽象语言Pig Latin,和SQL相比,Pig Latin更加灵活,但学习成本稍高
  • Impala由Cloudera公司开发,提供了与存储在HDFS、HBase上的海量数据进行交互式查询的SQL接口,其优点是查询非常迅速,其性能大幅领先于Hive,Impala并没有基于MapReduce计算框架,这也是Impala可以大幅领先Hive的原因。
  • Mahout是一个机器学习和数据挖掘库,它包含许多实现,包括聚类、分类、推荐过滤等。
  • Flume是由Cloudera提供的一个高可用、高可靠、分布式的海量日志采集、聚合和传输的框架。Flume支持在日志系统中定制各类数据发送方,用于收集数据,同时,Flume提供对数据进行简单处理并写到各种数据接收方。
  • Sqoop是SQL to Hadoop的缩写,主要用于关系数据库和Hadoop之间的数据双向交换。可以借助Sqoop完成关系型数据库如MySQL、Oracle、PostgreSQL等到Hadoop生态系统中HDFS、HBase、Hive等的数据导入导出操作,整个导入导出过程都是由MapReduce计算框架实现,非常高效。Sqoop项目开始于2009年,最早是作为Hadoop的一个第三方模块存在,后来为了让使用者能够快速部署,也为了让开发人员能够更快速地迭代开发,Sqoop就独立成为一个Apache项目。
  • Kafka是一种高吞吐量的、分布式的发布订阅消息系统,可以处理消费者在网站中的所有动作流数据。Kafka最初由LinkedIn公司开发,于2010年贡献给Apache基金会,并于2012年成为Apache顶级开源项目,它采用Scala和Java语言编写,是一个分布式、支持分区的、多副本的、基于ZooKeeper协调的分布式消息系统,它适合应用于以下两大类别场景:构造实时流数据管道,在系统或应用之间可靠地获取数据;构建实时流式应用程序,对这些流数据进行转换。
  • Apache Ambari是一个基于Web的工具,支持Apache Hadoop集群的安装、部署、配置和管理,目前已支持大多数Hadoop组件,包括HDFS、MapReduce、Hive、Pig、HBase、ZooKeeper、Oozie、Sqoop等。Ambari由Hortonworks主导开发,具有Hadoop集群自动化安装、中心化管理、集群监控、报警等功能,使得安装集群从几天缩短在几小时以内,运维人员也从数十人降低到几人以内,极大的提高集群管理的效率。

hadoop体系架构

Hadoop集群采用主从架构(Master/Slave),NameNode与ResourceManager为Master,DataNode与NodeManager为Slaves,守护进程NameNode和DataNode负责完成HDFS的工作,守护进程ResourceManager和NodeManager则负责完成YARN的工作

部署和运行hadoop

运行环境

  • 操作系统
    Hadoop运行平台支持以下两种:
    (1)Windows:Hadoop支持Windows
    (2)GNU/Linux:Hadoop的最佳运行环境无疑是开源操作系统Linux,Linux的发行版本众多,常见的有CentOS、Ubuntu、Red Hat、Debian、Fedora、SUSE、openSUSE等。
  • Java环境
    Hadoop使用Java语言编写,因此它的运行环境需要Java环境的支持。Hadoop 3.x需要Java 8,Hadoop 2.7及以后版本需要Java 7或Java 8,Hadoop 2.6及早期版本需要Java 6。
  • SSH
    Hadoop集群若想运行,其运行平台Linux必须安装SSH,且sshd服务必须运行,只有这样,才能使用Hadoop脚本管理远程Hadoop守护进程

运行模式

  • 单机模式(Local/Standalone Mode)
    只在一台计算机上运行,不需任何配置,在这种模式下,Hadoop所有的守护进程都变成了一个Java进程,存储采用本地文件系统,没有采用分布式文件系统HDFS。
  • 伪分布模式(Pseudo-Distributed Mode)
    只在一台计算机上运行,在这种模式下,Hadoop所有守护进程都运行在一个节点上,在一个节点上模拟了一个具有Hadoop完整功能的微型集群,存储采用分布式文件系统HDFS,但是HDFS的名称节点和数据节点都位于同一台计算机上。
  • 全分布模式(Fully-Distributed Mode)
    在多台计算机上运行,在这种模式下,Hadoop的守护进程运行在多个节点上,形成一个真正意义上的集群,存储采用分布式文件系统HDFS,且HDFS的名称节点和数据节点位于不同计算机上。

简述过程

理解部署Hadoop集群所需系统环境、Hadoop运行模式,熟练掌握在Linux下部署全分布模式Hadoop过程:

规划集群、

准备机器及软件环境(配置静态IP、修改主机名、编辑域名映射、安装和配置Java、安装和配置SSH免密登录)、安装和配置Hadoop集群( hadoop-env.sh 、 yarn-env.sh、 mapred-env.sh、 core-site.xml、 hdfs-site.xml、 yarn-site.xml、 mapred-site.xml、slaves )、

关闭防火墙、格式化文件系统、启动和验证Hadoop、关闭Hadoop。

分布式文件系统HDFS

HDFS简介

​ HDFS(Hadoop Distributed File System)是Hadoop分布式文件系统,是Hadoop三大核心之一,是针对谷歌文件系统GFS(Google File System)的开源实现(The Google File System, 2003)。HDFS是一个具有高容错性的文件系统,适合部署在廉价的机器上,HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。大数据处理框架如MapReduce、Spark等要处理的数据源大部分都存储在HDFS上,Hive、HBase等框架的数据通常也存储在HDFS上。简而言之,HDFS为大数据的存储提供了保障。经过多年的发展,HDFS自身已经十分成熟和稳定,且用户群愈加广泛,HDFS逐渐成为分布式存储的事实标准。

HDFS文件系统的基本特征包括以下几个方面

  • 高容错性:把硬件出错看做一种常态,HDFS设计了相应机制能够快速自动进行错误检测和恢复。例如,一个节点出现故障,它上面的数据在其它节点存在备份,并且会被自动补充。
  • 数据容量大:HDFS每个集群可以支持数百个节点,以支持应用程序的大数据需求。
  • 可扩展性:水平扩展性强,数据节点可以根据需要进行增删。
  • 高吞吐量:数据传输速率高,支持高并发大数据应用程序。
  • 就近计算:客户请求尽可能在数据节点上直接完成计算任务,这样在大数据的业务中可以降低网络负担,增加吞吐量。

HDFS体系结构

​ HDFS采用Master/Slave架构模型,一个HDFS集群包括一个NameNode和多个DataNode。名称节点NameNode为主节点,数据节点DataNode为从节点,文件被划分为一系列的数据块(Block)存储在从节点DataNode上,NameNode是中心服务器,不存储数据,负责管理文件系统的命名空间(Namespace)以及客户端对文件的访问。

Namenode

​ NameNode运行在日常硬件上,通常只有一个,是整个文件系统的管理节点。它维护着整个文件系统的文件目录树,包括文件/目录的元数据和每个文件对应的数据块列表,它负责接收用户的操作请求。作为HDFS主服务节点的核心,它主要完成下面任务

  • 管理命名空间(Namespace)。
  • 控制客户端对文件的读写。
  • 执行常见文件系统操作,比如文件的重命名、复制、移动、打开、关闭以及目录操作。

​ HDFS命名空间(NameSpace)支持传统的层次型文件组织结构,与大多数现有文件系统类似,用户可以创建、删除、移动或重命名文件。

​ 在HDFS中,NameNode负责管理分布式文件系统的命名空间,保存了两个核心数据结构:FsImage和EditLog。其中,FsImage用于维护文件系统树以及文件树中所有文件和目录的元数据;操作日志文件EditLog记录了所有针对文件的创建、删除、重命名等操作。NameNode记录了每个文件中各个块所在的数据节点的位置信息,但是并不持久化存储这些信息,而是在系统每次启动时扫描所有数据节点重构得到这些信息。

DataNode

​ DataNode也运行在日常硬件上,通常有多个,它为HDFS提供真实文件数据的**存储服务。**HDFS数据存储在DataNode上,数据块的创建、复制和删除都在DataNode上执行。DataNode将HDFS数据以文件的形式存储在本地的文件系统中,但并不知道有关HDFS文件的信息。DataNode把每个HDFS数据块存储在本地文件系统的一个单独的文件中,并不在同一个目录创建所有的文件,实际上,它用试探的方法来确定每个目录的最佳文件数目,并且在适当的时候创建子目录。在同一个目录中创建所有的本地文件并不是最优的选择,这是因为本地文件系统可能无法高效地在单个目录中支持大量的文件。当一个DataNode启动时,它会扫描本地文件系统,产生一个这些本地文件对应的所有HDFS数据块的列表,然后作为报告发送到NameNode,这个报告就是块状态报告。

客户端

​ 严格地说,客户端并不算是HDFS的一部分。客户端可以支持打开、读取、写入等常见操作,并且提供了类似Shell的命令行方式来访问HDFS中的数据,也提供了API作为应用程序访问文件系统的客户端编程接口

HDFS文件存储机制

Block

​ HDFS中的数据以文件块Block的形式存储,Block是最基本的存储单位,每次读写的最小单元是一个Block。对于文件内容而言,一个文件的长度大小是N,那么从文件的0偏移开始,按照固定的大小,顺序对文件进行划分并编号,划分好的每一个块称一个Block。
​ Hadoop 2.0中默认Block大小是128MB。不同于普通文件系统,HDFS中如果一个文件小于一个数据块的大小,并不占用整个数据块存储空间。
​ Block的大小可以根据实际需求进行配置,可以通过HDFS配置文件hdfs-site.xml中的参数dfs.blocksize来定义块大小,但要注意,数字必须是2K,文件的大小可以不是Block大小的整数倍,这时最后一个块可能存在剩余。

为什么HDFS数据块设置的这么大呢?
原因是和普通的本地磁盘文件系统不同,HDFS存储的是大数据文件,通常会有TB甚至PB的数据文件需要管理,所以数据的基本单元必须足够大才能提高管理效率。而如果还使用像Linux本地文件系统EXT3的4KB单元来管理数据,则会非常低效,同时会浪费大量的元数据空间。

Block副本管理策略

  • HDFS采用多副本方式对数据进行冗余存储,通常一个数据块的多个副本会被分布到不同的DataNode上。
  • HDFS提供可靠的算法实现在分布式环境中存储大量数据。简单来说,每个数据块Block都存在副本以提高容错性。默认情况下每个块存在3个副本。数据块的信息会定期由DataNode报送给NameNode,任何时候,当NameNode发现一个块的副本个数少于3个或者多于3个时都会进行补充或者删除。
  • 副本放置的基本原则是保证并非所有的副本都在同一个机架(Rack)上。这样放置的好处在于提供高容错性的同时降低延时,注意一个Rack可能包含多个DataNode,而数据分布在不同DataNode可以提高数据读写并发。对于多于3个副本的情况,其它副本将会随机分布在不同DataNode,同时保证同一个机架中最多存在两个副本。
  • 可以通过配置文件hdfs-site.xml中的参数dfs.replication来定义Block副本数。

ABC是机架,123是文件

A:112

B:223

C:331

HDFS数据读写过程

数据读取过程

客户端读取HDFS上的文件时,需要调用HDFS Java API一些类的方法,从编程角度来看,主要经过以下几个步骤。
(1)客户端生成一个FileSystem实例(DistributedFileSystem对象),并使用此实例的open()方法打开HDFS上的一个文件。
(2)DistributedFileSystem通过RPC调用向NameNode发出请求,得到文件的位置信息,即数据块编号和所在DataNode地址,对于每一个数据块,元数据节点返回保存数据块的数据节点的地址,通常按照DataNode地址与客户端的距离从近到远排序。
(3)FileSystem实例获得地址信息后,生成一个FSDataInputStream对象实例返回给客户端,此实例封装了一个DFSInputStream对象,负责存储数据块信息和DataNode地址信息,并负责后续的文件内容读取工作。
(4)客户端向FSDataInputStream发出读取数据的read()调用。
(5)FSDataInputStream收到read()调用请求后,FSDataInputStream封装的DFSInputStream选择与第一个数据块最近的DataNode,并读取相应的数据信息返回给客户端,在数据块读取完成后,DFSInputStream负责关闭到相应DataNode的链接。
(6)DFSInputStream依次选择后续数据块的最近DataNode节点,并读取数据返回给客户端,直到最后一个数据块读取完毕。DFSInputStream从DataNode读取数据时,可能会碰上某个DataNode失效的情况,则会自动选择下一个包含此数据块的最近的DataNode去读取。
(7)客户端读取完所有数据块,然后调用FSDataInputStream的close()方法关闭文件。

数据写入过程

​ HDFS的设计遵循**“一次写入,多次读取”**的原则,所有数据只能添加不能更新。数据会被划分为等尺寸的块写入不同的DataNode中。每个块通常保存指定数量的副本(默认3个)。HDFS数据写入基本过程为:客户端向NameNode发送文件写请求,NameNode给客户分配写权限,并随机分配块的写入地址——DataNode的IP,兼顾副本数量和块Rack自适应算法,例如副本因子是3,则每个块会分配到三个不同的DataNode,为了提高传输效率,客户端只会向其中一个DataNode复制一个副本,另外两个副本则由DataNode传输到相邻DataNode。

数据写入过程
从编程角度来说,将数据写入HDFS主要经过以下几个步骤。
(1)创建和初始化FileSystem,客户端调用create()来创建文件
(2)FileSystem用RPC调用元数据节点,在文件系统的命名空间中创建一个新的文件,元数据节点首先确定文件原来不存在,并且客户端有创建文件的权限,然后创建新文件。
(3)FileSystem返回DFSOutputStream,客户端开始写入数据。
(4)DFSOutputStream将数据分成块,写入data queue。data queue由Data Streamer读取,并通知名称节点分配数据节点,用来存储数据块(每块默认复制3块)。分配的数据节点放在一个数据流管道(pipeline)里。Data Streamer将数据块写入pipeline中的第一个数据节点,一个数据节点将数据块发送给第二个数据节点,第二个数据节点将数据发送给第三个数据节点。
(5)DFSOutputStream为发出去的数据块保存了ack queue,等待pipeline中的数据节点告知数据已经写入成功。
(6)当客户端结束写入数据,则调用stream的close函数。此操作将所有的数据块写入pipeline中的数据节点,并等待ack queue返回成功。
(7)通知名称节点写入完毕。

实战HDFS

为了方便用户使用HDFS,HDFS提供三种类型接口:
1.HDFS Web UI
2.HDFS Shell
3.HDFS API

  • HDFS Web UI:HDFS Web UI主要面向管理员,提供服务器基础统计信息和文件系统运行状态的查看功能,不支持配置更改操作。从该页面上,管理员可以查看当前文件系统中各个节点的分布信息,浏览名称节点上的存储、登录等日志,以及下载某个数据节点上某个文件的内容。
    HDFS Web UI地址为http://NameNodeIP:50070,进入后可以看到当前HDFS文件系统的Overview、Summary、NameNode Journal Status、NameNode Storage等信息。
  • HDFS Shell:在Linux命令行终端,使用Shell命令对HDFS进行操作,可以完成HDFS中文件的上传、下载、复制、查看、格式化名称节点等操作。
  • HDFS java API:HDFS使用Java语言编写,所以提供了丰富了Java编程接口供开发人员调用,当然HDFS同时支持其它语言如C++、Python等编程接口,但它们都没有Java接口方便。凡是使用Shell命令可以完成的功能,都可以使用相应Java API来实现,甚至使用API可以完成Shell命令不支持的功能。

HDFS高可靠性机制

  • 作为分布式存储系统,HDFS设计和实现了多种机制来保证高可靠性。高可靠性的主要目标之一就是即使在系统出错的情况下也要保证数据存储的正常。常见的三种出错情况是:NameNode出错、DataNode出错和数据出错。
  • NameNode是HDFS集群中的单点故障所在。如果Namenode节点出现故障,是需要手工干预的。
  • HDFS通过心跳(heartbeat)来检测发现DataNode是否出错。
  • 网络传输和磁盘错误等因素都会造成数据错误。

元数据备份

​ 在服务器系统中,发生硬件故障或者软件错误是难以避免的,所以需要对重要数据进行备份。元数据是HDFS的核心数据,通过它对整个HDFS进行管理,FsImage和EditLog是最重要的元数据文件。所以,NameNode通常会配置支持维护多个FsImage和EditLog的副本。任何对FsImage或EditLog的修改,都将同步到它们的副本上。这种多副本的同步操作可能会降低NameNode每秒处理的名字空间事务数量,然而这个代价是可以接受的,因为即使HDFS的应用是数据密集的,它们不一定是元数据密集的。当NameNode重启时,它会选取最近的完整的FsImage和EditLog来使用。

Secondary NameNode

  • HDFS中除了名称节点NameNode外,还有一个辅助NameNode,称为第二名称节点Secondary NameNode。从名称上看,Secondary NameNode似乎是作为NameNode的备份而存在的,事实上并非如此。Secondary NameNode有它自身的独立的角色和功能,通常认为它和NameNode是协同工作的。
  • Secondary NameNode主要有如下特征和功能:是HDFS高可用性的一个解决方案,但不支持热备,使用前配置即可;定期对NameNode中内存元数据进行更新和备份;默认安装在与NameNode相同的节点,但是建议安装在不同节点以提高可靠性。

当NameNode运行很长时间后,EditLog文件会变得很大。在这种情况下就会出现以下问题:

  • EditLog文件会变的很大,如何去管理这个文件?
  • NameNode的重启会花费很长时间,因为有很多改动要合并到FsImage文件上;
  • 如果NameNode宕机,那就丢失了很多改动,因为此时的FsImage文件时间戳比较旧。

为了克服这些问题,我们需要一个易于管理的机制来帮助减小EditLog文件的大小和得到一个最新的FsImage文件,这样也会减小NameNode的计算压力。而Secondary NameNode就是为了帮助解决上述问题提出的,它的主要职责是合并NameNode的EditLog到FsImage文件中,即对元数据进行定期更新和备份

Backup Node备份

​ Hadoop 2.0以后版本新提供了一个真正意义上的备用节点,即Backup Node。Backup Node在内存中维护了一份从NameNode同步过来的FsImage,同时它还从NameNode接收EditLog文件的日志流,并把它们持久化到硬盘。Backup Node在内存中维护与NameNode一样的元数据。

​ Backup Node的启动命令是“hdfs namenode -backup”。在配置文件hdfs-site.xml中进行设置,主要包括两个参数:dfs.backup.address、dfs.backup.http.address。

HDFS NameNode HA高可用机制

HDFS NameNode HA概述
  • 在HDFS中,NameNode管理整个HDFS文件系统的元数据信息,具有举足轻重的作用,NameNode的可用性直接决定了整个Hadoop的可用性,因此,NameNode绝对不允许出现故障。
  • 在Hadoop 1.0时代,NameNode存在单点故障问题,一旦NameNode进程不能正常工作,就会造成整个HDFS也无法使用,而Hive或HBase等的数据也都存放在HDFS上,因此Hive或HBase等框架也将无法使用,这可能导致生产集群上的很多框架都无法正常使用,而通过重启NameNode来进行数据恢复十分耗时。
  • 在Hadoop 2.0中,HDFS NameNode的单点故障问题得到了解决,这就是HDFS NameNode High Availability(HDFS NameNode高可用机制,简称HDFS NameNode HA)。

HDFS NameNode HA体系架构

  • ZooKeeper集群
    ZooKeeper集群的作用是为主备故障恢复控制器提供主备选举支持。
  • 主备名称节点(Active/Standby NameNode)
    典型高可用集群中,两个独立的机器作为NameNode,任何时刻,只有一个NameNode处于活跃(Active)状态,另一个处于待命(Standby)状态。活跃名称节点负责所有客户端操作,而待命名称节点只能简单地充当Slave,负责维护状态信息以便在需要时能快速切换。
  • 主备切换控制器(Active/Standby ZKFailoverController)
    ZKFailoverController作为独立的进程运行,对NameNode的主备切换进行总体控制。ZKFailoverController能及时检测到NameNode的健康状况,在主NameNode发生故障时借助ZooKeeper实现自动的主备选举和切换,当然,NameNode目前也支持不依赖于ZooKeeper的手动主备切换。
  • 共享存储系统(JournalNode集群)
    为了让活跃名称节点和待命名称节点保持状态同步,它们两个都要与称为“Journal Node”的一组独立的进程通信。活跃名称节点对命名空间所做的任何修改,都将修改日志(EditLog)发送给JournalNode,待命名称节点能够从JournalNode中读取修改日志,并且时时监控它们对修改日志的修改。待命名称节点获取edit后,将它们应用到自己的命名空间。故障切换时,待命名称节点(Standby)在提升自己为活跃(Active)状态前已经从Journal Node中读完了所有的修改日志(EditLog),这就确保了故障切换发生前两个NameNode命名空间的状态时完全同步的。
  • DataNode
    除了通过共享存储系统共享HDFS的元数据信息外,主NameNode和备NameNode还需要共享HDFS的数据块和DataNode之间的映射关系。DataNode会同时向主NameNode和备NameNode上报数据块的位置信息。
HDFS NameNode主备切换实现原理

​ 在HDFS NameNode的高可用体系架构中,NameNode的主备切换主要由故障恢复控制器(ZKFailoverController)、健康监视器(HealthMonitor)和主备选举器(ActiveStandbyElector)这3个组件来协同实现。

HDFS NameNode主备切换实现原理
  • ZKFailoverController作为NameNode机器上的一个独立进程启动(进程名为ZKFC),启动的时候会创建HealthMonitor和ActiveStandbyElector这两个主要的内部组件。ZKFailoverController在创建HealthMonitor和ActiveStandbyElector的同时,也会向HealthMonitor和ActiveStandbyElector注册相应的回调方法。
  • HealthMonitor主要负责检测NameNode的健康状态,如果检测到NameNode的健康状态发生变化,会回调ZKFailoverController的相应方法进行自动的主备选举。
  • ActiveStandbyElector主要负责完成自动的主备选举,内部封装了ZooKeeper的处理逻辑,一旦ZooKeeper主备选举完成,会回调ZKFailoverController的相应方法来进行NameNode的主备状态切换
HDFS NameNode Federation联邦机制

HDFS Federation概述

​ Hadoop集群的元数据信息是存放在NameNode的内存中的,当集群扩大到一定规模后,NameNode内存中存放的元数据信息可能会非常大。由于HDFS所有操作都会和NameNode进行交互,当集群很大时,NameNode的内存限制将成为制约集群横向扩展的瓶颈。在Hadoop 2.0诞生之前,HDFS中只能有一个命名空间,对于HFDS中的文件没有办法完成隔离。正因为如此,在Hadoop 2.0中引入了HDFS Federation联邦机制,可以解决如下问题。

(1)集群扩展性。
(2)性能更高效。
(3)良好隔离性。

HDFS数据管理体系架构

​ HDFS的数据存储采用两层分层结构,分别为命名空间(Namespace)和块存储服务(Block Storage Service)。其中命名空间由目录、文件、块组成,支持创建、删除、修改、列举命名空间等相关操作。块存储服务包括两部分,分别是块管理Block Management和存储Storage,块管理在NameNode中完成,通过控制注册和阶段性心跳来保证DataNode正常运行,可以处理块的信息报告和维护块的位置信息,可以创建、修改、删除、查询块,还可以管理副本和副本位置;存储在数据节点DataNode上,提供对块的读写操作。

HDFS Federation体系架构
  • HDFS Federation环境下,各个NameNode相互独立,各自分工管理自己的命名空间,且不需要互相协调,一个NameNode发生故障不会影响其他的NameNode。DataNode被用作通用的数据存储设备,每个DataNode要向集群中所有的NameNode注册,且周期性地向所有NameNode发送心跳和报告,并执行来自所有NameNode的命令。
  • 一个Block Pool由属于同一个Namespace的数据块组成,每个DataNode可能会存储集群中所有Block Pool的数据块,每个块池内部自治,各自管理各自的Block,不会与其他块池交流。每个NameNode维护一个命名空间卷,由命名Namespace和块池组成,它是管理的基本单位。当一个Namespace被删除后,所有DataNode上与其对应的块池也会被删除。当集群升级时,每个命名空间卷作为一个基本单元进行升级。
HDFS Snapshots快照机制

HDFS Snapshots概述
HDFS快照是文件系统在某一时刻的只读镜像,可以是一个完整的文件系统,也可以是某个目录的镜像。快照分两种:一种是建立文件系统的索引,每次更新文件不会真正的改变文件,而是新开辟一个空间用来保存更改的文件;另一种是拷贝所有的文件系统。HDFS快照属于前者。

​ HDFS快照常用于以下场景:
(1)防止用户的错误操作。
(2)备份。
(3)试验/测试。
(4)灾难恢复。
​ 通过HDFS快照机制可以定时或按固定时间间隔创建文件快照,并删除过期的文件快照,减少业务误操作造成的数据损失。快照的操作远低于外部备份的开销,可以作为备份HDFS最常用的方式。

分布式计算框架MapReduce

MapReduce简介

  • MapReduce是Hadoop生态中的一款分布式运算框架,它提供了非常完善的分布式架构,可以让不熟悉分布式计算的人员也能编写出优秀的分布式系统,因此可以让开发人员将精力专注到业务逻辑本身。
  • MapReduce采用“**分而治之”**的核心思想,可以先将一个大型任务拆分成若干个简单的子任务,然后将每个子任务交给一个独立的节点去处理。当所有节点的子任务都处理完毕后,再汇总所有子任务的处理结果,从而形成最终的结果。
  • MapReduce在发展史上经过一次重大改变,旧版MapReduce(MapReduce 1.0)采用的是典型的Master/Slave结构,Master表现为JobTracker进程,而Slave表现为TaskTracker。但是这种结果过于简单,例如Master的任务过于集中,并且存在单点故障等问题。因此,MapReduce进行了一次重要的升级,舍弃JobTracker和TaskTracker,而改用了ResourceManager进程负责处理资源,并且使用ApplicationMaster进程管理各个具体的应用,用NodeManager进程对各个节点的工作情况进行监听。升级后的MapReduce称为MapReduce 2.0,但也许由于“MapReduce”这个词已使用太久,有些参考资料中经常使用“MapReduce”来代指YARN
  • 过程简介:如果要统计一个拥有海量单词的词库,就可以先将整个词库拆分成若干个小词库(map),然后将各个小词库发送给不同的节点去计算(map),当所有节点将分配给自己的小词库中的单词统计完毕后,再将各个节点的统计结果进行汇总(reduce),形成最终的统计结果。

MapReduce计算模型

  • MapReduce将复杂的、运行于大规模集群上的并行计算过程高度地抽象到了两个函数:Map和Reduce。
  • MapReduce框架会为每个Map任务输入一个数据子集(split),Map任务生成的结果会继续作为Reduce任务的输入,最终由Reduce任务输出最后结果,并写入分布式文件系统。
  • 优点:编程容易,不需要掌握分布式并行编程细节,也可以很容易把自己的程序运行在分布式系统上,完成海量数据的计算。

Map和Reduce函数

键值对:

Step1 将数据抽象为键值对形式,接着map函数会以键值对作为输入,经过map函数的处理,产生一系列新的键值对作为中间结果输出到本地。

Step2 MapReduce框架自动将这些中间结果数据按照键做聚合处理,并将键相同的数据分发给reduce函数处理。

Step3 reduce函数以键和对应的值的集合作为输入,经过reduce函数处理后,产生另外一系列键值对作为最终输出。

{key1,value1}→{key2,List}→{key3,value3}

第一个MapReduce案例:WordCount

​ Hadoop提供了一个MapReduce入门案例“WordCount”,用于统计输入文件中每个单词出现的次数。该案例源码保存在$HADOOP_HOME/share/hadoop/mapreduce/sources/hadoop-mapreduce-examples-2.9.2.jar的WordCount.java中,其源码共分为TokenizerMapper类、IntSumReducer类和main()函数三个部分。

TokenizerMapper类

​ 从类名可知,该类是Map阶段的实现。并且能够发现,在MapReduce中Map阶段的业务代码需要继承自org.apache.hadoop.mapreduce.Mapper类。Mapper类的四个泛型分别表示输入数据的key类型、输入数据的value类型、输出数据的key类型、输出数据的value类型。

​ 以本次“WordCount”为例,每次Map阶段需要处理的数据是文件中的一行数据,而默认情况下这一行数据的偏移量(该行起始位置距离文件初始位置的位移)就是输入数据的key类型(一般而言,偏移量是一个长整型,也可以写成本例中使用的Object类型);输入数据的value类型就是这行数据本身,因此是Text类型(即hadoop中定义的字符串类型);输出数据的key类型是每个单词本身,因此也是Text类型;而输出数据的value类型,就表示该单词出现了一次,因此就是数字1,可以表示为IntWritable类型(即Hadoop中定义的整数类型)。

向Hadoop集群提交并运行WordCount

​ 使用如下命令向Hadoop集群提交并运行WordCount。
hadoop jar /usr/local/hadoop-2.9.2/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.9.2.jar wordcount /InputDataTest /OutputDataTest3
​ 上述命令中,/InputDataTest表示输入目录,/OutputDataTest3表示输出目录。执行该命令前,假设HDFS的目录/InputDataTest下已存在待分析词频的3个文件,而输出目录/OutputDataTest3不存在,在执行过程中会自动创建

MapReduce作业执行流程

  1. InputFormat:InputFormat模块首先对输入数据做预处理,比如验证输入格式是否符合输入定义;然后将输入文件切分为逻辑上的多个InputSplit,InputSplit是MapReduce对文件进行处理和运算的输入单位,并没有对文件进行实际切割;由于InputSplit是逻辑切分而非物理切分,所以还需要通过RecordReader(图4-4中的RR)根据InputSplit中的信息来处理InputSplit中的具体记录,加载数据并转换为适合Map任务读取的键值对,输入给Map任务。
  2. Map:Map模块会根据用户自定义的映射规则,输出一系列的作为中间结果。
  3. Shuffle:为了让Reduce可以并行处理Map的结果,需要对Map的输出进行一定的排序、分区、合并、归并等操作,得到形式的中间结果,再交给对应的Reduce进行处理,这个过程叫做Shuffle。
  4. Reduce:Reduce以一系列的中间结果作为输入,执行用户定义的逻辑,输出形式的结果给OutputFormat。
  5. OutputFormat:OutputFormat模块会验证输出目录是否已经存在以及输出结果类型是否符合配置文件中的配置类型,如果都满足,就输出Reduce的结果到分布式文件系统。

作业执行时架构

  • ResourceManager称为资源管理器,负责集中管理MapReduce执行作业时所有参与运算的全部计算机的资源(例如,统一管理所有计算机的CPU、内存、硬盘等),并将这些资源按需分配给各个节点。
  • ApplicationMaster称为应用管理器,负责管理某一个具体应用的调度工作。例如,某一次MapReduce作业可能被划分为了若干个小应用,而每一个应用就对应着由一个ApplicationMaster全权管理。
  • Container称为容器,ResourceManager分配给各个节点的资源会被存入独立存入Container中运行。也就是说,Container就是YARN提供的一套隔离资源的容器。每个容器内都拥有一套可独立运行的资源环境(CPU、内存以及当前需要执行的任务文件等)
  • NodeManager称为节点监视器,它会向ApplicationMaster汇报当前节点的资源使用情况,以及所管理的所有Container的运行状态。此外,它还会接收并处理ApplicationMaster发来的启动任务、停止任务等请求。

MapReduce数据类型与格式

MapReduce使用了Text定义字符串,使用了IntWritable定义整型变量,而没有使用Java内置的String和int类型。这样做主要是有两个方面的原因:

  1. MapReduce是集群运算,因此必然会在执行期间进行网络传输,然而在网络中传输的数据必须是可序列化的类型。
  2. 为了良好地匹配MapReduce内部的运行机制,MapReduce就专门设计了一套数据类型。

Shuffle机制

Map阶段的产物会经过一个名为Shuffle的阶段。

在Shuffle阶段中进行排序和分区操作,Shuffle阶段的产物是以“key=单词,value=出现次数的数组”形式输出。

在Shuffle阶段,会对数据进行以下操作。

  1. 首先,Shuffle会持续接收Map阶段发来的数据,并将数据写入到一个“环形缓冲区”中,当缓冲区被填满时就会将覆盖掉的部分数据溢出存放到“溢出文件”中。
  2. 其次,Shuffle会对溢出文件中的数据进行排序(Sort),然后再将排序后的数据进行分区(Partition),例如,将字母A-字母K开头的放在0个分区,将字母L-字母Q开头的放在第1个分区……
  3. 同样Shuffle会生成很多个排序且分区后的溢出文件,最后,会将所有溢出文件中相同分区号的内容进行合并(Combine),形成本Map阶段最终的第0区内容、第1区内容……
  4. 与此同时,其他Map阶段也会生成当前Map最终的第0区内容、第1区内容……最后Shuffle会对所有Map阶段相同分区号的内容再次进行合并,从而形成最终的第0区内容、第1区内容……最后不同区号中的内容就会发送到不同的Reduce中进行处理。

实战MapReduce

MapReduce Web UI

​ MapReduce Web接口面向管理员。可以在页面上看到已经完成的所有MR-App执行过程中的统计信息,该页面只支持读,不支持写。在MapReduce程序运行时,我们除了观察控制台打印的日志以外,还可以通过Web界面查看具体的运行情况。MapReduce Web UI的默认地址为http://JobHistoryServerIP:19888,可以查看MapReduce的历史运行情况。

MapReduce Shell

​ MapReduce Shell接口面向MapReduce程序员。程序员通过Shell接口能够向YARN集群提交MR-App,查看正在运行的MR-App,甚至可以终止正在运行的MR-App。

​ MapReduce Shell命令统一入口为:mapred,语法格式如下:
mapred [–config confdir] [–loglevel loglevel] COMMAND

​ 若$HADOOP_HOME/bin未加入到系统环境变量PATH中,则需要切换到Hadoop安装目录下,输入“bin/mapred”

MapReduce Java API编程

  • MapReduce Java API接口面向Java开发工程师。程序员可以通过该接口编写MR-App用户层代码
  • MRApplicationBusinessLogic。基于YARN编写的MR-App和基于MapReduce 1.0编写的MR-App编程步骤相同。
    MR-App称为MapReduce应用程序,标准YARN-App包含3部分:MRv2框架中的MRAppMaster、MRClient,加上用户编写的MRApplicationBusinessLogic(Mapper类和Reduce类),合称为MR-App。MR-App编写步骤如下所示:
    (1)编写MRApplicationBusinessLogic。自行编写。
    (2)编写MRApplicationMaster。无需编写,Hadoop开发人员已编写好MRAppMaster.java。
    (3)编写MRApplicationClient。无需编写,Hadoop开发人员已编写好YARNRunner.java。

其中,MRApplBusinessLogic编写步骤如下:
(1)确定对。
(2)定制输入格式。
(3)Mapper阶段。
(4)Reducer阶段。
(5)定制输出格式。
编写类后,main方法里,按下述过程依次指向各类即可:
(1)实例化配置文件类。
(2)实例化Job类。
(3)指向InputFormat类。
(4)指向Mapper类。
(5)指向Partitioner类。
(6)指向Reducer类。
(7)指向OutputFormat类。
(8)提交任务。

统一资源管理和调度框架YARN

初识YARN

​ 针对MapReduce 1.0在可用性、可扩展性、资源利用率、框架支持等方面的不足,对MapReduce 1.0的架构进行了重新设计,提出了全新的资源管理和调度框架YARN。YARN是Hadoop 2.0的资源管理和调度框架,是一个通用的资源管理系统,在其上可以部署各种计算框架,它可为上层应用提供统一的资源管理和调度,它的引入为集群高可用性、可扩展性、资源利用率和数据共享等方面带来了很大好处。

​ 在Hadoop 1.0中,MapReduce采用Master/Slave架构,有两类守护进程控制作业的执行过程,即一个JobTracker多个TaskTracker。JobTracker负责资源管理和作业调度;TaskTracker定期向JobTracker汇报本节点的健康状况、资源使用情况、任务执行情况以及接受来自JobTracker的命令并执行。随着集群规模负载的增加,MapReduce JobTracker在内存消耗、扩展性、可靠性、性能等方面暴露出各种缺点,具体包括以下几个方面。
(1)单点故障问题。JobTracker只有一个,它负责所有MapReduce作业的调度,若这个唯一的JobTracker出现故障就会导致整个集群不可用。
(2)可扩展性瓶颈。业内普遍总结出当节点数达到4000,任务数达到40000时,MapReduce 1.0会遇到可扩展性瓶颈,这是由于JobTracker“大包大揽”任务过重,既要负责作业的调度和失败恢复,又要负责资源的管理分配。当执行过多的任务时,需要巨大的内存开销,这也潜在增加了JobTracker失败的风险。
(3)资源划分不合理。资源(CPU、内存)被强制等量划分为多个Slot,每个TaskTracker都配置有若干固定长度的Slot,这些Slot是静态分配的,在配置的时候就被划分为Map Slot和Reduce Slot,且Map Slot仅能用于运行一个Map任务,Reduce Slot仅能用于运行一个Reduce任务,彼此之间不能使用分配给对方的Slot。这意味着,当集群中只存在单一Map任务或Reduce任务时,会造成资源的极大浪费。
(4)仅支持MapReduce一个计算框架。MapReduce是一个基于Map和Reduce、适合批处理、基于磁盘的计算框架,不能解决所有场景问题,而一个集群仅支持一个计算框架,不支持其他类型的计算框架如Spark、Storm等,造成集群多、管理复杂,且各个集群不能共享资源,造成集群间资源浪费。

为了解决MapReduce 1.0存在的问题,Hadoop 2.0以后版本对其核心子项目MapReduce的体系架构进行了重新设计,生成了MRv2和YARN。

Apache Hadoop YARN(Yet Another Resource Negotiator,另一种资源协调者)是Hadoop 2.0的资源管理和调度框架,是一个通用资源管理系统,可为上层应用提供统一的资源管理和调度,它的引入为集群在利用率、资源统一管理和数据共享等方面带来了很大好处

YARN设计的基本思路就是“放权”,即不让JobTracker承担过多功能,把MapReduce 1.0中JobTracker三大功能资源管理、任务调度和任务监控进行拆分,分别交给不同的新组件承担。重新设计后得到的YARN包括ResourceManager、ApplicationMaster和NodeManager,其中,ResourceManager负责资源管理,ApplicationMaster负责任务调度和任务监控,NodeManager负责承担原TaskTracker功能,且原资源被划分的Slot重新设计为容器Container,NodeManager能够启动和监控容器Container。另外,原JobTracker也负责存储已完成作业的作业历史,此功能也可以运行一个作业历史服务器作为一个独立守护进程来取代JobTracker,YARN中与之等价的角色是时间轴服务器Timeline Server。

MapReduce 1.0 YARN
JobTracker ResourceManager、ApplicationMaster、Timeline Server
TaskTracker NodeManager
Slot Container

总之,Hadoop 1.0中,MapReduce既是一个计算框架,又是一个资源管理和调度框架,到了Hadoop 2.0以后,MapReduce中资源管理和调度功能被单独分割出来形成YARNYARN是一个纯粹的资源管理调度框架,而被剥离了资源管理调度功能的MapReduce变成了MRv2,MRv2是运行在YARN上的一个纯粹的计算框架。

从MapReduce 1.0发展到YARN,客户端并没有发生变化,其大部分API及接口都保持兼容,因此,原来针对Hadoop 1.0开发的代码不需做大的改动,就可以直接放在Hadoop 2.0平台上运行。

YARN与MapReduce 1.0相比优势

(1)可扩展性(Scalability)
与MapReduce 1.0相比,YARN可以在更大规模的集群上运行。当节点数达到4000、任务数达到40000时,MapReduce 1.0会遇到可扩展性瓶颈,瓶颈来源于JobTracker负载过重,既要负责作业的调度和失败恢复,又要负责资源的管理分配。YARN利用ResourceManager和ApplicationMaster分离的架构优点克服了这个局限性,可以扩展到将近10000个节点和100000个任务。另外,YARN Federation联邦机制进一步增强了集群水平横向扩展性。
(2)可用性(Availability)
当守护进程失败时,通常需要另一个守护进程复制接管工作所需的状态以便其继续提供服务,从而可以获得高可用性(High Available)。但是,由于MapReduce 1.0中JobTracker内存中存在大量快速变化的复杂状态,使得改进JobTracker以使其获得高可用性非常困难。
YARN对MapReduce 1.0的体系架构进行了重新设计,ResourceManager和ApplicationMaster分别承担MapReduce 1.0中JobTracker的功能,那么高可用的服务随之称为一个分而治之的问题:先为ResourceManager提供高可用性,再为YARN应用提供高可用性。YARN的ResourceManager HA特性通过Active/Standby ResourceManager保证了YARN高可用性,ResourceManager Restart特性保证了若ResourceManager发生单点故障,ResourceManager能尽快自动重启。
(3)利用率(Utilization)
MapReduce 1.0使用Slot表示各个节点上的计算资源,Slot分为Map Slot和Reduce Slot两种,且不允许共享。对于一个作业,刚开始运行时,Map Slot资源紧缺而Reduce Slot空闲,当Map Task全部运行完成后,Reduce slot紧缺而Map slot空闲。很明显,这种区分Slot类别的资源管理方案在一定程度上降低了Slot的利用率。同时,这种基于无类别Slot的资源划分方法的划分粒度过大,往往会造成节点资源利用率过高或者过低。
YARN中,一个NodeManager管理一个资源池,而不是指定固定数目的Slot。YARN上运行的MapReduce不会出现MapReduce 1.0中由于集群中只有Map Slot可用而导致Reduce Task必须等待的情况。如果能够获取运行任务的资源,那么应用程序就会正常进行。更进一步,YARN中的资源是精细化管理的,这样一个应用程序能够按需请求资源,而不是请求一个不可分割的、对于特定任务而言可能太大(浪费资源)或太小(可能导致失败)的Slot。
(4)多租户(Multitenancy)
在某种程度上可以说,YARN最大的优点是向MapReduce以外的其他分布式计算框架开放了Hadoop,MapReduce仅是许多YARN应用中的一个,Spark、Tez、Storm等计算框架也都可以运行在YARN上。另外,用户甚至可以在同一个YARN集群上运行不同版本的MapReduce,这使得升级MapReduce更好管理。

YARN发展目标

​ YARN的提出并非仅仅为了解决MapReduce 1.0中存在的问题,实际上YARN有着更加伟大的目标,即实现“一个集群多个框架”,也就是说在一个集群上部署一个统一的资源管理调度框架YARN,打造以YARN为核心的生态圈,在YARN之上可以部署其他各种计算框架,满足一个公司各种不同的业务需求,如离线计算框架MapReduce、DAG计算框架Tez、流式计算框架Storm、内存计算框架Spark等,由YARN为这些计算框架提供统一的资源管理调度服务,并且能够根据各种计算框架的负载需求,调整各自占用的资源,实现集群资源共享和资源弹性收缩。

YARN体系架构

  • YARN采用主从架构(Master/Slave),其核心组件包括三个:ResourceManager、NodeManager和ApplicationMaster。其中,ResourceManager是主进程,NodeManager是从进程,一个ResourceManager对应多个NodeManager,每个应用程序拥有一个ApplicationMaster。

  • 此外,YARN中引入了一个逻辑概念——容器Container,它将各类资源(如CPU、内存)抽象化,方便从节点NodeManager管理本机资源,主节点ResourceManager管理集群资源,如规定<1核,2G>为1个Container

  • Client:Client向ResourceManager提交任务、终止任务等。

  • ResourceManager:整个集群只有一个ResourceManager,负责集群资源的统一管理和调度。具体承担功能包括:
    (1)处理来自客户端请求,包括启动/终止应用程序。
    (2)启动/监控ApplicationMaster,一旦某个ApplicationMaster出现故障,ResourceManager将会在另一个节点上启动该ApplicationMaster。
    (3)监控NodeManager,接收NodeManager汇报的心跳信息并分配任务给NodeManager去执行,一旦某个NodeManager出现故障,标记该NodeManager的任务,来告诉对应的ApplicationMaster如何处理。

  • NodeManager
    整个集群有多个NodeManager,负责单节点资源的管理和使用。具体承担功能包括:
    (1)周期性向ResourceManager汇报本节点上的资源使用情况和各个Container的运行状态。
    (2)接收并处理来自ResourceManager的Container启动/停止的各种命令。
    (3)处理来自ApplicationMaster的命令。

  • ApplicationMaster
    每个应用程序拥有一个ApplicationMaster,负责管理应用程序。具体承担功能包括:
    (1)数据切分。
    (2)为应用程序/作业向ResourceManager申请资源(Container),并分配给内部任务。
    (3)与NodeManager通信,以启动/停止任务。
    (4)任务监控和容错,在任务执行失败时重新为该任务申请资源并重启任务。
    (5)接收并处理ResourceManager发出的命令,如终止Container、重启NodeManager等。

  • .Container
    Container是YARN中新引入的一个逻辑概念,是YARN对资源的抽象,是YARN中最重要的概念之一。Container封装了某个节点上一定量的资源(CPU和内存两类资源),它与Linux Container没有任何关系,仅仅是YARN提出的一个概念。
    Container由ApplicationMaster向ResourceManager申请,由ResouceManager中的资源调度器异步分配给ApplicationMaster;Container的运行是由ApplicationMaster向资源所在的NodeManager发起的,Container运行时需提供内部执行的任务命令(可以是任何命令,比如Java、Python、C++进程启动命令等)以及该命令执行所需的环境变量和外部资源(比如词典文件、可执行文件、jar包等)。
    另外,一个应用程序所需的Container分为以下两大类:
    (1)运行ApplicationMaster的Container:这是由ResourceManager和其内部的资源调度器申请和启动的,用户提交应用程序时,可指定唯一的ApplicationMaster所需的资源。
    (2)运行各类任务的Container:这是由ApplicationMaster向ResourceManager申请的,并由ApplicationMaster与NodeManager通信以启动之。该类Container上运行的任务类型可以是Map Task、Reduce Task或Spark Task等。
    以上两类Container可能在任意节点上,它们的位置通常而言是随机的,即ApplicationMaster可能与它管理的任务运行在一个节点上。

YARN工作流程

① Client向YARN提交MapReduce应用程序,提交的内容包括ApplicationMaster程序、启动ApplicationMaster的命令、用户程序等。
② ResourceManager接收到Client应用程序请求后,为应用程序分配第一个Container,并与对应的NodeManager通信,要求它在这个Container中启动该应用程序的ApplicationMaster(即图中的“MRAppMaster”)。
③ ApplicationMaster被创建后会首先向ResourceManager注册,从而使得用户可以直接通过ResourceManager查询应用程序的运行状态。接下来的步骤④-⑦是具体应用程序的执行步骤。
④ ApplicationMaster采用轮询的方式通过RPC请求向ResourceManager申请资源。
⑤ ResourceManager以“容器Container”的形式向提出申请的ApplicationMaster分配资源,一旦ApplicationMaster申请到资源,便与对应的NodeManager通信,要求它启动任务。
⑥ 当ApplicationMaster要求容器启动任务时,它会为任务设置好运行环境,包括环境变量、JAR包、二进制程序等,然后将任务启动命令写到一个脚本中,最后NodeManager在容器中运行该脚本以启动任务。
⑦ 各个任务通过RPC协议向ApplicationMaster汇报自己的状态和进度,以便ApplicationMaster随时掌握各个任务的运行状态,从而可以在任务失败时重启任务;在应用程序运行过程中,用户可以随时通过RPC向ApplicationMaster查询应用程序当前运行状态。
⑧ 应用程序运行完成后,ApplicationMaster向ResourceManager的应用程序管理器ApplicationManager注销并关闭自己。若ApplicationMaster因故失败,ResourceManager中的应用程序管理器ApplicationManager会监测到失败的情形,然后将其重新启动,直到所有的任务都执行完毕为止。

实战YARN

YARN Web UI

​ YARN Web UI接口面向管理员。从页面上,管理员能看到“集群统计信息”、“应用程序列表”、“调度器”等功能模块,此页面支持读,不支持写。YARN Web UI的默认地址为http://ResourceManagerIP:8088。

YARN Shell

YARN Shell接口面向YARN管理员。通过Shell接口,管理员能够查看YARN系统级别统计信息,提交YARN-App等。
YARN Shell命令统一入口为:yarn,语法格式如下:
yarn [–config confdir] [COMMAND | CLASSNAME]

注意:若$HADOOP_HOME/bin未加入到系统环境变量PATH中,则需要切换到Hadoop安装目录下,输入“bin/yarn”。

YARN Java API编程

YARN Java API接口面向Java开发工程师。程序员可以通过该接口编写YARN-App。

YARN-App三大模块包括:
(1)ApplicationBusinessLogic:应用程序的业务逻辑模块。
(2)ApplicationClient:应用程序客户端,负责提交和监管应用程序。
(3)ApplicationMaster:负责整个应用程序的运行,是应用程序并行化指挥地,需要指挥所有container并行执行ApplicationBusinessLogic。

YARN-App三大模块对应不同范式的类

YARN应用程序标准模块 DistributedShell框架对应类 MapReduce框架对应类 Giraph框架对应类
ApplicationBussinessLogic 用户编写的Shell命令 用户自定义Mapper类、Partition类、和Reduce类 用户自定义BasicComputation类
ApplicationClient Client.java YARNRunner.java GiraphYarnClient.java
ApplicationMaster ApplicationMaster.java MRAPPMaster.java GiraphApplicationMaster.java

YARN新特性

ResourceManager Restart自动重启机制

​ 在YARN体系架构中,ResourceManager地位极其重要,它负责集群资源的统一管理和调度。若ResourceManager发生单点故障,为了减少生产集群中作业执行失败的可能性,YARN提供了新特性——ResourceManager自动重启,该特性保证ResourceManager能尽快自动重启,且重启的过程用户感知不到。

. ResourceManager Restart实现原理

ResourceManager自动重启机制在不同版本的Hadoop中有两种不同的实现,两种实现的原理不同,配置相同。
1)Non-work-preserving RM restart(Hadoop 2.4.0实现)
Non-work-preserving RM restart,即在重启过程中任务不保留。其原理是当Client提交一个Application给ResourceManager时,ResourceManager会将该Application的相关信息存储起来,具体存储位置是可以在配置文件中指定的,可以存储到本地文件系统、HDFS或是ZooKeeper上,此外ResourceManager也会保存Application的最终状态信息(failed,killed,finished),如果是在安全环境下运行,ResourceManager还会保存相关证书文件。
当ResourceManager被关闭后,NodeManager和Client由于发现连接不上ResourceManager,会不断向ResourceManager发送消息,以便能及时确认ResourceManager是否已经恢复正常,当ResourceManager重新启动后,它会发送一条re-sync(重新同步)的命令给所有的NodeManager和ApplicationMaster,NodeManager收到重新同步的命令后会杀死所有的正在运行的Containers并重新向ResourceManager注册,从ResourceManager的角度来看,每台重新注册的NodeManager跟一台新加入到集群中NodeManager是一样的。ApplicationMaster收到重新同步的命令后会自行将自己杀掉。接下来,ResourceManager会将存储的关于Application的相关信息读取出来,并重新提交运行在ResourceManager关闭之前最终状态为正在运行中的Application。

ResourceManager Restart实现原理
2)Work-preserving RM restart(Hadoop 2.6.0实现)
Work-preserving RM restart,即在重启过程中任务是保留的。它与第一种实现的区别在于,ResourceManager会记录下Container整个生命周期的数据,包括Application运行的相关数据、资源申请状况、队列资源使用状况等数据。如此一来,当ResourceManager重启之后,会读取之前存储的关于Application的运行状态数据,同时发送re-sync命令,与第一种方式不同的是,NodeManager在接受到重新同步的命令后并不会杀死正在运行的Containers,而是继续运行Containers中的任务,同时将Containers的运行状态发送给ResourceManager,之后,ResourceManager根据自己所掌握的数据重构Container实例和相关的Application运行状态,如此一来,就实现了在ResourceManager重启之后,会紧接着ResourceManager关闭时任务的执行状态继续执行。

对比以上两种实现方式,第一种只保存了Application提交的信息和最终执行状态,并不保存运行过程中的相关数据,所以ResourceManager重启后,会先杀死正在执行的任务,再重新提交,从零开始执行任务。第二种方式则保存了Application运行中的状态数据,所以在ResourceManager重启之后,不需要杀死之前的任务,而是接着原来执行到的进度继续执行。ResourceManager将应用程序的状态及其他验证信息保存到一个可插拔的状态存储中,ResourceManager重启时从状态存储中重新加载这些信息,然后重新开始之前正在运行的应用程序,用户不需要重新提交应用程序。

ResourceManager HA高可用机制

在YARN中,ResourceManager负责整个集群资源的管理和应用程序的调度,Hadoop 2.4之前,ResourceManager存在单点故障问题,一旦出现故障,就会影响到整个集群的正常运行。在Hadoop 2.4中,增加了Active/Standby ResourceManager,以解决ResourceManager单点故障问题,这就是ResourceManager High Availability(ResourceManager高可用机制,简称ResourceManager HA)。

ResourceManager HA切换配置

1)手工切换
当Active ResourceManager发生故障时,管理员可通过命令手工切换,首先查看当前RM状态,然后手工切换RM,依次使用命令如下所示:
yarn rmadmin -getServiceState rm1
yarn rmadmin -transitionToStandby rm1
2)自动切换
通过内嵌的基于ZooKeeper的ActiveStandbyElector来决定哪个ResourceManager处于Active状态。当Active ResourceManager出现故障时,其他的ResourceManager将会被自动选举并切换成Active状态。

YARN Federation联邦机制

YARN Federation概述

​ 众所周知,YARN可以扩展到数千个节点。YARN的可伸缩性由ResourceManager确定,并且与节点数、活跃的应用程序、活跃的容器和心跳频率成比例。降低心跳可以提高可扩展性,但对利用率有害。基于联邦(Federation)的方法,通过联合多个YARN子集,可以将单个YARN集群扩展到数万个节点。YARN Federation是将大的(10-100k节点)集群划分成称为子集群的较小单元,每个集群具有其自己的ResourceManager和NodeManager。联合系统(Federation System)将这些子集群拼接在一起,使它们成为应用程序的一个大型YARN集群。在此联合环境中运行的应用程序将看到单个大型YARN集群,并且能够在联合集群的任何节点上计划任务。联合系统将与子集群的ResourceManager协商并为应用程序提供资源,目标是允许单个作业无缝地“跨越”子集群。

​ 这种设计在结构上是可扩展的,因为通过限制每个ResourceManager负责的节点数量,并且采用适当的策略将会保证大多数应用程序驻留在单个子集群中,因此每个ResourceManager看到的应用程序数量也是有限的。这意味着几乎可以通过简单地添加子集来线性扩展(因为它们之间需要很少的协调)。

分布式协调框架ZooKeeper

分布式协调技术

什么是分布式协调技术?

​ 分布式协调技术主要用来解决分布式环境中多个进程之间的同步控制,让它们有序地访问某种临界资源,防止造成“脏数据”。为了防止分布式系统中的多个进程之间相互干扰,就需要一种分布式协调技术来对这些进程进行调度,而分布式协调技术的核心就是实现分布式锁。

分布式系统中进程同步

​ 假设在Server1上挂载了一个资源,三个物理分布的进程都要竞争这个资源,但不希望它们同时进行访问,这时就需要一个协调器,来让三个进程有序地访问这个资源。这个协调器就是经常提到的锁,比如“Process-1”在使用该资源的时候会先去获得锁,“Process-1”获得锁之后会对该资源保持独占,这样其他进程就无法访问该资源,“Process-1”用完该资源后就将锁释放掉,让其他进程来获得锁。通过这个锁机制,就能保证分布式系统中多个进程能够有序地访问该临界资源。把这个分布式环境下的锁叫做分布式锁,这个分布式锁就是分布式协调技术实现的核心内容。

​ 那么,如何实现分布式锁呢?在分布式环境中,由于网络的不可靠,对一个服务调用的失败并不表示一定是失败的,可能是执行成功了,但是响应返回的时候失败了。另外,A和B都去调用C服务,在时间上A先调用,B后调用,那么最后的结果是不是一定A的请求就先于B。这些在同一台机器上的种种假设都要重新思考,还要思考这些问题给的设计和编码带来了哪些影响。还有,在分布式环境中为了提升可靠性,往往会部署多套服务,但是如何在多套服务中达到一致性,这在同一台机器上多个进程之间的同步相对来说是比较容易的,但在分布式环境中确实一个难题。

目前,已实现分布式协调技术的有Google Chubby,Apache ZooKeeper,它们都是分布式锁的实现者。

初识ZooKeeper

  • Apache ZooKeeper于2010年11月成为Apache顶级项目。ZooKeeper是Google Chubby的开源实现,是一个分布式的、开放源码的分布式应用程序协调框架,为大型分布式系统提供了高效且可靠的分布式协调服务,提供了诸如统一命名服务、配置管理、分布式锁等分布式基础服务,并广泛应用于大型分布式系统如Hadoop、HBase、Kafka等开源系统。

  • Apache ZooKeeper是一个分布式的、开放源码的分布式应用程序协调框架,是Google Chubby的开源实现,它为大型分布式系统中的各种协调问题提供了一个解决方案,主要用于解决分布式应用中经常遇到的一些数据管理问题,如配置管理、命名服务、分布式同步、集群管理等。

  • ZooKeeper易于编程,使用文件系统目录树作为数据模型,提供Java和C的编程接口。众所周知,协调服务非常容易出错,但却很难恢复正常,例如,协调服务很容易出现死锁。ZooKeeper的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用

  • ZooKeeper最早起源于雅虎研究院的一个研究小组。当时,研究人员发现,在雅虎内部很多大型系统基本都需要依赖一个类似的系统来进行分布式协调,但是这些系统往往都存在分布式单点问题。所以,雅虎的开户人员就试图开发一个通用的无单点问题的分布式协调框架,以便让开发人员将精力集中在处理业务逻辑上。

  • 雅虎模仿Google Chubby开发出了ZooKeeper,实现了类似的分布式锁功能,并且将ZooKeeper捐献给了Apache,ZooKeeper于2010年11月正式成为了Apache的顶级项目。

选择ZooKeeper原因

  • 随着分布式架构的出现,越来越多的分布式应用会面临数据一致性问题。在解决分布式数据一致性问题上,除了ZooKeeper之外,目前还没有其它成熟稳定且被大规模应用的解决方案。ZooKeeper无论从性能、易用性还是稳定性上来说,都已经达到了一个工业级产品标准。
  • 其次,ZooKeeper是开放源码的。所有人都可以贡献自己的力量,你可以和全世界成千上万的ZooKeeper开发者们一起交流使用经验,共同解决问题。
  • 再次,ZooKeeper是免费的。这点对于一个小型公司,尤其是初创团队来说,无疑是非常重要的。
  • 最后,ZooKeeper已经得到了广泛的应用。诸如Hadoop、HBase、Storm、Solr、Kafka等越来越多的大型分布式项目都已经将ZooKeeper作为其核心组件,用于分布式协调。

ZooKeeper基本概念

  1. 集群角色
    ZooKeeper并没有沿用传统的Master/Slave概念,而是引入了Leader、Follower、Observer三种角色。ZooKeeper集群中的所有机器通过选举机制来选定一台被称为“Leader”的机器,除Leader外,其它机器包括Follower和Observer。
    Leader服务器为客户端提供读和写服务,Leader不直接接受Client的请求,接受由其他Follower和Observer转发过来的Client请求,Leader还负责投票的发起和决议。
    Follower和Observer都能为客户端提供读服务,两者区别在于Observer不参与Leader选举过程,也不参与写操作的“过半写成功”策略,因此Observer可以在不影响写性能的情况下提升集群的读性能。
  2. 会话(Session)
    Session是指客户端会话。在介绍会话前,先来了解一下客户端连接,在ZooKeeper中,一个客户端连接是指客户端和服务器之间的一个TCP长连接。ZooKeeper对外服务端口默认是2181,客户端启动的时候,首先会与服务器建立一个TCP连接,从第一次连接建立开始,客户端会话的生命周期也就开始了,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向ZooKeeper服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的Watch事件通知。
    Session的sessionTimeout参数用于设置一个客户端会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在sessionTimeout规定时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话就仍然有效。
  3. 数据节点(ZNode)
    在谈到分布式的时候,通常说的“节点”是指组成集群的每一台机器。然而在ZooKeeper中,“节点”分为两类,第一类同样是指构成集群的机器,称之为机器节点;第二类则是指数据模型中的数据单元,称之为数据节点——ZNode。
    ZooKeeper将所有数据存储在内存中,数据模型是一棵树,由斜杠“/”进行分割的路径,就是一个ZNode,例如/app1/p_1。每个ZNode上都会保存自己的数据内容,同时还会保存一系列属性信息。
  4. 版本
    ZooKeeper每个ZNode上会存储数据,对应于每个ZNode,ZooKeeper都会为其维护一个Stat的数据结构,Stat中记录了这个ZNode的上数据版本,分别是dataVersion(当前ZNode数据内容的版本号)、cversion(当前ZNode子节点的版本号)、aclVersion(当前ZNode的ACL版本号)。
  5. 事件监听器(Watcher)
    Watcher是ZooKeeper中的一个很重要的特性。ZooKeeper允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发时,ZooKeeper服务端会将事件通知到感兴趣的客户端上去,该机制是ZooKeeper实现分布式协调服务的重要特性。
  6. 访问控制列表(ACL)
    ZooKeeper采用ACL(Access Control Lists)即访问控制列表策略来进行权限控制,类似于但不同于UNIX/Linux文件系统的权限控制方式UGO,UGO是一种粗粒度的文件系统权限控制模式,利用UGO只能对三类用户进行权限控制,而ACL是一种相对来说细粒度的权限管理方式,可以针对任意用户和组进行细粒度的权限控制。

ZooKeeper系统模型

数据模型

​ ZooKeeper采用类似标准文件系统的数据模型,其节点构成了一个具有层次关系的树状结构。其中每个节点被称为数据节点ZNode,ZNode是ZooKeeper中数据的最小单元,每个节点上可以存储数据,同时也可以挂载子节点,因此构成了一个层次化的命名空间。
​ ZNode通过路径引用,如同Unix中的文件路径。路径必须是绝对的,因此它们必须由斜杠“/”来开头。在ZooKeeper中,路径由Unicode字符串组成,并且有一些限制。例如ZooKeeper系统的保留ZNode“/zookeeper”用以保存管理信息,比如关键配额信息。

节点特性

节点类型
在ZooKeeper中,每个数据节点都是有生命周期的,其生命周期的长短取决于数据节点的节点类型。ZNode类型在创建时即被确定,并且不能改变。节点可以分为持久节点(PERSISTENT)、临时节点(EPHEMERAL)和顺序节点(SEQUENTIAL)三大类型。在节点创建过程中,通过组合使用,可以生成以下四种组合型节点类型:
1)持久节点PERSISTENT
持久节点是ZooKeeper中最常见的一种节点类型。所谓持久节点,是指此类节点的生命周期不依赖于会话,自节点被创建就会一直存在于ZooKeeper服务器上,并且只有在客户端显式执行删除操作时,它们才能被删除。
2)持久顺序节点PERSISTENT_SEQUENTIAL
持久顺序节点的基本特性与持久节点相同,额外特性表现在顺序性上。在ZooKeeper中,每个父节点都会为它的第一级子节点维护一份顺序,用于记录每个子节点创建的先后顺序。基于这个顺序特性,在创建子节点的时候,可以设置这个标记,那么在创建节点过程中,ZooKeeper会自动为给定节点名加上一个数字后缀,作为一个新的、完整的节点名。不过ZooKeeper会给此类节点名称进行顺序编号,自动在给定节点名后加上一个数字后缀。这个数字后缀的上限是整型的最大值,其格式为“%10d”(10位数字,没有数值的数位用0补充,例如“0000000001”),当计数值大于232-1时,计数器将溢出。

3)临时节点EPHEMERAL
与持久节点不同的是,临时节点的生命周期依赖于创建它的会话,也就是说,如果客户端会话失效,临时节点将被自动删除,当然也可以手动删除。注意,这里提到的是客户端会话失效,而非TCP连接断开。另外,ZooKeeper规定临时节点不允许拥有子节点。
4)临时顺序节点EPHEMERAL_SEQUENTIAL
临时顺序节点的基本特性和临时节点也是一致的,同样是在临时节点的基础上,添加了顺序的特性。

节点结构

ZooKeeper命名空间中的ZNode,兼具文件和目录两种特点,既能像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又能像目录一样可以作为路径标识的一部分。每个ZNode由3部分组成:
(1)stat:状态信息,描述该ZNode的版本、权限等信息。
(2)data:与该ZNode关联的数据。
(3)children:该ZNode下的子节点。
ZooKeeper虽然可以关联一些数据,但并没有被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,比如分布式应用中的配置文件信息、状态信息、汇集位置等。这些数据的共同特性就是它们都是很小的数据,通常以KB为大小单位。ZooKeeper的服务器和客户端都被设计为严格检查并限制每个ZNode的数据大小至多1M,但常规使用中应该远小于此值。

版本——保证分布式数据原子性操作

每个数据节点都具有三种类型的版本信息,对数据节点的任何更新操作都会引起版本号的编号。三种类型的版本分别为:
(1)version:当前数据节点数据内容的版本号。
(2)cversion:当前数据节点子节点的版本号。
(3)aversion:当前数据节点的ACL版本号。
注意:version表示的是数据节点数据内容的变更次数,强调的是变更次数,因此即使前后两次变更并没有使得数据内容的值发生变化,version的值依然会变更。
事实上,在ZooKeeper中,version属性正是用来实现乐观锁机制中的“写入校验”的。

Watcher——数据变更的通知

​ ZooKeeper提供了分布式数据的发布/订阅功能。一个典型的发布/订阅模型系统定义了一种一对多的订阅关系,能够让多个订阅者同时监听某一个主题的对象,当这个主题对象自身状态变化时,会通知所有订阅者,使它们能够做出相应的处理。
​ 在ZooKeeper中,引入了Watcher机制来实现这种分布式的通知功能。ZooKeeper允许客户端向服务端注册一个Watcher监听,当服务端的一些指定事件触发了这个Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。

ZooKeeper的Watcher机制主要包括客户端线程、客户端WatcherManager和ZooKeeper服务器三部分。
在工作流程上,简单地讲,客户端在向ZooKeeper服务器注册的同时,会将Watcher对象存储在客户端的WatcherManager当中。当ZooKeeper服务器触发Watcher事件后,会向客户端发送通知,客户端线程从WatcherManager中取出对应的Watcher对象来执行回调逻辑。

在ZooKeeper中,接口类Watcher用于表示一个标准的事件处理器,其定义了事件通知相关的逻辑,包含KeeperState和EventType两个枚举类,分别代表了通知状态和事件类型,同时定义了事件的回调方法:process(WatchedEvent event)。同一事件类型在不同的通知状态中代表的含义有所不同。

Watcher事件类型

其中,NodeDataChanged事件的触发条件是数据内容的变化变更,此处所说的变更包括节点的数据内容和数据版本号version的变化,因此,即使使用相同的数据内容来更新,还是会触发这个事件,因为对于ZooKeeper来说,无论数据内容是否变更,一旦有客户端调用了数据更新的接口且更新成功,就会更新version值。
NodeChildrenChanged事件会在数据节点的子节点列表发生变更的时候被触发,这里说的子节点变化特指子节点个数和组成情况的变更,即新增子节点或删除子节点,而子节点数据内容的变化时不会触发这个事件的。
对于AuthFailed事件,需要注意的是,它的触发条件并不是简单因为当前客户端会话没有权限,而是授权失败。

ACL——保障数据安全

  • ZooKeeper提供了一套完善ACL(Access Control List)权限控制机制来保障数据的安全。
  • 提到权限控制,首先来看看在Unix/Linux文件系统中使用的也是目前应用最广泛的权限控制方式——UGO(User、Group和Others)权限控制机制。简单地说,UGO就是针对一个文件或目录,对创建者(User)、创建者所在的组(Group)和其他用户(Others)分别配置不同的权限。从这里可以看出,UGO是一种粗粒度的文件系统权限控制模式,利用UGO只能对三类用户进行权限控制,无法解决所有场景问题。
  • 接下来,介绍另一种典型的权限控制方式——ACL。ACL,即访问控制列表,是一种相对来说比较新颖且更细粒度的权限管理方式,可以针对任意用户和组进行细粒度的权限控制。目前绝大部分Unix系统都已经只支持ACL,Linux也从2.6版本的内核开始支持这ACL权限控制方式。
  • ZooKeeper提供了一套完善ACL(Access Control List)权限控制机制来保障数据的安全。
  • 在Unix/Linux文件系统中使用的也是目前应用最广泛的权限控制方式——UGO(User、Group和Others)权限控制机制。简单地说,UGO就是针对一个文件或目录,对创建者(User)、创建者所在的组(Group)和其他用户(Others)分别配置不同的权限。从这里可以看出,UGO是一种粗粒度的文件系统权限控制模式,利用UGO只能对三类用户进行权限控制,无法解决所有场景问题。
  • 另一种典型的权限控制方式——ACL。ACL,即访问控制列表,是一种相对来说比较新颖且更细粒度的权限管理方式,可以针对任意用户和组进行细粒度的权限控制。目前绝大部分Unix系统都已经只支持ACL,Linux也从2.6版本的内核开始支持这ACL权限控制方式。
  • ZooKeeper的ACL权限控制和Unix/Linux操作系统中的ACL有一些区别,读者可以从三个方面来理解ACL机制,分别是:权限模式(Scheme)、授权对象(ID)和权限(Permission),通常使用“schemepermission”来标识一个有效的ACL信息。

权限模式(Scheme)

权限模式用来确定权限验证过程中使用的检验策略。在ZooKeeper中,开发人员使用最多的就是以下四种权限模式。
1)IP
IP模式通过IP地址粒度进行权限控制,例如配置了“ip:192.168.18.130”,就表示权限控制都是针对这个IP地址的。同时,IP模式也支持按照网段的方式进行配置,例如“ip:192.168.18.1/24”表示针对192.168.18.*的IP段进行权限控制。
2)Digest
Digest是最常用的控制权限模式,也更符合对于权限控制的认识,其以类似于“username:password”形式的权限标识来进行权限配置,便于区分不同应用来进行权限控制。当通过“username:password”形式配置了权限标识后,ZooKeeper会对其先后进行两次编码处理,分别是SHA-1算法加密和BASE64编码。
3)World
World是一种最开放的权限控制模式,事实上这种权限控制方式几乎没有任何作用,数据节点的访问权限对所有用户开发,即所有用户都可以在不进行任何权限校验的情况下操作该数据节点。另外,World模式也可以看作是一种特殊的Digest模式,它只有一个权限标识“world:anyone”。
4)Super
Super模式顾名思义就是超级用户的有意思,也是一种特殊的Digest模式。在Super模式下,超级用户可以对任意ZooKeeper上的数据节点进行任何操作。

授权对象(ID)

授权对象指的是权限赋予的用户或一个指定实体,例如IP地址或机器等。在不同的授权模式下,授权对象是不同的

权限(Permission)

权限就是指那些通过权限检查后可以被允许执行的操作。在ZooKeeper中,所有对数据的操作权限分为以下五大类:
(1)CREATE(C):数据节点的创建权限,允许授权对象在该数据节点下创建子节点。
(2)DELETE(D):子节点的删除权限,允许授权对象删除该数据接地点的子节点。
(3)READ(R):数据节点的读取权限,允许授权对象访问该数据节点并读取其数据内容或子节点列表等。
(4)WRITE(W):数据节点的更新权限,允许授权对象对该数据节点进行更新操作。
(5)ADMIN(A):数据节点的管理权限,允许授权对象对数据节点进行ACL相关的设置操作。

ZooKeeper工作原理

ZooKeeper集群架构

  • ZooKeeper运行模式有两种:单机模式(Standalone Mode)、集群模式(Replicated Mode)。其中,单机模式主要用于评估、开发和测试,而在实际的生产环境中均采用集群模式。
  • ZooKeeper单机模式部署时,只在一台机器上安装ZooKeeper,该ZooKeeper提供一切协调服务;ZooKeeper单机模式运行时该ZooKeeper就是Leader。
  • ZooKeeper集群模式部署时,在多台机器上安装ZooKeeper,ZooKeeper采用对等结构,无Master、Slave之分,统一都是QuorumPeerMain进程;ZooKeeper集群模式运行时采取选举方式选择Leader,采用原子广播协议ZAB完成。
  • Leader选举时要求“可用节点数量>总节点数量/2”,即ZooKeeper集群中的存活节点必须过半。因此,在节点数量是奇数的情况下,ZooKeeper集群总能对外提供服务(即使损失了一部分节点);如果节点数量是偶数,会存在ZooKeeper集群不能用的可能性。在生产环境中,如果ZooKeeper集群不能提供服务,那将是致命的,所以ZooKeeper集群的节点数一般采用奇数。
  • 每个Server在工作过程中有四种状态:
    (1)LOOKING:竞选状态。
    (2)FOLLOWING:随从状态,同步Leader状态,参与投票。
    (3)OBSERVING:观察状态,同步Leader状态,不参与投票。
    (4)LEADING:领导者状态。

ZooKeeper服务器角色

Leader

Leader服务器是整个ZooKeeper集群工作机制中的核心,其主要工作包括以下两个方面:
(1)事务请求的唯一调度和处理者,保证集群事务处理的顺序性。
(2)集群内部各服务器的调度者。

Follower

Follower服务器是ZooKeeper集群状态的跟随者,其主要工作包括以下三个方面:
(1)处理客户端非事务请求,转发事务请求给Leader服务器。
(2)参与事务请求Proposal的投票。
(3)参与Leader选举投票。

Observer

Observer是ZooKeeper自3.3.0版本开始引入的一个全新的服务器角色。从字面意思看,该服务器充当了一个观察者的角色——其观察ZooKeeper集群的最新状态变化并将这些状态变更同步过来。Observer服务器在工作原理上和Follower基本是一致的,对于非事务请求,都可以进行独立的处理,而对于事务请求,则会转发给Leader服务器进行处理。和Follower唯一的区别在于,Observer不参与任何形式的投票,包括事务请求Proposal的投票和Leader的选举投票。简单地说,Observer服务器只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力。

ZooKeeper的ZAB协议

  • 在深入了解ZooKeeper之前,相信很多读者都会认为ZooKeeper就是Paxos算法的一个实现。但事实上,ZooKeeper并没有完全采用Paxos算法,而是使用了一种称为ZooKeeper Atomic Broadcast(ZAB,ZooKeeper原子消息广播协议)的协议作为其数据一致性的核心算法。
  • ZAB协议是专门为分布式协调服务ZooKeeper设计的一种支持崩溃恢复的原子广播协议。ZAB协议的开发设计人员在协议设计之初并没有要求其具有很好的扩展性,最初只是为雅虎公司内部那些高吞吐量、低延迟、健壮、简单的分布式系统场景设计的。在ZooKeeper的官方文档中也指出;“ZAB协议并不像Paxos算法,是一种通用的分布式一致性算法,它是一种特别为ZooKeeper设计的崩溃可恢复的原子消息广播算法”。
  • ZAB协议的核心是定义了对于那些会改变ZooKeeper服务器数据状态的事务请求的处理方式,即:所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器就是Leader,而其他服务器则成为Follower。Leader服务器负责将一个客户端事务请求转换成一个事务Proposal(提议),并将该Proposal分发给集群中所有Follower,之后Leader需要等待所有Follower的反馈,一旦超过半数的Follower服务器进行了正确的反馈后,那么Leader就会再次向所有的Follower分发Commit消息,要求其将前一个Proposal进行提交。
    1. ZAB协议具体内容
      ZAB协议包括两种基本模式,分别是崩溃恢复和消息广播。
      当整个服务器框架在启动过程中,或是当Leader服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB协议就会进入崩溃恢复模式并选举产生新的Leader服务器。当选举产生了新的Leader服务器,同时集群中已经有过半的机器与该Leader服务器完成了状态同步后,ZAB协议就会退出崩溃恢复模式进入消息广播模式了。其中,所谓的状态同步是指数据同步,用来保证集群中存在过半的机器能够和Leader服务器的数据状态保持一致。
      当一台同样遵守ZAB协议的服务器启动后加入到集群中时,如果此时集群中已经存在一个Leader服务器在负责进行消息广播,那么新加入的服务器就会自觉进入恢复模式:找到Leader所在的服务器,并与其进行数据同步,然后一起参与到消息广播流程中去。正如上文所说,ZooKeeper设计成只允许唯一的一个Leader服务器来进行事务请求的处理,Leader服务器在接收到客户端的事务请求后,会生成对应的事务提议并发起一轮广播协议;而如果集群中的其他机器接收到客户端的事务请求,那么这些非Leader服务器会首先将这个事务请求转发给Leader服务器。
      当Leader出现崩溃退出或是机器重启,亦或是集群中已经不存在过半的服务器与该Leader保持正常通信时,那么在重新开始新一轮的原子广播事务操作之前,所有进程首先会使用崩溃恢复协议来使彼此达到一个一致的状态,于是整个ZAB流程就会从消息广播模式进入到崩溃恢复模式。
      一个机器要称为新的Leader,必须获得过半进程的支持,同时由于每个进程都有可能会崩溃,因此,在ZAB协议运行过程中,前后会出现多个Leader,并且每个进程也有可能会多次称为Leader。进入崩溃恢复模式后,只要集群中存在过半的服务器能够彼此进行正常通信,那么就可以产生一个新的Leader并再次进入消息广播模式。

Leader选举机制

选举机制中的术语
1)SID:服务器ID
SID是一个数字,用来唯一标识一台ZooKeeper集群中机器,每台机器不能重复,和myid值一致。
2)ZXID:事务ID
ZXID是一个事务ID,用来唯一标识一次服务器状态的变更。在某一个时刻,集群中的每台服务器的ZXID值不一定全都一致,这和ZooKeeper服务器对于客户端“更新请求”的处理逻辑有关。
3)Vote:投票
Leader选举必须通过投票来实现。当集群中的机器发现自己无法检测到Leader机器时,就会开始尝试进行投票。
4)Quorum:过半机器数
这是整个Leader选举算法中最重要的一个术语,可以把它理解为一个量词,指的是ZooKeeper集群中过半的机器数,如果集群中总的机器数是n的话,那么可以通过下面公式计算quorum值:
quorum = n/2 + 1
5)服务器状态
服务器状态有4种:LOOKING竞选状态,FOLLOWING随从状态,OBSERVING观察状态,LEADING领导者状态。

Leader选举概述
以3台机器组成的服务器集群为例介绍,假设3台机器的myid依次为1、2、3,称它们依次为Server1、Server 2和Server3,那么Server1的SID为1,Server2的SID为2,Server3的SID为3。
1)服务器启动时期的Leader选举
在服务器集群初始化阶段,当只有服务器Server1启动时,它是无法完成Leader选举的,当第二台服务器Server2也启动后,此时这两台机器已经能够进行互相通信,每台机器都试图找到一个Leader,于是便进入了Leader选举流程。
(1)每个Server会发出一个投票。
由于是初始状态,因此Server1和Server2都会将自己作为Leader服务器来进行投票,每次投票包含的最基本的元素包括:所推举的服务器的SID和ZXID,以(SID,ZXID)形式表示。因为是初始化阶段,因此无论是Server1还是Server2,都会投给自己,即Server1的投票为(1,0),Server2的投票为(2,0),然后各自将这个投票发给集群中其他所有机器。
(2)接收来自各个服务器的投票。
每个服务器都会接收来自其他服务器的投票。集群中的每个服务器在接收到投票后,首先会判断该投票的有效性,包括检查是否是本轮投票、是否来自LOOKING状态的服务器。
(3)处理投票。
在接收到来自其他服务器的投票后,针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下:
优先检查ZXID。ZXID比较大的服务器优先作为Leader。
如果ZXID相同,那么就比较SID。SID比较大的服务器作为Leader服务器。
根据以上规则,对于Server1,它自己的投票是(1,0),而接收到的投票为(2,0)。首先对比两者的ZXID,因为都是0,所以无法决定谁是Leader;接下来会对比两者的SID,很显然,Server1发现接收到的投票中的SID是2,大于自己,于是就会更新自己的投票为(2,0),然后重新将投票发出去。而对于Server2来说,不需要更新自己的投票信息,只是再一次向集群中所有机器发出上一次投票信息即可。

(4)统计投票。
每次投票后,服务器都会统计所有投票,判断是否已经有过半的机器收到相同的投票信息。对于Server1和Server2服务器来说,都统计出集群中已经有2台机器接受了(2,0)这个投票信息,对于由3台机器构成的集群,2台即达到了“过半”要求(≥n/2+1)。那么,当Server1和Server2都收到相同的投票信息(2,0)时,即认为已经选出了Leader。
(5)改变服务器状态。
一旦确定了Leader,每个服务器都会更新自己的状态:如果是Follower,那么就变更为FOLLOWING;如果是Leader,那么久变更为LEADING。

2)服务器运行期间的Leader选举
在ZooKeeper集群正常运行过程中,一旦选出一个Leader,那么所有服务器的集群角色一般不会再发生变化——也就是说,Leader服务器将一直作为集群的Leader,即使集群中有非Leader宕机或是有新机器加入集群,也不会影响Leader。但是一旦Leader宕机,那么整个集群将暂时无法对外服务,而是进入新一轮的Leader选举。服务器运行期间的Leader选举和启动时期的Leader选举基本过程是一致的

假设当前正在运行的ZooKeeper集群有Server1、Server2、Server3三台机器组成,当前Leader是Server2,假设某一瞬间,Leader宕机,这个时候便开始了新一轮Leader选举,具体过程如下所示。
(1)变更服务器状态。
当Leader宕机后,余下的非Observer服务器就会将自己的服务器状态变更为LOOKING,然后开始进入Leader选举流程。
(2)每个Server都会发出一个投票。
在这个过程中,需要生成投票信息(SID,ZXID)。因为是运行期间,因此每个服务器上的ZXID可能不同,假定Server1的ZXID为123,而Server3的ZXID为122。在第一轮投票中,Server1和Server3都会投自己,即分别产生投票(1,123)和(3,122),然后各自将投票发给集群中所有机器。
(3)接收来自各个服务器的投票。
(4)处理投票。
对于投票的处理,和上面提到的服务器启动期间的处理规则是一致的。在这个例子中,由于Server1的ZXID值大于Server3的ZXID值,因此,Server1会成为Leader。
(5)统计投票。
(6)改变服务器状态。

ZooKeeper典型应用场景

​ ZooKeeper是一个典型的发布/订阅模式的分布式数据管理与协调框架,开发人员可以使用它进行分布式数据的发布与订阅。另一方面,通过ZooKeeper中丰富的数据节点类型进行交叉使用,配合Watcher事件通知机制,可以非常方便地构建一系列分布式应用中都会涉及的核心功能,如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁、分布式队列等。

数据发布/订阅

  • 到ZooKeeper的一个或一系列节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。
  • 数据发布/订阅系统一般有两种设计模式,分别是推模式和拉模式。在推模式中,服务端主动将数据更新发送给所有订阅的客户端;而拉模式则是客户端主动发起请求来获取最新数据,通常客户端都采用定时进行轮询拉取的方式。
  • 这两种模式各自都有优缺点,ZooKeeper采用推拉相结合的方式,即:客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应的客户端发送Watcher事件通知,客户端接收到这个消息通知之后,需要主动到服务端获取最新数据。

命名服务

  • 命名服务(Name Service)也是分布式系统中比较常见的一类场景。在分布式系统中,被命名的实体通常可以是集群中的机器、提供的服务地址或远程对象等,这些都可以统称它们为名字(Name),其中较为常见的就是一些分布式服务框架如RPC、RMI中的服务地址列表,通过使用命名服务,客户端应用能够根据指定名字来获取资源的实体、服务地址、提供者的信息等。
  • ZooKeeper提供的命名服务功能与Java的JNDI技术相似,都能够帮助应用系统通过一个资源引用的方式来实现对资源的定位与使用。另外,广义上命名服务的资源定位都不是真正意义的实体资源——在分布式环境中,上层应用仅仅需要一个全局唯一的名字,类似于数据库中的唯一主键。

集群管理

  • 所谓集群管理,包括集群监控和集群控制两大块,前者侧重对集群运行时状态的收集,后者则是对集群进行操作与控制。

  • 在日常开发和运维过程中,经常会有类似于如下的需求:希望知道当前集群中究竟有多少机器在工作;对集群中每台机器的运行时状态进行数据收集;对集群中机器进行上下线操作。

  • 在传统的基于Agent的分布式集群管理体系中,都是通过在集群中的每台机器上部署一个Agent,由这个Agent负责主动向指定的一个监控中心系统汇报自己所在机器的状态。在集群规模适中的场景下,这确实是一种在生产实践中广泛使用的解决方案,能够快速有效地实现分布式环境集群监控,但是一旦系统的业务场景增多,集群规模变大之后,该解决方案的弊端就显现出来了,例如大规模升级困难、统一的Agent无法满足多样的需求、编程语言多样性。

  • ZooKeeper具有以下两大特性:
    客户端如果对ZooKeeper的一个数据节点注册Watcher监听,那么当该数据节点的内容或是其子节点列表发生变更时,ZooKeeper服务器就会向订阅客户端发送变更通知。

  • 对在ZooKeeper上创建的临时节点,一旦客户端与服务器之间的会话失效,那么该临时节点也就被自动清除。
    利用ZooKeeper的这两个特性,就可以实现另一种集群机器存活性监控的系统。例如,监控系统在/clusterServers节点上注册一个Watcher监听,那么但凡进行动态添加机器的操作,就会在/clusterServers节点下创建一个临时节点/clusterServers/[Hostname]。这样一来,控制系统就能够实时检测到机器的变动情况,置于后续的处理就是监控系统的业务了。

    分布式锁

分布式锁是控制分布式系统之间同步访问共享资源的一种方式。如果不同的系统同或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要通过一些互斥手段来防止彼此之间的干扰,以保证一致性,在这种情况下,就需要使用分布式锁了。
在平时的项目开发中,人们往往很少会去在意分布式锁,而是依赖于关系型数据库固有的排他性来实现不同进程之间的互斥。这确实是一种非常简单且被广泛使用的分布式锁实现方式。然而,目前绝大多数大型分布式系统的性能瓶颈都集中在数据库操作上,因此,如果上层业务再给数据库添加一些额外的锁,例如行锁、表锁甚至是繁重的事务处理,那么是不是会让数据库更加不堪重负呢?

排它锁

**排它锁(Exclusive Locks,简称X锁),又称为写锁或独占锁。**如果事务T1对数据对象O1加上了排它锁,那么在整个加锁期间,只允许事务T1对O1进行读取和更新操作,其他任何事务都不能再对这个数据对象进行任何类型的操作,直到T1释放了排它锁。排它锁的核心是如何保证当前有且仅有一个事务获得锁,并且锁被释放后,所有正在等待获取锁的事务都能够被通知到。借助ZooKeeper实现排它锁可以通过以下步骤完成。

1)定义锁
通过ZooKeeper上的临时数据节点来表示一个锁,例如/exclusive_lock/lock节点就可以被定义为一个锁。
2)获取锁
在需要获取排它锁时,所有客户端都会试图在/exclusive_lock节点下创建临时子节点/exclusive_lock/lock。ZooKeeper会保证在所有的客户端中,最终只有一个客户端能够创建成功,那么就可以认为该客户端获取了锁。同时,所有没有获取到锁的客户端就需要在/exclusive_lock节点上注册一个子节点变更的Watcher监听,以便实时监听到lock节点的变更情况。
3)释放锁
在“定义锁”部分,已经提到,/exclusive_lock/lock是一个临时节点,因此在以下两种情况下,都有可能释放锁:
当前获取锁的客户端机器发生宕机,那么ZooKeeper上的这个临时节点就会被移除。
正常执行完业务逻辑后,客户端就会主动将自己创建的临时节点删除。

共享锁

共享锁(Shared Locks,简称S锁),又称为读锁。如果事务T1对数据对象O1加上了共享锁,那么当前事务T1对O1进行读取操作,其他事务也只能对这个数据对象加共享锁,直到该数据对象上的所有共享锁都被释放。共享锁和排他锁的最根本区别在于,加上排他锁后,数据对象只对一个事务可见,而加上共享锁后,数据对所有事务都可见。借助ZooKeeper实现共享锁可以通过以下步骤完成。

1)定义锁
与排他锁相同,通过ZooKeeper上的数据节点来表示一个锁,是一个类似于“/shared_lock/[hostname]-请求类型-序号”的临时顺序节点。例如,/shared_lock/192.168.18.1-R-0000000001就代表了一个共享锁。
2)获取锁
在需要获取共享锁时,所有客户端都会到/shared_lock节点下创建一个临时顺序节点。如果当前是读请求,那么就创建/shared_lock/192.168.18.1-R-0000000001;如果当前是写请求,那么就创建/shared_lock/192.168.18.1-W-0000000001。
3)判断读写顺序
根据共享锁的定义,不同的事务都可以同时对同一个数据对象进行读取操作,而更新操作必须在当前没有任何事务进行读写操作的情况下进行。基于这个原则,通过ZooKeeper的节点来确定分布式读写顺序的步骤如下所示:
(1)创建完节点后,获取/shared_lock节点下的所有子节点,并对该节点注册子节点变更的Watcher监听。
(2)确定自己的节点序号在所有子节点中的顺序。
(3)对读和写请求分类处理。对于读请求,若没有比自己序号小的子节点,或是所有比自己序号小的子节点都是读请求,那么表明自己已经成功获取到了共享锁,同时开始执行读取操作;若比自己序号小的子节点中有写请求,那么就需要进入等待。对于写请求,若自己不是序号最小的子节点,那么就需要进入等待。
(4)接收到Watcher通知后,重复步骤(1)。
4)释放锁
释放锁的逻辑和排他锁相同。

部署ZooKeeper集群

对于大部分Java开源产品而言,在部署与运行之前,总是需要搭建一个合适的环境,通常包括操作系统和Java环境两方面。同样,ZooKeeper部署与运行所需要的系统环境,同样包括操作系统和Java环境两部分。
1)操作系统
ZooKeeper支持不同平台,在当前绝大多数主流的操作系统上都能够运行,例如GNU/Linux、Sun Solaris、FreeBSD、Windows、Mac OS X等。需要注意的是,ZooKeeper官方文档中特别强调,不建议在Mac OS X系统上部署生成环境的ZooKeeper服务器。本书采用的操作系统为Linux发行版CentOS 7。
2)Java环境
ZooKeeper使用Java语言编写,因此它的运行环境需要Java环境的支持,对于ZooKeeper 3.4.13,需要Java 1.6及以上版本的支持。

运行模式

ZooKeeper有两种运行模式:单机模式和集群模式。单机模式是只在一台机器上安装ZooKeeper,主要用于开发测试,而集群模式则是在多台机器上安装ZooKeeper,实际的生产环境中均采用集群模式。无论哪种部署方式,创建ZooKeeper的配置文件zoo.cfg都是至关重要的。单机模式和集群模式部署的步骤基本一致,只是在zoo.cfg文件的配置上有些差异。
读者需要注意的是,假设你拥有一台比较好的机器(CPU核数大于10,内存大于等于8GB),如果作为单机模式进行部署,资源明显有点浪费,如果按照集群模式进行部署,需要借助硬件上的虚拟化技术,把一台物理机器转换成几台虚拟机,这样操作成本太高。幸运的是,和其他分布式系统如Hadoop一样,ZooKeeper也允许在一台机器上完成一个伪集群的搭建。所谓伪集群,是指集群所有的机器都在一台机器上,但是还是以集群的特性来对外提供服务。这种模式和集群模式非常类似,只是把zoo.cfg文件中配置项“server.id=host:port:port”略做修改。

你可能感兴趣的:(hadoop,hadoop)