<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>iCocos</title>
  
  <subtitle>www.icocos.cn</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://icocos.github.io/"/>
  <updated>2020-04-09T14:45:08.847Z</updated>
  <id>https://icocos.github.io/</id>
  
  <author>
    <name>曹理鹏@iCocos</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>大数据之平台建设</title>
    <link href="https://icocos.github.io/2020/04/07/%E5%A4%A7%E6%95%B0%E6%8D%AE%E4%B9%8B%E5%B9%B3%E5%8F%B0%E5%BB%BA%E8%AE%BE/"/>
    <id>https://icocos.github.io/2020/04/07/大数据之平台建设/</id>
    <published>2020-04-07T15:56:50.000Z</published>
    <updated>2020-04-09T14:45:08.847Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h2 id="分布式系统基础架构Hadoop"><a href="#分布式系统基础架构Hadoop" class="headerlink" title="分布式系统基础架构Hadoop"></a>分布式系统基础架构Hadoop</h2><blockquote><h3 id="Hadoop详细介绍"><a href="#Hadoop详细介绍" class="headerlink" title="Hadoop详细介绍"></a>Hadoop详细介绍</h3></blockquote><p>Hadoop是一个分布式系统基础架构，由Apache基金会开发。用户可以在不了解分布式底层细节的情况下，开发分布式程序。充分利用集群的威力高速运算和存储。Hadoop实现了一个分布式文件系统（Hadoop Distributed File System），简称HDFS。HDFS有着高容错性的特点，并且设计用来部署在低廉的（low-cost）硬件上。而且它提供高传输率（high throughput）来访问应用程序的数据，适合那些有着超大数据集（large data set）的应用程序。HDFS放宽了（relax）POSIX的要求（requirements）这样可以流的形式访问（streaming access）文件系统中的数据。</p><h3 id="Hadoop体系结构"><a href="#Hadoop体系结构" class="headerlink" title="Hadoop体系结构"></a>Hadoop体系结构</h3><p><img src="https://static.oschina.net/uploads/space/2016/1222/183728_qCnc_12.png" alt=""></p><blockquote><h3 id="分布式文件系统HDFS"><a href="#分布式文件系统HDFS" class="headerlink" title="分布式文件系统HDFS"></a>分布式文件系统HDFS</h3></blockquote><p>Hadoop Distributed File System，简称HDFS，是一个分布式文件系统。HDFS有着高容错性（fault-tolerent）的特点，并且设计用来部署在低廉的（low-cost）硬件上。而且它提供高吞吐量（high throughput）来访问应用程序的数据，适合那些有着超大数据集（large data set）的应用程序。HDFS放宽了（relax）POSIX的要求（requirements）这样可以实现流的形式访问（streaming access）文件系统中的数据。HDFS开始是为开源的apache项目nutch的基础结构而创建，HDFS是hadoop项目的一部分，而hadoop又是lucene的一部分。</p><p><img src="https://static.oschina.net/uploads/img/201209/10164821_w6pR.jpg" alt=""></p><blockquote><h3 id="大规模数据集软件架构MapReduce"><a href="#大规模数据集软件架构MapReduce" class="headerlink" title="大规模数据集软件架构MapReduce"></a>大规模数据集软件架构MapReduce</h3></blockquote><p>MapReduce是Google提出的一个软件架构，用于大规模数据集（大于1TB）的并行运算。概念”Map（映射）”和”Reduce（化简）”，和他们的主要思想，都是从函数式编程语言借来的，还有从矢量编程语言借来的特性。</p><p>当前的软件实现是指定一个Map（映射）函数，用来把一组键值对映射成一组新的键值对，指定并发的Reduce（化简）函数，用来保证所有映射的键值对中的每一个共享相同的键组。</p><h2 id="Hadoop资源管理器YARN"><a href="#Hadoop资源管理器YARN" class="headerlink" title="Hadoop资源管理器YARN"></a>Hadoop资源管理器YARN</h2><blockquote><h3 id="YAEN详细介绍"><a href="#YAEN详细介绍" class="headerlink" title="YAEN详细介绍"></a>YAEN详细介绍</h3><p>YARN是新一代Hadoop资源管理器，通过YARN,用户可以运行和管理同一个物理集群机上的多种作业，例如MapReduce批处理和图形处理作业。这样不仅可以巩固一个组织管理的系统数目，而且可以对相同的数据进行不同类型的数据分析。某些情况下，整个数据流可以执行在同一个集群机上。</p></blockquote><p><img src="https://static.oschina.net/uploads/img/201308/15080717_9LdU.gif" alt=""></p><h2 id="数据仓库平台Hive"><a href="#数据仓库平台Hive" class="headerlink" title="数据仓库平台Hive"></a>数据仓库平台Hive</h2><blockquote><h3 id="Hive详细介绍"><a href="#Hive详细介绍" class="headerlink" title="Hive详细介绍"></a>Hive详细介绍</h3></blockquote><p>Hive是一个基于Hadoop的数据仓库平台。通过hive，我们可以方便地进行ETL的工作。hive定义了一个类似于SQL的查询语言：HQL，能 够将用户编写的QL转化为相应的Mapreduce程序基于Hadoop执行。</p><p>Hive是Facebook 2008年8月刚开源的一个数据仓库框架，其系统目标与 Pig 有相似之处，但它有一些Pig目前还不支持的机制，比如：更丰富的类型系统、更类似SQL的查询语言、Table/Partition元数据的持久化等。</p><h2 id="数据表和存储管理服务HCatalog"><a href="#数据表和存储管理服务HCatalog" class="headerlink" title="数据表和存储管理服务HCatalog"></a>数据表和存储管理服务HCatalog</h2><blockquote><h3 id="Hcatalog-详细介绍"><a href="#Hcatalog-详细介绍" class="headerlink" title="Hcatalog 详细介绍"></a>Hcatalog 详细介绍</h3></blockquote><p>Apache HCatalog是基于Apache Hadoop之上的数据表和存储管理服务。</p><p>包括:  </p><ul><li>提供一个共享的模式和数据类型的机制。  </li><li>抽象出表，使用户不必关心他们的数据怎么存储。  </li><li>提供可操作的跨数据处理工具，如Pig，MapReduce，Streaming，和Hive。  </li></ul><h2 id="大规模数据分析平台Pig"><a href="#大规模数据分析平台Pig" class="headerlink" title="大规模数据分析平台Pig"></a>大规模数据分析平台Pig</h2><blockquote><h3 id="Pig详细介绍"><a href="#Pig详细介绍" class="headerlink" title="Pig详细介绍"></a>Pig详细介绍</h3></blockquote><p>Pig是一个基于Hadoop的大规模数据分析平台，它提供的SQL-LIKE语言叫Pig Latin，该语言的编译器会把类SQL的数据分析请求转换为一系列经过优化处理的MapReduce运算。Pig为复杂的海量数据并行计算提供了一个简单的操作和编程接口。</p><p><img src="https://static.oschina.net/uploads/space/2012/1019/065510_xgGH_12.jpg" alt=""></p><h2 id="Hadoop管理监控工具Apache-Ambari"><a href="#Hadoop管理监控工具Apache-Ambari" class="headerlink" title="Hadoop管理监控工具Apache Ambari"></a>Hadoop管理监控工具Apache Ambari</h2><blockquote><h3 id="Apache-Ambari-详细介绍"><a href="#Apache-Ambari-详细介绍" class="headerlink" title="Apache Ambari 详细介绍"></a>Apache Ambari 详细介绍</h3></blockquote><p>Apache Ambari是一个基于Web的Apache Hadoop集群的供应、管理和监控。Ambari目前已支持大多数Hadoop组件，包括HDFS、MapReduce、Hive、Pig、 Hbase、Zookeper、Sqoop和Hcatalog等。</p><p>Apache Ambari 支持HDFS、MapReduce、Hive、Pig、Hbase、Zookeper、Sqoop和Hcatalog等的集中管理。也是5个顶级hadoop管理工具之一。</p><p>Ambari主要取得了以下成绩:  </p><ul><li>通过一步步的安装向导简化了集群供应。  </li><li>预先配置好关键的运维指标（metrics），可以直接查看Hadoop Core（HDFS和MapReduce）及相关项目（如HBase、Hive和HCatalog）是否健康。  </li><li>支持作业与任务执行的可视化与分析，能够更好地查看依赖和性能。   </li><li>通过一个<strong><em>完整的RESTful API</em></strong>把监控信息暴露出来，集成了现有的运维工具。  </li><li>用户界面非常直观，用户可以轻松有效地查看信息并控制集群。  </li></ul><p>Ambari使用<strong><em>Ganglia</em></strong>收集度量指标，用<strong><em>Nagios</em></strong>支持系统报警，当需要引起管理员的关注时（比如，节点停机或磁盘剩余空间不足等问题），系统将向其发送邮件。</p><p>此外，Ambari能够安装安全的（基于Kerberos）Hadoop集群，以此实现了对Hadoop 安全的支持，提供了基于角色的用户认证、授权和审计功能，并为用户管理集成了LDAP[轻量目录访问协议]和Active Directory。  </p><p><img src="https://static.oschina.net/uploads/img/201212/25102517_KO3u.jpg" alt=""></p><h2 id="分布式监控系统Ganglia"><a href="#分布式监控系统Ganglia" class="headerlink" title="分布式监控系统Ganglia"></a>分布式监控系统Ganglia</h2><blockquote><h3 id="Ganglia详细介绍"><a href="#Ganglia详细介绍" class="headerlink" title="Ganglia详细介绍"></a>Ganglia详细介绍</h3></blockquote><p>Ganglia是用于高性能计算系统（如集群和网格）的可扩展分布式监控系统。它基于针对集群联盟的分层设计。它利用广泛使用的技术，例如用于数据表示的XML，用于紧凑型，便携式数据传输的XDR和用于数据存储和可视化的RRDtool。它使用精心设计的数据结构和算法来实现非常低的每节点开销和高并发性。该实现是强大的，已被移植到广泛的操作系统和处理器架构，目前正在世界各地的数千个集群中使用。它已经被用来连接大学校园和世界各地的群集，并且可以扩展到处理具有2000个节点的群集。</p><p><img src="https://www.oschina.net/uploads/img/200812/05225208_77n8.png" alt=""></p><h2 id="监控系统Nagios"><a href="#监控系统Nagios" class="headerlink" title="监控系统Nagios"></a>监控系统Nagios</h2><blockquote><h3 id="Nagios"><a href="#Nagios" class="headerlink" title="Nagios"></a>Nagios</h3></blockquote><p>Nagios是一个监视系统运行状态和网络信息的监视系统。Nagios能监视所指定的本地或远程主机以及服务，同时提供异常通知功能等</p><p>Nagios可运行在Linux/Unix平台之上，同时提供一个可选的基于浏览器的WEB界面以方便系统管理人员查看网络状态，各种系统问题，以及日志等等。</p><p>Nagios 有一个 Windows 下的客户端： <a href="http://www.oschina.net/p/nsclientpp" target="_blank" rel="noopener">http://www.oschina.net/p/nsclientpp</a><br>Nagios的主要功能特点：   </p><ul><li>监视网络服务 (SMTP, POP3, HTTP, NNTP, PING等)   </li><li>监视主机资源 (进程, 磁盘等)   </li><li>简单的插件设计可以轻松扩展Nagios的监视功能   </li><li>服务等监视的并发处理   </li><li>错误通知功能 (通过email, pager, 或其他用户自定义方法)   </li><li>可指定自定义的事件处理控制器   </li><li>可选的基于浏览器的WEB界面以方便系统管理人员查看网络状态，各种系统问题，以及日志等等  </li><li>可以通过手机查看系统监控信息  </li></ul><h2 id="开源集群计算环境Apache-Spark"><a href="#开源集群计算环境Apache-Spark" class="headerlink" title="开源集群计算环境Apache Spark"></a>开源集群计算环境Apache Spark</h2><blockquote><p>Apache Spark详细介绍</p></blockquote><p>Apache Spark 是一种与 Hadoop 相似的开源集群计算环境，但是两者之间还存在一些不同之处，这些有用的不同之处使 Spark 在某些工作负载方面表现得更加优越，换句话说，Spark 启用了内存分布数据集，除了能够提供交互式查询外，它还可以优化迭代工作负载。</p><p>Spark 是在 Scala 语言中实现的，它将 Scala 用作其应用程序框架。与 Hadoop 不同，Spark 和 Scala 能够紧密集成，其中的 Scala 可以像操作本地集合对象一样轻松地操作分布式数据集。</p><p>尽管创建 Spark 是为了支持分布式数据集上的迭代作业，但是实际上它是对 Hadoop 的补充，可以在 Hadoo 文件系统中并行运行。通过名为 Mesos 的第三方集群框架可以支持此行为。Spark 由加州大学伯克利分校 AMP 实验室 (Algorithms, Machines, and People Lab) 开发，可用来构建大型的、低延迟的数据分析应用程序。</p><p><img src="https://static.oschina.net/uploads/img/201210/18081142_mrQd.png" alt="">           </p><p><img src="https://static.oschina.net/uploads/img/201404/19090754_rALi.png" alt=""></p><h2 id="基于Hadoop的实时查询Cloudera-Impala"><a href="#基于Hadoop的实时查询Cloudera-Impala" class="headerlink" title="基于Hadoop的实时查询Cloudera Impala"></a>基于Hadoop的实时查询Cloudera Impala</h2><blockquote><p>Cloudera Impala详细的介绍</p></blockquote><p>Cloudera 发布实时查询开源项目 Impala (黑斑羚)！多款产品实测表明，比原来基于MapReduce的Hive SQL查询速度提升3～90倍。Impala是Google Dremel的模仿，但在SQL功能上青出于蓝胜于蓝。</p><p>Impala采用与Hive相同的元数据、SQL语法、ODBC驱动程序和用户接口(Hue Beeswax)，这样在使用CDH产品时，批处理和实时查询的平台是统一的。目前支持的文件格式是文本文件和SequenceFiles（可以压缩为Snappy、GZIP和BZIP，前者性能最好）。其他格式如Avro, RCFile, LZO文本和Doug Cutting的Trevni将在正式版中支持。</p><p><img src="https://static.oschina.net/uploads/space/2012/1025/102003_S0XK_12.jpg" alt=""></p><h2 id="Hadoop柱状存储格式Parquet"><a href="#Hadoop柱状存储格式Parquet" class="headerlink" title="Hadoop柱状存储格式Parquet"></a>Hadoop柱状存储格式Parquet</h2><blockquote><h3 id="Parquet详细介绍"><a href="#Parquet详细介绍" class="headerlink" title="Parquet详细介绍"></a>Parquet详细介绍</h3></blockquote><p>Parquet是一种面向列存存储的文件格式，Cloudera的大数据在线分析（OLAP）项目Impala中使用该格式作为列存储。</p><p>Apache Parquet 是一个列存储格式，主要用于 Hadoop 生态系统。对数据处理框架、数据模型和编程语言无关。</p><h2 id="OLAP分析引擎Apache-Kylin"><a href="#OLAP分析引擎Apache-Kylin" class="headerlink" title="OLAP分析引擎Apache Kylin"></a>OLAP分析引擎Apache Kylin</h2><blockquote><h3 id="Apache-Kylin详细介绍"><a href="#Apache-Kylin详细介绍" class="headerlink" title="Apache Kylin详细介绍"></a>Apache Kylin详细介绍</h3></blockquote><p>Apache Kylin 是一个开源的分布式的 OLAP 分析引擎，来自 eBay 公司开发，基于 Hadoop 提供 SQL 接口和 OLAP 接口，支持 TB 到 PB 级别的数据量。</p><p>Apache kylin是:  </p><ul><li>超级快的OLAP引擎，具备可伸缩性  </li><li>为Hadoop提供ANSI-SQL接口   </li><li>交互式查询能力   </li><li>MOLAP Cube  </li><li>可与其他BI工具无缝集成，如Tableau,而Microstrategy和Excel将很快推出</li></ul><p>Apache kylin总结的特点</p><ul><li>通过空间换时间-&gt;实现了亚秒级别延迟——&gt;提供了一个交互式的查询  </li><li>预计算，计算结果保存在HBase中，基于行的关系模式转换为基于键值对的列式模式  </li><li>维度组合，查询访问不需要扫描表  </li><li>提供SQL接口 </li></ul><p>其他值得关注的特性包括：  </p><ul><li>作业管理和监控  </li><li>压缩和编码的支持  </li><li>Cube 的增量更新  </li><li>Leverage HBase Coprocessor for query latency  </li><li>Approximate Query Capability for distinct Count (HyperLogLog)  </li><li>易用的 Web 管理、构建、监控和查询 Cube 的接口  </li><li>Security capability to set ACL at Cube/Project Level  </li><li>支持 LDAP 集成   </li></ul><p><img src="https://static.oschina.net/uploads/space/2014/1009/072755_6Bee_5189.png" alt=""></p><h2 id="分布式实时计算系统Apache-Storm"><a href="#分布式实时计算系统Apache-Storm" class="headerlink" title="分布式实时计算系统Apache Storm"></a>分布式实时计算系统Apache Storm</h2><blockquote><h3 id="Apache-Storm详细介绍"><a href="#Apache-Storm详细介绍" class="headerlink" title="Apache Storm详细介绍"></a>Apache Storm详细介绍</h3></blockquote><p>Apache Storm 的前身是 Twitter Storm 平台，目前已经归于 Apache 基金会管辖。</p><p>Apache Storm 是一个免费开源的分布式实时计算系统。简化了流数据的可靠处理，像 Hadoop 一样实现实时批处理。Storm 很简单，可用于任意编程语言。Apache Storm 采用 Clojure 开发。</p><p>Storm 有很多应用场景，包括实时数据分析、联机学习、持续计算、分布式 RPC、ETL 等。Storm 速度非常快，一个测试在单节点上实现每秒一百万的组处理。</p><p>目前已经有包括阿里百度在内的数家大型互联网公司在使用该平台。</p><p><img src="https://static.oschina.net/uploads/img/201410/01065722_3geS.png" alt=""></p><h2 id="分布式系统协调Zookeeper"><a href="#分布式系统协调Zookeeper" class="headerlink" title="分布式系统协调Zookeeper"></a>分布式系统协调Zookeeper</h2><blockquote><h3 id="Zookeeper详细介绍"><a href="#Zookeeper详细介绍" class="headerlink" title="Zookeeper详细介绍"></a>Zookeeper详细介绍</h3></blockquote><p>ZooKeeper是Hadoop的正式子项目，它是一个针对大型分布式系统的可靠协调系统，提供的功能包括：配置维护、名字服务、分布式同步、组服务等。ZooKeeper的目标就是封装好复杂易出错的关键服务，将简单易用的接口和性能高效、功能稳定的系统提供给用户。</p><p>Zookeeper是Google的Chubby一个开源的实现.是高有效和可靠的协同工作系统.Zookeeper能够用来leader选举,配置信息维护等.在一个分布式的环境中,我们需要一个Master实例或存储一些配置信息,确保文件写入的一致性等.Zookeeper能够保证如下3点:</p><ul><li><p>Watches are ordered with respect to other events, other watches, and<br>asynchronous replies. The ZooKeeper client libraries ensures that<br>everything is dispatched in order.</p></li><li><p>A client will see a watch event for a znode it is watching before seeing the new data that corresponds to that znode.</p></li><li><p>The order of watch events from ZooKeeper corresponds to the order of the updates as seen by the ZooKeeper service.</p></li></ul><p>在Zookeeper中,znode是一个跟Unix文件系统路径相似的节点,可以往这个节点存储或获取数据.如果在创建znode时Flag设置 为EPHEMERAL,那么当这个创建这个znode的节点和Zookeeper失去连接后,这个znode将不再存在在Zookeeper 里.Zookeeper使用Watcher察觉事件信息,当客户端接收到事件信息,比如连接超时,节点数据改变,子节点改变,可以调用相应的行为来处理数 据.Zookeeper的Wiki页面展示了如何使用Zookeeper来处理事件通知,队列,优先队列,锁,共享锁,可撤销的共享锁,两阶段提交.</p><p>那么Zookeeper能帮我们作什么事情呢?简单的例子:假设我们我们有个20个搜索引擎的服务器(每个负责总索引中的一部分的搜索任务)和一个 总服务器(负责向这20个搜索引擎的服务器发出搜索请求并合并结果集),一个备用的总服务器(负责当总服务器宕机时替换总服务器),一个web的 cgi(向总服务器发出搜索请求).搜索引擎的服务器中的15个服务器现在提供搜索服务,5个服务器正在生成索引.这20个搜索引擎的服务器经常要让正在 提供搜索服务的服务器停止提供服务开始生成索引,或生成索引的服务器已经把索引生成完成可以搜索提供服务了.使用Zookeeper可以保证总服务器自动 感知有多少提供搜索引擎的服务器并向这些服务器发出搜索请求,备用的总服务器宕机时自动启用备用的总服务器,web的cgi能够自动地获知总服务器的网络 地址变化.这些又如何做到呢?</p><ol><li><p>提供搜索引擎的服务器都在Zookeeper中创建znode,zk.create(“/search/nodes/node1”,<br>“hostname”.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateFlags.EPHEMERAL);</p></li><li><p>总服务器可以从Zookeeper中获取一个znode的子节点的列表,zk.getChildren(“/search/nodes”, true);</p></li><li><p>总服务器遍历这些子节点,并获取子节点的数据生成提供搜索引擎的服务器列表.</p></li><li><p>当总服务器接收到子节点改变的事件信息,重新返回第二步.</p></li><li><p>总服务器在Zookeeper中创建节点,zk.create(“/search/master”, “hostname”.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateFlags.EPHEMERAL);</p></li><li><p>备用的总服务器监控Zookeeper中的”/search/master”节点.当这个znode的节点数据改变时,把自己启动变成总服务器,并把自己的网络地址数据放进这个节点.</p></li><li><p>web的cgi从Zookeeper中”/search/master”节点获取总服务器的网络地址数据并向其发送搜索请求.</p></li><li><p>web的cgi监控Zookeeper中的”/search/master”节点,当这个znode的节点数据改变时,从这个节点获取总服务器的网络地址数据,并改变当前的总服务器的网络地址.</p></li></ol><p><img src="https://www.oschina.net/uploads/img/200810/27115143_nkDj.png" alt=""></p><h2 id="Hadoop和数据库数据迁移工具Sqoop"><a href="#Hadoop和数据库数据迁移工具Sqoop" class="headerlink" title="Hadoop和数据库数据迁移工具Sqoop"></a>Hadoop和数据库数据迁移工具Sqoop</h2><blockquote><h3 id="Sqoop详细介绍"><a href="#Sqoop详细介绍" class="headerlink" title="Sqoop详细介绍"></a>Sqoop详细介绍</h3></blockquote><p>Sqoop是一个用来将Hadoop和关系型数据库中的数据相互转移的工具，可以将一个关系型数据库（例如 ： MySQL ,Oracle ,Postgres等）中的数据导入到Hadoop的HDFS中，也可以将HDFS的数据导入到关系型数据库中。</p><p><img src="https://static.oschina.net/uploads/img/201108/24081823_kseF.gif" alt=""></p><h2 id="日志服务器Apache-Flume"><a href="#日志服务器Apache-Flume" class="headerlink" title="日志服务器Apache Flume"></a>日志服务器Apache Flume</h2><blockquote><h3 id="Apache-Flume详细介绍"><a href="#Apache-Flume详细介绍" class="headerlink" title="Apache Flume详细介绍"></a>Apache Flume详细介绍</h3></blockquote><p>Flume 是一个分布式、可靠和高可用的服务，用于收集、聚合以及移动大量日志数据，使用一个简单灵活的架构，就流数据模型。这是一个可靠、容错的服务。</p><p><img src="https://static.oschina.net/uploads/img/201207/28074231_wS4R.png" alt=""></p><h2 id="分布式发布订阅消息系统kafka"><a href="#分布式发布订阅消息系统kafka" class="headerlink" title="分布式发布订阅消息系统kafka"></a>分布式发布订阅消息系统kafka</h2><blockquote><h3 id="Kafka详细介绍"><a href="#Kafka详细介绍" class="headerlink" title="Kafka详细介绍"></a>Kafka详细介绍</h3></blockquote><p>Kafka是一种高吞吐量的分布式发布订阅消息系统，她有如下特性：  </p><ul><li>通过O(1)的磁盘数据结构提供消息的持久化，这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能。  </li><li>高吞吐量：即使是非常普通的硬件kafka也可以支持每秒数十万的消息。  </li><li>支持通过kafka服务器和消费机集群来分区消息。  </li><li>支持Hadoop并行数据加载。  </li></ul><p>kafka的目的是提供一个发布订阅解决方案，它可以处理消费者规模的网站中的所有动作流数据。 这种动作（网页浏览，搜索和其他用户的行动）是在现代网络上的许多社会功能的一个关键因素。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。 对于像Hadoop的一样的日志数据和离线分析系统，但又要求实时处理的限制，这是一个可行的解决方案。kafka的目的是通过Hadoop的并行加载机制来统一线上和离线的消息处理，也是为了通过集群机来提供实时的消费。</p><p><img src="https://static.oschina.net/uploads/img/201501/04163400_TClW.png" alt=""></p><p><img src="https://static.oschina.net/uploads/img/201501/04163400_kxLs.png" alt=""></p><p><img src="https://static.oschina.net/uploads/img/201501/04163401_nfYT.png" alt=""></p><h2 id="开源计算框架Apache-Tez"><a href="#开源计算框架Apache-Tez" class="headerlink" title="开源计算框架Apache Tez"></a>开源计算框架Apache Tez</h2><blockquote><h3 id="Apache-Tez详细介绍"><a href="#Apache-Tez详细介绍" class="headerlink" title="Apache Tez详细介绍"></a>Apache Tez详细介绍</h3></blockquote><p>Tez 是 Apache 最新的支持 DAG 作业的开源计算框架，它可以将多个有依赖的作业转换为一个作业从而大幅提升DAG作业的性能。Tez并不直接面向最终用户——事实上它允许开发者为最终用户构建性能更快、扩展性更好的应用程序。Hadoop传统上是一个大量数据批处理平台。但是，有很多用例需要近乎实时的查询处理性能。还有一些工作则不太适合MapReduce，例如机器学习。Tez的目的就是帮助Hadoop处理这些用例场景。</p><p><img src="http://tez.apache.org/images/PigHiveQueryOnMR.png" alt=""></p><p><img src="http://tez.apache.org/images/PigHiveQueryOnTez.png" alt=""></p><h2 id="开源工作流引擎Oozie"><a href="#开源工作流引擎Oozie" class="headerlink" title="开源工作流引擎Oozie"></a>开源工作流引擎Oozie</h2><blockquote><h3 id="Oozie-详细介绍"><a href="#Oozie-详细介绍" class="headerlink" title="Oozie 详细介绍"></a>Oozie 详细介绍</h3></blockquote><p>ozie 是一个开源的工作流和协作服务引擎，基于 Apache Hadoop 的数据处理任务。Oozie 是可扩展的、可伸缩的面向数据的服务，运行在Hadoop 平台上。</p><p>Oozie 包括一个离线的Hadoop处理的工作流解决方案，以及一个查询处理 API。</p><h2 id="分布式文档存储数据库MongoDB"><a href="#分布式文档存储数据库MongoDB" class="headerlink" title="分布式文档存储数据库MongoDB"></a>分布式文档存储数据库MongoDB</h2><blockquote><h3 id="MongoDB详细介绍"><a href="#MongoDB详细介绍" class="headerlink" title="MongoDB详细介绍"></a>MongoDB详细介绍</h3></blockquote><p>MongoDB是一个介于关系数据库和非关系数据库之间的产品，是非关系数据库当中功能最丰富，最像关系数据库的。他支持的数据结构非常松散，是类似json的bjson格式，因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大，其语法有点类似于面向对象的查询语言，几乎可以实现类似关系数据库单表查询的绝大部分功能，而且还支持对数据建立索引。</p><p><strong>内部架构</strong></p><p><img src="https://static.oschina.net/uploads/space/2015/1230/183400_uCc0_12.jpg" alt=""></p><p>它的特点是高性能、易部署、易使用，存储数据非常方便。主要功能特性有：  </p><ul><li>面向集合存储，易存储对象类型的数据。  </li><li>模式自由  </li><li>支持动态查询  </li><li>支持完全索引，包含内部对象。   </li><li>支持查询。  </li><li>支持复制和故障恢复。  </li><li>使用高效的二进制数据存储，包括大型对象（如视频等）。  </li><li>自动处理碎片，以支持云计算层次的扩展性   </li><li>支持RUBY，PYTHON，JAVA，C++，PHP等多种语言。  </li><li>文件存储格式为BSON（一种JSON的扩展）   </li><li>可通过网络访问  </li></ul><p>所谓“面向集合”（Collenction-Orented），意思是数据被分组存储在数据集中，被称为一个集合（Collenction)。每个 集合在数据库中都有一个唯一的标识名，并且可以包含无限数目的文档。集合的概念类似关系型数据库（RDBMS）里的表（table），不同的是它不需要定 义任何模式（schema)。<br>模式自由（schema-free)，意味着对于存储在mongodb数据库中的文件，我们不需要知道它的任何结构定义。如果需要的话，你完全可以把不同结构的文件存储在同一个数据库里。<br>存储在集合中的文档，被存储为键-值对的形式。键用于唯一标识一个文档，为字符串类型，而值则可以是各中复杂的文件类型。我们称这种存储形式为BSON（Binary Serialized dOcument Format）。</p><h2 id="高性能的NoSQL图形数据库Neo4j"><a href="#高性能的NoSQL图形数据库Neo4j" class="headerlink" title="高性能的NoSQL图形数据库Neo4j"></a>高性能的NoSQL图形数据库Neo4j</h2><blockquote><h3 id="Neo4j详细介绍"><a href="#Neo4j详细介绍" class="headerlink" title="Neo4j详细介绍"></a>Neo4j详细介绍</h3></blockquote><p>Neo4j是一个网络——面向网络的数据库——也就是说，它是一个嵌入式的、基于磁盘的、具备完全的事务特性的Java持久化引擎，但是它将结构化数据存储在网络上而不是表中。网络（从数学角度叫做图）是一个灵活的数据结构，可以应用更加敏捷和快速的开发模式。</p><p>你可以把Neo4j看作是一个高性能的图引擎，该引擎具有成熟和健壮的数据库的所有特性。程序员工作在一个面向对象的、灵活的网络结构下而不是严格、静态的表中——但是他们可以享受到具备完全的事务特性、企业级的数据库的所有好处。</p><p><img src="https://static.oschina.net/uploads/space/2015/1230/183056_DSED_12.png" alt=""></p><h2 id="数据序列化系统Apache-Avro"><a href="#数据序列化系统Apache-Avro" class="headerlink" title="数据序列化系统Apache Avro"></a>数据序列化系统Apache Avro</h2><blockquote><h3 id="Apache-Avro详细介绍"><a href="#Apache-Avro详细介绍" class="headerlink" title="Apache Avro详细介绍"></a>Apache Avro详细介绍</h3></blockquote><p><img src="https://www.oschina.net/uploads/space/2010/1018/103447_SgGY_100925.png" alt=""></p><p>Avro（读音类似于[ævrə]）是Hadoop的一个子项目，由Hadoop的 创始人Doug Cutting（也是Lucene，Nutch等项目的创始人）牵头开发。Avro是一个数据序列化系统，设计用于支持大 批量数据交换的应用。它的主要特点有：支持二进制序列化方式，可以便捷，快速地处理大量数据；动态语言友好，Avro提供的机制使动态语言可以方便地处理 Avro数据。  </p><h2 id="容器集群管理系统Kubernetes"><a href="#容器集群管理系统Kubernetes" class="headerlink" title="容器集群管理系统Kubernetes"></a>容器集群管理系统Kubernetes</h2><blockquote><h3 id="Kubernetes详细介绍"><a href="#Kubernetes详细介绍" class="headerlink" title="Kubernetes详细介绍"></a>Kubernetes详细介绍</h3></blockquote><p>Kubernetes是一个开源的，用于管理云平台中多个主机上的容器化的应用，Kubernetes的目标是让部署容器化的应用简单并且高效（powerful）,Kubernetes提供了应用部署，规划，更新，维护的一种机制。</p><p>Kubernetes一个核心的特点就是能够自主的管理容器来保证云平台中的容器按照用户的期望状态运行着（比如用户想让apache一直运行，用户不需要关心怎么去做，Kubernetes会自动去监控，然后去重启，新建，总之，让apache一直提供服务），管理员可以加载一个微型服务，让规划器来找到合适的位置，同时，Kubernetes也系统提升工具以及人性化方面，让用户能够方便的部署自己的应用（就像canary deployments）。</p><p>现在Kubenetes着重于不间断的服务状态（比如web服务器或者缓存服务器）和原生云平台应用（Nosql）,在不久的将来会支持各种生产云平台中的各种服务，例如，分批，工作流，以及传统数据库。</p><p>在Kubenetes中，所有的容器均在Pod中运行,一个Pod可以承载一个或者多个相关的容器，在后边的案例中，同一个Pod中的容器会部署在同一个物理机器上并且能够共享资源。一个Pod也可以包含O个或者多个磁盘卷组（volumes）,这些卷组将会以目录的形式提供给一个容器，或者被所有Pod中的容器共享，对于用户创建的每个Pod,系统会自动选择那个健康并且有足够容量的机器，然后创建类似容器的容器,当容器创建失败的时候，容器会被node agent自动的重启,这个node agent叫kubelet,但是，如果是Pod失败或者机器，它不会自动的转移并且启动，除非用户定义了 replication controller。</p><p>用户可以自己创建并管理Pod,Kubernetes将这些操作简化为两个操作：基于相同的Pod配置文件部署多个Pod复制品；创建可替代的Pod当一个Pod挂了或者机器挂了的时候。而Kubernetes API中负责来重新启动，迁移等行为的部分叫做“replication controller”，它根据一个模板生成了一个Pod,然后系统就根据用户的需求创建了许多冗余，这些冗余的Pod组成了一个整个应用，或者服务，或者服务中的一层。一旦一个Pod被创建，系统就会不停的监控Pod的健康情况以及Pod所在主机的健康情况，如果这个Pod因为软件原因挂掉了或者所在的机器挂掉了，replication controller 会自动在一个健康的机器上创建一个一摸一样的Pod,来维持原来的Pod冗余状态不变，一个应用的多个Pod可以共享一个机器。</p><p>我们经常需要选中一组Pod，例如，我们要限制一组Pod的某些操作，或者查询某组Pod的状态，作为Kubernetes的基本机制，用户可以给Kubernetes Api中的任何对象贴上一组 key:value的标签，然后，我们就可以通过标签来选择一组相关的Kubernetes Api 对象，然后去执行一些特定的操作，每个资源额外拥有一组（很多） keys 和 values,然后外部的工具可以使用这些keys和vlues值进行对象的检索，这些Map叫做annotations（注释）。</p><p>Kubernetes支持一种特殊的网络模型，Kubernetes创建了一个地址空间，并且不动态的分配端口，它可以允许用户选择任何想使用的端口，为了实现这个功能，它为每个Pod分配IP地址。</p><p>现代互联网应用一般都会包含多层服务构成，比如web前台空间与用来存储键值对的内存服务器以及对应的存储服务，为了更好的服务于这样的架构，Kubernetes提供了服务的抽象，并提供了固定的IP地址和DNS名称，而这些与一系列Pod进行动态关联，这些都通过之前提到的标签进行关联，所以我们可以关联任何我们想关联的Pod，当一个Pod中的容器访问这个地址的时候，这个请求会被转发到本地代理（kube proxy）,每台机器上均有一个本地代理，然后被转发到相应的后端容器。Kubernetes通过一种轮训机制选择相应的后端容器，这些动态的Pod被替换的时候,Kube proxy时刻追踪着，所以，服务的 IP地址（dns名称），从来不变。</p><p>所有Kubernetes中的资源，比如Pod,都通过一个叫URI的东西来区分，这个URI有一个UID,URI的重要组成部分是：对象的类型（比如pod），对象的名字，对象的命名空间，对于特殊的对象类型，在同一个命名空间内，所有的名字都是不同的，在对象只提供名称，不提供命名空间的情况下，这种情况是假定是默认的命名空间。UID是时间和空间上的唯一。</p><h2 id="Hadoop图形化用户界面Hue"><a href="#Hadoop图形化用户界面Hue" class="headerlink" title="Hadoop图形化用户界面Hue"></a>Hadoop图形化用户界面Hue</h2><blockquote><h3 id="Hue详细介绍"><a href="#Hue详细介绍" class="headerlink" title="Hue详细介绍"></a>Hue详细介绍</h3></blockquote><p>Hue 是运营和开发Hadoop应用的图形化用户界面。Hue程序被整合到一个类似桌面的环境，以web程序的形式发布，对于单独的用户来说不需要额外的安装。</p><p><img src="https://static.oschina.net/uploads/img/201411/10070033_gOkK.png" alt=""></p><h2 id="大数据可视化工具Nanocubes"><a href="#大数据可视化工具Nanocubes" class="headerlink" title="大数据可视化工具Nanocubes"></a>大数据可视化工具Nanocubes</h2><blockquote><h3 id="Nanocubes-详细介绍"><a href="#Nanocubes-详细介绍" class="headerlink" title="Nanocubes 详细介绍"></a>Nanocubes 详细介绍</h3></blockquote><p>Nanocubes 是一个大数据可视化的工具，32Tb Twitter数据，在一台16GB内存的机器上流畅、交互式地可视化。</p><p><img src="https://static.oschina.net/uploads/space/2014/0109/085829_QGhT_12.jpg" alt=""></p><h2 id="Hadoop集群监控工具HTools"><a href="#Hadoop集群监控工具HTools" class="headerlink" title="Hadoop集群监控工具HTools"></a>Hadoop集群监控工具HTools</h2><blockquote><p>HTools详细介绍 </p></blockquote><p>HTools是一款专业的Hadoop管理工具，不管您是非专业IT人士，还是多年经验的技术人员，本工具都会为您提供优质的管理服务和轻松的操作过程， 释放无谓的工作压力，提高Hadoop的管理水平。我们以最权威的专家为您量身定做的Hadoop管理工具，本系统提供优秀的用户体验，让您能够轻松的管 理Hadoop集群环境。</p><p>友善的向导式操作流程<br>图形报表、日志分析供您明了查看各节点使用情况<br>智能诊断,修复故障并发出短信、邮件故障告警<br>图形化UI、拖拖拽拽即可管理管理HDFS数据<br>傻瓜式操作优化Hadoop,方便快捷</p><p>免客户端部署,无需安装HTools客户端<br>版本控制灵活,不绑定Hadoop的JDK版本<br>一键智能搜索当前网段可部署节点<br>支持多个Hadoop集群同时监管</p><p>支持同时管理多个Hadoop集群和节点<br>支持7 &times; 24小时多集群实时监控<br>支持节点热插拔,服务不间断的情况下随时对节点进行扩展和调整<br>支持系统配置文件的推送和同步</p><p><img src="https://static.oschina.net/uploads/img/201312/24101139_sKGV.png" alt=""></p><h2 id="大数据查询引擎PrestoDB"><a href="#大数据查询引擎PrestoDB" class="headerlink" title="大数据查询引擎PrestoDB"></a>大数据查询引擎PrestoDB</h2><blockquote><h3 id="PrestoDB详细介绍"><a href="#PrestoDB详细介绍" class="headerlink" title="PrestoDB详细介绍"></a>PrestoDB详细介绍</h3></blockquote><p>Presto是Facebook最新研发的数据查询引擎，可对250PB以上的数据进行快速地交互式分析。据称该引擎的性能是 Hive 的 10 倍以上。</p><p>PrestoDB 是 Facebook 推出的一个大数据的分布式 SQL 查询引擎。可对从数 G 到数 P 的大数据进行交互式的查询，查询的速度达到商业数据仓库的级别。</p><p>Presto 可以查询包括 Hive、Cassandra 甚至是一些商业的数据存储产品。单个 Presto 查询可合并来自多个数据源的数据进行统一分析。</p><p>Presto 的目标是在可期望的响应时间内返回查询结果。Facebook 在内部多个数据存储中使用 Presto 交互式查询，包括 300PB 的数据仓库，超过 1000 个 Facebook 员工每天在使用 Presto 运行超过 3 万个查询，每天扫描超过 1PB 的数据。此外包括 Airbnb 和 Dropbox 也在使用 Presto 产品。</p><p>Presto 是一个分布式系统，运行在集群环境中，完整的安装包括一个协调器 (coordinator) 和多个 workers。查询通过例如 Presto CLI 的客户端提交到协调器，协调器负责解析、分析和安排查询到不同的 worker 上执行。</p><p>此外，Presto 需要一个数据源来运行查询。当前 Presto 包含一个插件用来查询 Hive 上的数据，要求：</p><ul><li><p>Hadoop CDH4</p></li><li><p>远程 Hive metastore service</p></li></ul><p>Presto 不使用 MapReduce ，只需要 HDFS</p><p><img src="https://static.oschina.net/uploads/space/2013/1107/071851_lSFS_12.png" alt=""></p><h2 id="大数据批处理和流处理标准Apache-Beam"><a href="#大数据批处理和流处理标准Apache-Beam" class="headerlink" title="大数据批处理和流处理标准Apache Beam"></a>大数据批处理和流处理标准Apache Beam</h2><blockquote><h3 id="Apache-Beam详细介绍"><a href="#Apache-Beam详细介绍" class="headerlink" title="Apache Beam详细介绍"></a>Apache Beam详细介绍</h3></blockquote><p>Apache Beam 是 Apache 软件基金会越来越多的数据流项目中最新增添的成员，是 Google 在2016年2月份贡献给 Apache 基金会的孵化项目。</p><p>这个项目的名称表明了设计：结合了批处理（Batch）模式和数据流（Stream）处理模式。它基于一种统一模式，用于定义和执行数据并行处理管道（pipeline），这些管理随带一套针对特定语言的SDK用于构建管道，以及针对特定运行时环境的Runner用于执行管道。</p><p>Apache Beam 的主要目标是统一批处理和流处理的编程范式，为无限，乱序，web-scale的数据集处理提供简单灵活，功能丰富以及表达能力十分强大的SDK。Apache Beam项目重点在于数据处理的编程范式和接口定义，并不涉及具体执行引擎的实现，Apache Beam希望基于Beam开发的数据处理程序可以执行在任意的分布式计算引擎上。</p><p><img src="https://static.oschina.net/uploads/space/2016/1113/215003_JDS8_2896879.jpeg" alt=""></p><h2 id="安全大数据分析框架OpenSOC"><a href="#安全大数据分析框架OpenSOC" class="headerlink" title="安全大数据分析框架OpenSOC"></a>安全大数据分析框架OpenSOC</h2><blockquote><h3 id="OpenSOC详细介绍"><a href="#OpenSOC详细介绍" class="headerlink" title="OpenSOC详细介绍"></a>OpenSOC详细介绍</h3></blockquote><p>OpenSOC：安全大数据分析框架。OpenSOC已经加入Apache工程改名为Apache Metron。</p><p>思科在 BroCON 大会上亮相了其安全大数据分析架构 OpenSOC，引起了广泛关注。OpenSOC 是一个针对网络包和流的大数据分析框架，它是大数据分析与安全分析技术的结合, 能够实时的检测网络异常情况并且可以扩展很多节点，它的存储使用开源项目 Hadoop，实时索引使用开源项目 <strong>ElasticSearch</strong>，在线流分析使用著名的开源项目 <strong>Storm</strong>。OpenSOC 概念性体系架构如下图所示:</p><p><img src="https://static.oschina.net/uploads/space/2014/1220/085108_1UUV_12.png" alt=""></p><p>OpenSOC 主要功能包括：</p><ul><li><p>可扩展的接收器和分析器能够监视任何Telemetry数据源</p></li><li><p>是一个扩展性很强的框架，且支持各种Telemetry数据流</p></li><li><p>支持对Telemetry数据流的异常检测和基于规则实时告警</p></li><li><p>通过预设时间使用Hadoop存储Telemetry的数据流</p></li><li><p>支持使用ElasticSearch实现自动化实时索引Telemetry数据流</p></li><li><p>支持使用Hive利用SQL查询存储在Hadoop中的数据</p></li><li><p>能够兼容ODBC/JDBC和继承已有的分析工具</p></li><li><p>具有丰富的分析应用,且能够集成已有的分析工具</p></li><li><p>支持实时的Telemetry搜索和跨Telemetry的匹配</p></li><li><p>支持自动生成报告、和异常报警</p></li><li><p>支持原数据包的抓取、存储、重组</p></li><li><p>支持数据驱动的安全模型</p></li></ul><p>OpenSOC 官方文档介绍了以下五大优点：</p><ul><li><p>由思科全力支持，适用于内部多用户</p></li><li><p>免费、开源、基于Apache协议授权</p></li><li><p>基于高可扩展平台（Hadoop、Kafka、Storm）实现</p></li><li><p>基于可扩展的插件式设计</p></li><li><p>具有灵活的部署模式，可在企业内部部署或者云端部署</p></li><li><p>具有集中化的管理流程、人员和数据</p></li></ul><h2 id="实时网络安全监测框架Apache-Metron"><a href="#实时网络安全监测框架Apache-Metron" class="headerlink" title="实时网络安全监测框架Apache Metron"></a>实时网络安全监测框架Apache Metron</h2><blockquote><h3 id="Apache-Metron详细介绍"><a href="#Apache-Metron详细介绍" class="headerlink" title="Apache Metron详细介绍"></a>Apache Metron详细介绍</h3></blockquote><p>Apache Metron 是一个网络安全的实时数据处理、分析、查询、可视化框架。</p><p>Metron 集成了各种开源大数据技术，为安全监控和分析提供了集中工具。 Metron 拥有支持大规模摄取、处理、检索与信息可视化的所有适当元素，一些关键的网络数据将推动数据保护、监控、分析与检测，并且有助于对恶意的非法行为予以回应。</p><p>亮点包括：</p><ul><li><p>捕获、存储和规范化所有类型的安全机制;</p></li><li><p>高速远程检测;</p></li><li><p>实时处理和应用改进;</p></li><li><p>高效信息存储;</p></li><li><p>提供通过系统传递的数据和警报的集中视图的接口</p></li><li><p>使用统计摘要数据结构，即使在最大的数据集上也可执行安全分析</p></li></ul><h2 id="企业级流式计算引擎JStorm"><a href="#企业级流式计算引擎JStorm" class="headerlink" title="企业级流式计算引擎JStorm"></a>企业级流式计算引擎JStorm</h2><blockquote><h3 id="JStorm-详细介绍"><a href="#JStorm-详细介绍" class="headerlink" title="JStorm 详细介绍"></a>JStorm 详细介绍</h3></blockquote><p>JStorm 是参考 Apache Storm 实现的实时流式计算框架，在网络IO、线程模型、资源调度、可用性及稳定性上做了持续改进，已被越来越多企业使用。JStorm 可以看作是 storm 的java增强版本，除了内核用纯java实现外，还包括了thrift、python、facet ui。从架构上看，其本质是一个基于zk的分布式调度系统</p><p>JStorm 的性能是Apache Storm 的4倍， 可以自由切换行模式或 mini-batch 模式：</p><p><img src="https://static.oschina.net/uploads/img/201701/10171441_7ui5.png" alt=""></p><p><img src="https://static.oschina.net/uploads/img/201701/10171441_5G2r.png" alt=""></p>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="大数据" scheme="https://icocos.github.io/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="平台" scheme="https://icocos.github.io/tags/%E5%B9%B3%E5%8F%B0/"/>
    
  </entry>
  
  <entry>
    <title>大数据之常见面试题</title>
    <link href="https://icocos.github.io/2020/04/04/%E5%A4%A7%E6%95%B0%E6%8D%AE%E4%B9%8B%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
    <id>https://icocos.github.io/2020/04/04/大数据之常见面试题/</id>
    <published>2020-04-04T15:56:50.000Z</published>
    <updated>2020-04-09T14:45:08.091Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h2 id="大数据–面试题一览"><a href="#大数据–面试题一览" class="headerlink" title="大数据–面试题一览"></a>大数据–面试题一览</h2><h2 id="20180524"><a href="#20180524" class="headerlink" title="[20180524]"></a>[20180524]</h2><ul><li>阐述HDFS生成文件的过程</li><li>Hadoop有哪些优化，调优点</li><li>阐述对Hive分区的理解</li><li>Hive分桶</li><li>用Spark干过什么</li><li>你们公司生产的集群规模</li><li>懂不懂CDH</li></ul><h2 id="20180508-七牛云面试题"><a href="#20180508-七牛云面试题" class="headerlink" title="[20180508] 七牛云面试题"></a>[20180508] 七牛云面试题</h2><ul><li>快排</li><li>hive和hdfs之间的联系</li><li>inode和文件描述符</li><li>linux指令如何创建文件 </li><li>http中header中放入key value 有什么变化</li><li>系统调用和库函数区别</li><li>http缓冲实现机智</li><li>session cookie  区别</li><li>进程间通信方式</li><li>jsp本质</li><li>http请求状状态</li><li>get post put remove</li><li>数据库join </li><li>数据库引擎</li><li>hibernate和mybiters区别</li><li>jvm垃圾回收</li><li>hive和关系型数据库区别</li><li>hive实现原理</li><li>spark与mr的区别  </li></ul><h2 id="20180502-二三四五面试题"><a href="#20180502-二三四五面试题" class="headerlink" title="[20180502] 二三四五面试题"></a>[20180502] 二三四五面试题</h2><ul><li>画图讲解Spark工作流程，以及在集群上和各个角色的对应关系</li><li>Spark Streaming程序代码更新后如何操作</li><li>在一个电商网站中，设计一个订单ID生成方案</li><li>spark-submit如何引入外部jar包</li><li>Spark对于OOM从什么角度下手调整</li><li>org.apache.spark.SparkExectption:Task not serializable，这个错误是什么意思？如何解决？哪些场景会出现这错误？</li></ul><h2 id="20180427-面试题"><a href="#20180427-面试题" class="headerlink" title="[20180427] 面试题"></a>[20180427] 面试题</h2><ul><li>有10个文件，每个文件1G，每个文件的每一行存放的都是用户的query，每个文件的query都可能重复。要求你按照query的频度排序</li><li>有一个1G大小的一个文件，里面每一行是一个词，词的大小不超过16字节，内存限制大小是1M。返回频数最高的100个词</li></ul><h2 id="20180426-美图二面"><a href="#20180426-美图二面" class="headerlink" title="[20180426] 美图二面"></a>[20180426] 美图二面</h2><ul><li>ThriftServer的HA如何去实现，能说下实现的思路嘛</li><li>说下Zookeeper的watch机制是如何实现的嘛？</li><li>场景题：<ul><li>现在有1个client，2个server，当我动态加入一台机器，或者删除一台机器，或者某台机器宕机了，client该如何去感知到，说下实现思路(不使用Zookeeper)</li><li>如何通信，说说具体实现</li></ul></li></ul><h2 id="20180425-蚂蚁金服编程题"><a href="#20180425-蚂蚁金服编程题" class="headerlink" title="[20180425] 蚂蚁金服编程题"></a>[20180425] 蚂蚁金服编程题</h2><ul><li><p>编程题A：求一个整数的平方根，不保留小数。</p></li><li><p>编程题B：</p><ul><li>1.年/月/日/xxx.jpg ， 文件夹以 这个形式组织。</li><li>2.新建文件夹，将所有jpg文件拷贝到该文件夹，更名为 年_月_日_xxx.jpg。</li><li>3.监控文件夹，如果有增加的jpg文件，自动同步到新文件夹。</li><li>4.jpg文件只增加不删除。</li></ul></li></ul><h2 id="20180424-成都某公司面试题"><a href="#20180424-成都某公司面试题" class="headerlink" title="[20180424] 成都某公司面试题"></a>[20180424] 成都某公司面试题</h2><ul><li>谈谈你对HDFS的了解</li><li>Hadoop2.0做了哪些改动</li><li>Spark与MR的区别在哪里</li><li>知道除了Spark之外的大数据处理框架嘛</li><li>Spark shuffle，说说</li><li>StringBuilder与StringBuffer的区别</li><li>HashMap与Hashtable的区别</li><li>二叉树的数据结构是什么样的</li><li>数据库索引的实现原理</li><li>jvm垃圾收集器，挑一种讲讲</li></ul><h2 id="20180423-美图面试题"><a href="#20180423-美图面试题" class="headerlink" title="[20180423] 美图面试题"></a>[20180423] 美图面试题</h2><ul><li>为什么选择美图，你知道美图地点在哪里嘛</li><li>介绍下你做的项目吧</li><li>数据统一管理平台，我挺感兴趣的，你说说吧</li><li>我大概知道是怎么回事了，java web这块你参与开发了吗</li><li>你刚刚项目提到了元数据，你能说说hive的元数据管理嘛，对它了解嘛</li><li>还是hive，你对hive有哪些原理性了解呢</li><li>知道AST、operator tree这些长什么样吗</li><li>那你的hive转mr过程是怎么了解的呢？</li><li>除了谓词下推，还能说说其它的优化嘛？别说数据倾斜的调优</li><li>jvm了解不，说下垃圾收集算法</li><li>平常用java和scala语言哪个多点</li><li>如果我现在要使用map集合，你觉得哪种适合多线程情况下进行访问</li><li>如何去监控线程</li><li>Spark 出现OOM，你觉得该怎么进行调优呢？不去动jvm的参数</li><li>你觉得join该怎么优化</li><li>你对未来的规划是什么？(五年内)</li><li>你也就是走技术路线咯</li></ul><h2 id="20180421-北京3-家面试题"><a href="#20180421-北京3-家面试题" class="headerlink" title="[20180421] 北京3+家面试题"></a>[20180421] 北京3+家面试题</h2><h4 id="hadoop面试"><a href="#hadoop面试" class="headerlink" title="hadoop面试:"></a>hadoop面试:</h4><p>1、hadoop集群、namenode如何做到数据同步？<br>2、hdfs副本存放策略<br>3、HA如何在挂掉一台namenode节点的状态下，自动切换到另一台？<br>4、mapreduce shuffle过程<br>5、mapreduce优化  </p><h4 id="flume面试"><a href="#flume面试" class="headerlink" title="flume面试:"></a>flume面试:</h4><p>1、你能二次源码修改支持parquent格式吗？  </p><h4 id="sqoop面试"><a href="#sqoop面试" class="headerlink" title="sqoop面试:"></a>sqoop面试:</h4><p>1、抽取某个数据库下的某张表+条件  怎么抽取?<br>2、sqoop增量导入  </p><h4 id="hbase面试"><a href="#hbase面试" class="headerlink" title="hbase面试:"></a>hbase面试:</h4><p>1、rowkey如何设计<br>2、hbase热点问题<br>3、协处理器<br>4、hbase优化<br>5、hbase的二级索引  </p><h4 id="hive面试"><a href="#hive面试" class="headerlink" title="hive面试:"></a>hive面试:</h4><p>1、数据倾斜<br>2、hive能加索引吗？  </p><h4 id="spark面试"><a href="#spark面试" class="headerlink" title="spark面试:"></a>spark面试:</h4><p>1、rdd dataset dataframe 概念<br>2、mapflat<br>3、spark资源分配  </p><h4 id="kafka面试"><a href="#kafka面试" class="headerlink" title="kafka面试:"></a>kafka面试:</h4><p>1、怎么保证数据零丢失?和spark streaming结合说说看？<br>2、怎么解决数据重复问题？<br>3、某个kafka节点挂掉对生产和消费有影响吗？<br>4、生产大于消费 lag产生大量的滞后怎么解决？  </p><h4 id="数据库面试"><a href="#数据库面试" class="headerlink" title="数据库面试:"></a>数据库面试:</h4><p>1、btree<br>2、索引<br>3、拉链表  </p><h4 id="shell面试"><a href="#shell面试" class="headerlink" title="shell面试:"></a>shell面试:</h4><p>1、如何查找在Linux目录下的某个文本里的包含相关内容的操作?  </p><h2 id="20180420-蚂蚁金服面试题"><a href="#20180420-蚂蚁金服面试题" class="headerlink" title="[20180420] 蚂蚁金服面试题"></a>[20180420] 蚂蚁金服面试题</h2><ul><li>小文件的合并</li><li>MR与Spark的区别       </li><li>关注哪些名人的博客</li><li>对大数据领域有什么自己的见解    </li><li>平常怎么学习大数据的</li><li>StringBuilder与StringBuffer的区别</li><li>HashMap与Hashtable的区别</li><li>谈谈你对树的理解</li><li>数据库索引的实现</li><li>jvm的内存模型</li><li>jvm的垃圾收集器</li><li>jvm的垃圾收集算法</li><li>HDFS架构</li><li>HDFS读写流程</li><li>Hadoop3.0做了哪些改进</li><li>谈谈YARN</li><li>为什么项目选择使用Spark，你觉得Spark的优点在哪里</li><li>了解Flink与Storm嘛，他们与Spark Streaming的区别在哪里</li><li>1TB文件，取重复的词，top5指定的资源的场景下，如何快速统计出来</li></ul><h2 id="20180419-网易大数据面试题"><a href="#20180419-网易大数据面试题" class="headerlink" title="[20180419] 网易大数据面试题"></a>[20180419] 网易大数据面试题</h2><ul><li>说说项目</li><li>Spark哪部分用得好，如何调优</li><li>Java哪部分了解比较好</li><li>聊聊并发，并发实现方法，volatile关键字说说</li><li>HashMap的底层原理</li><li>为什么要重写hashcode和equals</li><li>说说jvm</li><li>各个垃圾收集器运用在什么情形</li><li>jvm调优</li><li>说说io</li><li>为什么考虑转行呢？是因为原专业不好就业吗？</li></ul><h2 id="20180418-数据挖掘面试题"><a href="#20180418-数据挖掘面试题" class="headerlink" title="[20180418] 数据挖掘面试题"></a>[20180418] 数据挖掘面试题</h2><ul><li>Java字符串拼接StringBuffer和+=区别</li><li>Scala map和foreach区别 </li><li>Spark groupByKey和reduceByKey区别</li><li>Spark将数据写MySQL要注意什么</li><li>Spark repartition和coalesce函数的区别</li><li>梯度下降、随机梯度下降、mini batch 梯度下降的区别</li><li>SVM原理</li><li>SVM中为什么要转成对偶问题</li><li>SVM在分类时怎么选择合适的核函数</li><li>特征共线性问题</li><li>Hive外表和内表的区别</li><li>求解字符串的所有的回文子串</li><li>贝叶斯定理</li><li>人员画像</li><li>推荐系统 svd knn</li></ul><h2 id="20180417"><a href="#20180417" class="headerlink" title="[20180417]"></a>[20180417]</h2><ul><li>自我介绍</li><li>最近一个项目的架构，你所负责的模块</li><li>谈谈你对Spark的理解</li><li>在这个项目中，你觉得你做的模板中出彩的地方与哪些</li><li>Spark作业提交的流程</li><li>在工作中使用Spark遇到了哪些问题，如何解决的，请举3个例子</li><li>谈谈你对JVM的了解</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="大数据" scheme="https://icocos.github.io/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="面试" scheme="https://icocos.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
    
  </entry>
  
  <entry>
    <title>大数据之博客推荐</title>
    <link href="https://icocos.github.io/2020/03/26/%E5%A4%A7%E6%95%B0%E6%8D%AE%E4%B9%8B%E5%8D%9A%E5%AE%A2%E6%8E%A8%E8%8D%90/"/>
    <id>https://icocos.github.io/2020/03/26/大数据之博客推荐/</id>
    <published>2020-03-26T15:56:50.000Z</published>
    <updated>2020-04-09T14:45:08.218Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h2 id="大数据–博客一览"><a href="#大数据–博客一览" class="headerlink" title="大数据–博客一览"></a>大数据–博客一览</h2><p>大数据零基础：</p><h5 id="Linux-And-Shell"><a href="#Linux-And-Shell" class="headerlink" title="Linux And Shell:"></a>Linux And Shell:</h5><p><a href="https://ke.qq.com/course/262452?tuin=11cffd50" target="_blank" rel="noopener">零基础大数据入门【free视频】</a>  </p><p><a href="http://blog.itpub.net/30089851/viewspace-2131153/" target="_blank" rel="noopener">1.VMware Workstation9 下安装 CentOS6.5( 安装图文教程 )</a><br><a href="http://blog.itpub.net/30089851/viewspace-2131167/" target="_blank" rel="noopener">2.Linux最常用命令及快捷键整理</a><br><a href="http://blog.itpub.net/30089851/viewspace-1992210/" target="_blank" rel="noopener">3.配置多台机器SSH相互通信信任</a>   </p><p><a href="http://blog.itpub.net/30089851/viewspace-2131678/" target="_blank" rel="noopener">4.Memory参数，你真的懂吗?</a><br><a href="http://blog.itpub.net/30089851/viewspace-2120628/" target="_blank" rel="noopener">5.yum安装xxx包时出错，提示No package xxx available.</a><br><a href="http://blog.itpub.net/30089851/viewspace-2130239/" target="_blank" rel="noopener">6.CentOS6.x使用163和epel yum源的选择</a><br><a href="http://blog.itpub.net/30089851/viewspace-2129636/" target="_blank" rel="noopener">7.Centos6.5 python2.6.6 升级到2.7.5</a><br><a href="http://blog.itpub.net/30089851/viewspace-2147840/" target="_blank" rel="noopener">8.CentOS清理swap和buffer/cache</a>   </p><p><a href="http://blog.itpub.net/30089851/viewspace-2132326/" target="_blank" rel="noopener">9.记录在shell脚本中使用sudo echo x &gt; 时,抛Permission denied错误</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483967&amp;idx=1&amp;sn=ae3a4b8c3b8ec271aa25ce8355672433&amp;chksm=908f2856a7f8a140967e6c55712511fdf4582261f1be7237646a1e362771140649703a0797f3&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">10.Linux系统重要参数调优，你知道吗</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484114&amp;idx=1&amp;sn=38b76cb666d7d571c01b3784092a4cab&amp;chksm=908f28bba7f8a1adfa9868a99fbdd14a835e9470d19a57292e9889d5cd6c27161d491c5d2e40&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">11.大数据之必会的Linux命令</a></p><h5 id="DataBase-And-SQL"><a href="#DataBase-And-SQL" class="headerlink" title="DataBase And SQL:"></a>DataBase And SQL:</h5><h5 id="Hadoop"><a href="#Hadoop" class="headerlink" title="Hadoop:"></a>Hadoop:</h5><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483841&amp;idx=1&amp;sn=a34e93ca29a523a6ab34781f68a02d41&amp;chksm=908f2ba8a7f8a2bea89641d4e77a7c3daf50036c7c234eabb986a1128885d66c09cf9a50030e&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">1.Hadoop2.8.1全网最详细编译</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483731&amp;idx=1&amp;sn=40021b1c7db069b56dad3646bfa45408&amp;chksm=908f2b3aa7f8a22c7d9d2bd4b19956fee63cb02c205c0f1bf836426abf5c26f1e2e40af1f045#rd" target="_blank" rel="noopener">2.Hadoop全网最详细的伪分布式部署(HDFS)</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483744&amp;idx=1&amp;sn=9d0d61ecc3be2f71de640c9bcd7f8027&amp;chksm=908f2b09a7f8a21fe62cd8adda08acb6f2ce040d5e2b73dbcf6dd2874dcedd3baf0f64804cfc#rd" target="_blank" rel="noopener">3.Hadoop全网最详细的伪分布式部署(MapReduce+Yarn)</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483764&amp;idx=1&amp;sn=7a4c9d254f85cf8f302724a0b511e16c&amp;chksm=908f2b1da7f8a20bdc4928c13f313f379204256d809c4e7212f16574f7b4a2eb3b265d1b59cb#rd" target="_blank" rel="noopener">4.Hadoop常用命令大全01</a><br><a href="http://blog.itpub.net/30089851/viewspace-1994585/" target="_blank" rel="noopener">5.Hadoop-2.7.2+zookeeper-3.4.6完全分布式环境搭建(HDFS、YARN HA)</a><br><a href="http://blog.itpub.net/30089851/viewspace-2006108/" target="_blank" rel="noopener">6.Hadoop2.x 参数汇总</a><br><a href="http://blog.itpub.net/30089851/viewspace-2127851/" target="_blank" rel="noopener">7.YARN的Memory和CPU调优配置详解</a><br><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484061&amp;idx=1&amp;sn=2a5f030ce2ad509efa909891957f8788&amp;chksm=908f28f4a7f8a1e2519c59eed168f651365c86ef14affc0c4640b7de09936cee0bea5a0aecb4&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">8.资源调度yarn之生产详解</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484084&amp;idx=1&amp;sn=8a4bb03a613c8bb5c3ea900aee29643b&amp;chksm=908f28dda7f8a1cbbcf42ce87623ec2c7e475b156f051d368902b513827ade5bbf34520c0f0a&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">9.fsimage？editlog？这些都是什么？？</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484108&amp;idx=1&amp;sn=fa0afcdc9109ecd4876e5cf3de99199d&amp;chksm=908f28a5a7f8a1b36f9ec3e456b791af7b2c77fb1e3f7a8a5c8f4be1bcabab3a66d53c75435b&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">10.你真的了解jps命令吗</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484125&amp;idx=1&amp;sn=b1155d6f09e5d17f39f04750dc53b29c&amp;chksm=908f28b4a7f8a1a2655f396b649c8ed35ff7c939b54d2c3c27344d2e6273644c68205ea6e04d&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">11.Hadoop HA 的配置，你了解吗？</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484128&amp;idx=1&amp;sn=2b8dc90a5caf08e24f79fab4b6f0b639&amp;chksm=908f2889a7f8a19f0d4208d823cc5fd008c5b6d82baa18d67f6eef1a466126553b37114c7f84&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">12.Hadoop之Yarn架构设计(command memory cpu)</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484159&amp;idx=1&amp;sn=19e63529fb75ed897d8ce4b7c571d770&amp;chksm=908f2896a7f8a18020f40219dd9e1edadd80e9e8e8de3e7dd7d8db216690a39d9449c1f1148e&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">13.HDFS之垃圾回收箱配置及使用</a></p><h5 id="Zookeeper"><a href="#Zookeeper" class="headerlink" title="Zookeeper:"></a>Zookeeper:</h5><h5 id="Hive"><a href="#Hive" class="headerlink" title="Hive:"></a>Hive:</h5><p><a href="https://ke.qq.com/course/236561?tuin=11cffd50" target="_blank" rel="noopener">Hive应用实战课程【buy视频】</a>  </p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483748&amp;idx=1&amp;sn=3a7db2f9c8a667bcad3aa37ece461360&amp;chksm=908f2b0da7f8a21b5fb24869d7204709176fd417da2ab55afa88c3f15e45546eb8906a4b9496#rd" target="_blank" rel="noopener">1.Hive全网最详细的编译及部署</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483815&amp;idx=1&amp;sn=63b2a8307fab8d53efcb6677b8140e82&amp;chksm=908f2bcea7f8a2d80b06d5f01c8cfe0dd2382e3226d54d325d9aa51834bfcf911de01b96a484&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">2.Hive DDL，你真的了解吗？</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483821&amp;idx=1&amp;sn=996cd6ddab7fa237cfdb297466651443&amp;chksm=908f2bc4a7f8a2d28e838aa20ac34b6bcba80983df6df0bc3ae91cea9df2a91fd3c96fb1f1fe&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">3.Hive自定义函数(UDF)的编程开发，你会吗？</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483825&amp;idx=1&amp;sn=e8f240339d698d44a0aba30245437ed5&amp;chksm=908f2bd8a7f8a2ceb1cbe0a7afe847f0b23d5c61a24fa1b27c4d41bb0198c7ca48d2f9b0bd16&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">4.Hive自定义函数(UDF)的部署使用，你会吗？</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483840&amp;idx=1&amp;sn=eff7a0596d3dde834073cba4b2dbc9f4&amp;chksm=908f2ba9a7f8a2bf03e5dc2a674d7653df6c0c90acea484f77a4b802bfe1c578f152d0838fe0&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">5. 2min快速了解，Hive内部表和外部表</a>  </p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483846&amp;idx=1&amp;sn=4f1ce755bc9807121351d4f0b92c8183&amp;chksm=908f2bafa7f8a2b9580a8dc2f299eb23398cd469259730c4403571a91fc2aed8afe000ea6797&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">6. 5min掌握，Hive的HiveServer2 和JDBC客户端&amp;代码的生产使用</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483850&amp;idx=1&amp;sn=62c50ce20116818a22db8f71da7f2ab1&amp;chksm=908f2ba3a7f8a2b514bd7c4adba3d69e8f90886e9c7f01701ce385e6096efcfc782f187d6309#rd" target="_blank" rel="noopener">7.生产中Hive静态和动态分区表，该怎样抉择呢？</a> </p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483923&amp;idx=1&amp;sn=631bbb80949dad73a76a9ab5fb50f5a3&amp;chksm=908f287aa7f8a16c12838454bb634227502083d5455993bb43015da35e1368fbbcc87b714956&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">8.Hive中自定义UDAF函数生产小案例</a> </p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483951&amp;idx=1&amp;sn=d967c0495f1f08eb0f6b1e15482333c9&amp;chksm=908f2846a7f8a15006aa8fad55237095ca0562e95072de3cc8f4b18689d2b08e822e8e62feb2&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">9.从Hive中的stored as file_foramt看hive调优</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484030&amp;idx=1&amp;sn=f9bfe226a4efc3b38536caee21fa070d&amp;chksm=908f2817a7f8a10123445446fe035e04374b3c6e173831c50920fcd9d6810886814e7bc50b20&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">10.你真的了解 Hive 的元数据吗？</a></p><p>[11.hive实战  (<a href="https://blog.csdn.net/liweihope/article/details/88584985" target="_blank" rel="noopener">https://blog.csdn.net/liweihope/article/details/88584985</a>)</p><h5 id="SQOOP"><a href="#SQOOP" class="headerlink" title="SQOOP:"></a>SQOOP:</h5><p><a href="https://ke.qq.com/course/243478?tuin=11cffd50" target="_blank" rel="noopener">Sqoop应用实战课程【buy视频】</a>  </p><h2 id="大数据进阶："><a href="#大数据进阶：" class="headerlink" title="大数据进阶："></a>大数据进阶：</h2><h5 id="Compress-And-Storage-Format"><a href="#Compress-And-Storage-Format" class="headerlink" title="Compress And Storage Format:"></a>Compress And Storage Format:</h5><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483778&amp;idx=1&amp;sn=ea586218142d9b21cb4d68d9675956b6&amp;chksm=908f2beba7f8a2fda956ea7b128ed7c419cc322715199813bff22c3c00667afa34d93cf96853#rd" target="_blank" rel="noopener">1.大数据压缩格式，你们真的了解吗？</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483779&amp;idx=1&amp;sn=03051f1cfc307b70bd442bf16dcf0d67&amp;chksm=908f2beaa7f8a2fc49ac09613db06679dc41955cdaa4ef07c9704da357d6c7d8b5350a3e67cf#rd" target="_blank" rel="noopener">2.Hive压缩格式的生产应用</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483808&amp;idx=1&amp;sn=a3d1377b50ecb87bb75c9a089e9273c8&amp;chksm=908f2bc9a7f8a2dfed87b00d06239dc175a70f70a2851343273a942ee74cb3121a9849a50353#rd" target="_blank" rel="noopener">3.大数据存储格式，你们真的了解吗？</a><br><a href="https://mp.weixin.qq.com/s/B2tsou1siflOVh7cIVqh3Q" target="_blank" rel="noopener">4.Hive存储格式的生产应用</a>  </p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483811&amp;idx=1&amp;sn=cb6473764efdc4c2796cd832042887b4&amp;chksm=908f2bcaa7f8a2dc4fdb5c25aa35356a5ee26376af2e585e7b3cec7c73e9d1049a10f15ed519&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">5.Hive生产上，压缩和存储结合使用案例</a></p><h5 id="Flume"><a href="#Flume" class="headerlink" title="Flume:"></a>Flume:</h5><h5 id="Kafka"><a href="#Kafka" class="headerlink" title="Kafka:"></a>Kafka:</h5><p><a href="https://ke.qq.com/course/278667?tuin=11cffd50" target="_blank" rel="noopener">批处理ETL已亡，Kafka才是数据处理的未来【buy视频】</a></p><h5 id="Scala"><a href="#Scala" class="headerlink" title="Scala:"></a>Scala:</h5><h5 id="Spark"><a href="#Spark" class="headerlink" title="Spark:"></a>Spark:</h5><p><a href="https://ke.qq.com/course/238513?tuin=11cffd50" target="_blank" rel="noopener">Spark零基础实战【free视频】</a>  </p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483751&amp;idx=1&amp;sn=5b0b76b6fba35e1cb32ad8bf49530f3c&amp;chksm=908f2b0ea7f8a2187e9745f816d2582b1544277fe863b14be319bd1f9e7018b70718691f67e6#rd" target="_blank" rel="noopener">1.Spark2.2.0 全网最详细的源码编译</a><br><a href="https://blog.csdn.net/lsr40/article/details/80116235" target="_blank" rel="noopener">2.Spark-2.2.0-bin-2.6.0-cdh5.12.1.tgz 编译方法总结！</a>  </p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483862&amp;idx=1&amp;sn=7b66ca607dd4335918747d15e535a4e1&amp;chksm=908f2bbfa7f8a2a9bfb2117f53e8ae5aae2c275801b8860b2b6ba633ba8f6a17903c3ff4baf4#rd" target="_blank" rel="noopener">3.生产改造Spark1.6源代码，create table语法支持Oracle列表分区</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483867&amp;idx=1&amp;sn=6a6998b735f2ddd9b5d0ca8e07787398&amp;chksm=908f2bb2a7f8a2a42858e5430461dbe90b8cfc099dccf94f20da6bfe6af3f6ea79c32c75b7e5#rd" target="_blank" rel="noopener">4.Spark History Server Web UI配置</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483875&amp;idx=1&amp;sn=a6fe847f74ef213312589836585f2ef7&amp;chksm=908f2b8aa7f8a29cecd379618ef51c2ef765d73266c091a04ea8ece8098f79446cc523fe99b7#rd" target="_blank" rel="noopener">5.Spark on YARN-Cluster和YARN-Client的区别</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483897&amp;idx=1&amp;sn=ddcd1e7d6f1220847319b0facc6bd3d2&amp;chksm=908f2b90a7f8a286c6a5df5f3d5dba8c856fd105e425706a74db7ea71d94d2ea3bd3044c6229&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">6.Spark RDD、DataFrame和DataSet的区别</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483897&amp;idx=1&amp;sn=ddcd1e7d6f1220847319b0facc6bd3d2&amp;chksm=908f2b90a7f8a286c6a5df5f3d5dba8c856fd105e425706a74db7ea71d94d2ea3bd3044c6229&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">7.Spark RDD、DataFrame和DataSet的区别</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483908&amp;idx=1&amp;sn=030c206ab12edcf8f63b20fb99fc85c0&amp;chksm=908f286da7f8a17b28ceef4c73fbc99ffae01ccc656e4656288c865dc4702bb779a02fc50dd0&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">8.Spark不得不理解的重要概念——从源码角度看RDD</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483917&amp;idx=1&amp;sn=c6197eea3bc83cbe9154ce5853a1bd1a&amp;chksm=908f2864a7f8a1727233f6c65dd8bbe06c67ca5f4aca9b5aa4820ef66db524e03aa652152211&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">9.Spark 基本概念</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483936&amp;idx=1&amp;sn=5354cda77d0183e47030767054da274d&amp;chksm=908f2849a7f8a15f42046717d0977421ffbfc8fe079a2996c3a1117229a6a3c778177f59fb43&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">10.Spark调优的关键之——RDD Cache缓存使用详解</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483947&amp;idx=1&amp;sn=3f2a3a7a1bf27f69ddc05e348fb1b3c2&amp;chksm=908f2842a7f8a154a70f7d62e821e118e1346ccbe873abf1b3e8af0c8398059ba4467d088ac2&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">11.Spark之序列化在生产中的应用</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483962&amp;idx=1&amp;sn=11729c88e10122daf0b86313d5961b0d&amp;chksm=908f2853a7f8a145df09f08fe1de28ad2f422eaa2c08635f082b8ca17c6975d8a8630af0c42a&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">12.还不收藏？Spark动态内存管理源码解析！</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483971&amp;idx=1&amp;sn=cf1f4b6cc314c1cd97ca27bf1596e9b5&amp;chksm=908f282aa7f8a13c6d52be7875b72a00342aa6793c5dde1102ec2fe96d87b9d5b55e68b320f1&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">13.Spark SQL 外部数据源（External DataSource）</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483984&amp;idx=1&amp;sn=80c4fde272713caf64296c62235a2179&amp;chksm=908f2839a7f8a12fd7aba4535559616b9b3ce25c41f0854097389c8fbd17d2239dd821011bb3&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">14.你大爷永远是你大爷，RDD血缘关系源码详解！</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483997&amp;idx=1&amp;sn=751e7cfe35f7738688d48eefffa94ca2&amp;chksm=908f2834a7f8a1221ee03cbe234be501d610bf4066e42d552146b5d2179dbeb3ef95c7bc2d30&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">15.Apache Spark 技术团队开源机器学习平台 MLflow</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484006&amp;idx=1&amp;sn=3dc65a83f94fc419aed7da5fd71742e0&amp;chksm=908f280fa7f8a119cd9c9231e5a84b79eab8e7ab7ca0d4865345b55bfd952f13ded40a7023f9&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">16.生产开发必用-Spark RDD转DataFrame的两种方法</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484010&amp;idx=1&amp;sn=30ce4120dc5f82c84930b36959ec1daa&amp;chksm=908f2803a7f8a115ce408d7f85f222ff6a455db01db94f3311997fc7af9bb8919660a128c230&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">17.最前沿！带你读Structured Streaming重量级论文！</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484039&amp;idx=1&amp;sn=54a3fa60316b787b081b45aa68eee6a0&amp;chksm=908f28eea7f8a1f80244c82f5e6622213bb1b6705c7f5bcbd437f9dff2ac100401f81887c965&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">18.Apache Spark和DL/AI结合，谁与争锋? 期待Spark3.0的到来！</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484048&amp;idx=1&amp;sn=3453b497dbf53ea507d54d7f29f9fe69&amp;chksm=908f28f9a7f8a1efcf3c48935c90b36fd8ffd04886103c24717c9529ed761d39edc75d7cca25&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">19.又又又是源码！RDD 作业的DAG是如何切分的？</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484051&amp;idx=1&amp;sn=650f10652f15103af7b711e50a0759d2&amp;chksm=908f28faa7f8a1ec86cc64f407756966cbc8d4a0dfac116398af4aace5c77936b73efbec678f&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">20.Spark Streaming 状态管理函数，你了解吗</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484152&amp;idx=1&amp;sn=7a50b1ab285969c1eb739b23e0d0c451&amp;chksm=908f2891a7f8a187220e87a923eadf3f0972b11622b85a17e22e0db48170e181df51d3914ff4&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">21.Spark序列化，你了解吗</a></p><h5 id="Flink"><a href="#Flink" class="headerlink" title="Flink:"></a>Flink:</h5><p><a href="https://ke.qq.com/course/350493" target="_blank" rel="noopener">1.数据Flink实战系列</a><br><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484501&amp;idx=1&amp;sn=8fb72307838fcd5abe0614d2887b10e2&amp;chksm=908f2e3ca7f8a72a311f28c0c961b5861fab2f55d89095ba439e5e61baf4ad4027c22f1f84b0&amp;mpshare=1&amp;scene=23&amp;srcid=1110xBCjEud8MbVLBkZURrNJ#rd" target="_blank" rel="noopener">2.最全的Flink部署及开发案例(KafkaSource+SinkToMySQL)</a></p><h5 id="Phoenix"><a href="#Phoenix" class="headerlink" title="Phoenix:"></a>Phoenix:</h5><h5 id="HBase"><a href="#HBase" class="headerlink" title="HBase:"></a>HBase:</h5><h5 id="Kudu"><a href="#Kudu" class="headerlink" title="Kudu:"></a>Kudu:</h5><h5 id="Storm"><a href="#Storm" class="headerlink" title="Storm:"></a>Storm:</h5><h5 id="Hue"><a href="#Hue" class="headerlink" title="Hue:"></a>Hue:</h5><h5 id="Azkaban"><a href="#Azkaban" class="headerlink" title="Azkaban:"></a>Azkaban:</h5><p><a href="https://ke.qq.com/course/238175?tuin=11cffd50" target="_blank" rel="noopener">全网唯一Azkaban3.x应用实战【buy视频】</a>  </p><h5 id="Rundeck"><a href="#Rundeck" class="headerlink" title="Rundeck:"></a>Rundeck:</h5><h5 id="Docker"><a href="#Docker" class="headerlink" title="Docker:"></a>Docker:</h5><h5 id="Harbor"><a href="#Harbor" class="headerlink" title="Harbor:"></a>Harbor:</h5><h5 id="Kubernetes"><a href="#Kubernetes" class="headerlink" title="Kubernetes:"></a>Kubernetes:</h5><h5 id="Python"><a href="#Python" class="headerlink" title="Python:"></a>Python:</h5><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247483922&amp;idx=1&amp;sn=6a9d3fd86618657370fcc1d76cc6e2c3&amp;chksm=908f287ba7f8a16d38a2db5d661e55f2b406c62502f701ee6abf58421b7863756c4534ca676d&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">PyTorch 1.0宣布用于研究和生产AI项目</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484184&amp;idx=1&amp;sn=b59de2a180a9a7dd30b0fe36caf64579&amp;chksm=908f2971a7f8a0671b9c8378a7343e3747cf0b74f2c55fcf733e833aa9b5f0c392a33c598306&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">1.Python核心笔记（一）</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484185&amp;idx=1&amp;sn=8040433e2ba557364b73536fbbf164a6&amp;chksm=908f2970a7f8a066398a577e75586b0f1687ac57eece3a78802f154287fa3c34d13bc5882a7b&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">2.Python核心笔记（二）</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484190&amp;idx=1&amp;sn=53dfa78568d493b5eeec0149577a1945&amp;chksm=908f2977a7f8a061b211dd701e6dd2478bc8b3f39b9f3fb8d017bb4f951e9802e65ae8e41842&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">3.Pandas数据分析入门（一）</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484193&amp;idx=1&amp;sn=d79339ff1f04bdd8ee6b938c24f357cc&amp;chksm=908f2948a7f8a05eccc7c62e591953b732822e621b7ecba657506e17250b7d344401def9a2c3&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">4.Pandas数据分析入门（二）</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484197&amp;idx=1&amp;sn=30d1a46a45154240acdd64a13a81843d&amp;chksm=908f294ca7f8a05ad8ae971745f9ec3995664d912d0d6ba6ad20fefe3269199c6e3be2bbbbc7&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">5.Kaggle入门经典：Titanic生还预测</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484203&amp;idx=1&amp;sn=dc892ec40da8bb0601ba70cb7bcd7e25&amp;chksm=908f2942a7f8a05475c784612c5ae46fdb86b1522ad6bb5f0e941c4a97a75c711d890e9c60b7&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">7.Titanic生还预测（一）构建基本模型</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484202&amp;idx=1&amp;sn=0327f9d82e5242ca720faa4ae4d57b18&amp;chksm=908f2943a7f8a055f2013bfae49f16407e9bc393b370a5c2ed6e8160ef3228018685b5627944&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">7.Titanic生还预测（二）</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484206&amp;idx=1&amp;sn=a49f51074dbc4ace404b885233d1f18d&amp;chksm=908f2947a7f8a05149a14a0838616a1aa19197904e7598b87940684c0b569c4f48ab4ab3aedb&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">8.Titanic生还预测（三）</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484209&amp;idx=1&amp;sn=10960b21f750bd77eb58713a68f60101&amp;chksm=908f2958a7f8a04ecfb134006c876e4fc950125049b7e1b4b4a3eb84ae4be789e13250910e47&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">9.Titanic生还预测（四）</a></p><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484212&amp;idx=1&amp;sn=9462578cd45bfe1ac9152522eb7f27aa&amp;chksm=908f295da7f8a04be16a2bef07e4fc6c90e3a3302978d20dfce0ad3f439c4ef2d68f46994220&amp;scene=38#wechat_redirect" target="_blank" rel="noopener">10. Titanic生还预测（五）</a></p><h5 id="Spark-MLlib"><a href="#Spark-MLlib" class="headerlink" title="Spark MLlib:"></a>Spark MLlib:</h5><h5 id="TensorFlow"><a href="#TensorFlow" class="headerlink" title="TensorFlow:"></a>TensorFlow:</h5><h5 id="实时同步中间件"><a href="#实时同步中间件" class="headerlink" title="实时同步中间件:"></a>实时同步中间件:</h5><p><a href="大数据之实时数据源同步中间件--生产上Canal与Maxwell颠峰对决">大数据之实时数据源同步中间件–生产上Canal与Maxwell颠峰对决</a></p><h5 id="Java"><a href="#Java" class="headerlink" title="Java:"></a>Java:</h5><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484003&amp;idx=1&amp;sn=b2a7c375204d0077c7e949193bd3afc3&amp;chksm=908f280aa7f8a11c7502fc8312d907ea35cbb7abb80ac70952485a770a28c14974835e9663d4&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">1.Java可扩展线程池之ThreadPoolExecutor</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484034&amp;idx=1&amp;sn=27d8fbcc33788b7dbb4de6a0a7f0155f&amp;chksm=908f28eba7f8a1fd7ae42c8f21932d4cebd9c1d2e7cdd4a604adc48d3f3f479085c19d7eb864&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">2.面试常考点-Java线程池之拒绝策略</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484120&amp;idx=1&amp;sn=9c48b5aa6e515590d3c4de7d0112378b&amp;chksm=908f28b1a7f8a1a77c10a821b7a60cbe17d9b103f733cff775349844168c79b72c79596d57bb&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">3.再谈单例设计模式</a></p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484156&amp;idx=1&amp;sn=bedde12e5ee13fc14060ad484671e012&amp;chksm=908f2895a7f8a183c0585d83af54bbcee64bd2e8a46f6cca906ccc52cab6b46a2cb7c90aa1a5&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">4.Java类加载方式你知道几种？</a></p><h5 id="Github"><a href="#Github" class="headerlink" title="Github:"></a>Github:</h5><p><a href="https://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484169&amp;idx=1&amp;sn=37fc35363216396fd18ae4caef87a657&amp;chksm=908f2960a7f8a0760939286b2c97daa75aa0783515805839434a099557837e434f58519d2447&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">如何将我们谱写的代码供凡人瞻仰</a></p><p>生产项目： </p><h5 id="线上项目-承诺企业生产项目，而不是那种pv-uv网上搜搜的项目"><a href="#线上项目-承诺企业生产项目，而不是那种pv-uv网上搜搜的项目" class="headerlink" title="线上项目: 承诺企业生产项目，而不是那种pv,uv网上搜搜的项目"></a>线上项目: 承诺企业生产项目，而不是那种pv,uv网上搜搜的项目</h5><p><a href="https://ke.qq.com/course/238183?tuin=11cffd50" target="_blank" rel="noopener">1.Spark实时分析预警平台(架构+提交流程+现场排错)【free视频】</a><br><a href="http://www.ruozedata.com/advanced.html" target="_blank" rel="noopener">2.Spark实时分析预警平台项目(在进阶班课表)</a><br><a href="http://www.ruozedata.com/advanced.html" target="_blank" rel="noopener">3.Strom互联网金融实时计算与分析项目(在进阶班课表)</a><br><a href="http://www.ruozedata.com/advanced.html" target="_blank" rel="noopener">4.构建企业级PaaS平台项目(在进阶班课表)</a></p><h5 id="线下项目-承诺上课是直接VPN公司生产环境，直接生产环境讲解生产项目"><a href="#线下项目-承诺上课是直接VPN公司生产环境，直接生产环境讲解生产项目" class="headerlink" title="线下项目: 承诺上课是直接VPN公司生产环境，直接生产环境讲解生产项目"></a>线下项目: 承诺上课是直接VPN公司生产环境，直接生产环境讲解生产项目</h5><p><a href="https://ke.qq.com/course/258137?tuin=11cffd50" target="_blank" rel="noopener">1.基于Spark的某互联网直播平台大数据分析项目实战第3季，正在报名！单击查看前2季的目录.</a><br><a href="http://www.ruozedata.com/line.html" target="_blank" rel="noopener">2.线下班生产项目第10期，国庆节线下4天课程，正在报名！</a></p><p>大数据平台运维：</p><h5 id="CDH-入门"><a href="#CDH-入门" class="headerlink" title="CDH 入门:"></a>CDH 入门:</h5><p><a href="https://ke.qq.com/course/241568?tuin=11cffd50" target="_blank" rel="noopener">CDH离线部署和暴力卸载、Kerberos【free视频】</a>  </p><p><a href="http://blog.itpub.net/30089851/viewspace-2092318/" target="_blank" rel="noopener">1.CDH下载各种软件包</a><br><a href="http://blog.itpub.net/30089851/viewspace-2126298/" target="_blank" rel="noopener">2.CDH4/5集群正确启动和停止顺序</a><br><a href="http://blog.itpub.net/30089851/viewspace-1991862/" target="_blank" rel="noopener">3.CDH5 快速入门手册v1.0(体系架构+目录详解)</a><br><a href="http://blog.itpub.net/30089851/viewspace-2110288/" target="_blank" rel="noopener">4.CDH4/5配置文件之深度解析</a><br><a href="http://blog.itpub.net/30089851/viewspace-1990991/" target="_blank" rel="noopener">5.CDH5之Trash</a></p><h5 id="CDH-案例"><a href="#CDH-案例" class="headerlink" title="CDH 案例:"></a>CDH 案例:</h5><p><a href="http://blog.itpub.net/30089851/viewspace-2133322/" target="_blank" rel="noopener">1.记录一次帮网友解决CDH集群机器的时钟偏差</a><br><a href="http://blog.itpub.net/30089851/viewspace-2128683/" target="_blank" rel="noopener">2.CDH集群机器,安装多个CDH版,会出现命令找不到,如hadoop,hdfs等等</a><br><a href="http://blog.itpub.net/30089851/viewspace-2128607/" target="_blank" rel="noopener">3.CDH5.8.2安装之Hash verification failed</a><br><a href="http://blog.itpub.net/30089851/viewspace-2134627/" target="_blank" rel="noopener">4.记录CDH Spark2的spark2-submit的一个No such file or directory问题</a><br><a href="http://blog.itpub.net/30089851/viewspace-2135135/" target="_blank" rel="noopener">5.记录CDH5.10一个clients.NetworkClient: Bootstrap broker ip:9092 disconnected问题</a>  </p><p><a href="http://blog.itpub.net/30089851/viewspace-2136372/" target="_blank" rel="noopener">6.记录自定义kafka的parcel库,CDH安装kafka服务,无法安装过去的排雷过程</a><br><a href="http://blog.itpub.net/30089851/viewspace-2137618/" target="_blank" rel="noopener">7.记录CDH安装的一个坑:could not contact scm server at localhost:7182, giving up</a><br><a href="http://blog.itpub.net/30089851/viewspace-2082146/" target="_blank" rel="noopener">8.CDH5之Found class jline.Terminal, but interface was expected</a><br><a href="http://blog.itpub.net/30089851/viewspace-2075759/" target="_blank" rel="noopener">9.CDH5之Exhausted available authentication methods</a><br><a href="http://blog.itpub.net/30089851/viewspace-1987886/" target="_blank" rel="noopener">10.CDH5之Unexpected error.Unable to verify database connection</a> </p><p><a href="http://mp.weixin.qq.com/s?__biz=MzA5ODY0NzgxNA==&amp;mid=2247484122&amp;idx=1&amp;sn=bf236f01873ad65d7da4503adbd6614e&amp;chksm=908f28b3a7f8a1a5caedb3437baf9667a649a4575b0d7a23ea396366a5f870540a902f3649bd&amp;scene=27#wechat_redirect" target="_blank" rel="noopener">11.生产CDH5配置lzo</a> </p>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="大数据" scheme="https://icocos.github.io/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="博客" scheme="https://icocos.github.io/tags/%E5%8D%9A%E5%AE%A2/"/>
    
  </entry>
  
  <entry>
    <title>大数据完整实战视频教程</title>
    <link href="https://icocos.github.io/2020/03/17/%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%95%B4%E5%AE%9E%E6%88%98%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B/"/>
    <id>https://icocos.github.io/2020/03/17/大数据完整实战视频教程/</id>
    <published>2020-03-17T15:56:50.000Z</published>
    <updated>2020-04-09T14:45:08.218Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h1 id="Big-Data-Project"><a href="#Big-Data-Project" class="headerlink" title="Big-Data-Project"></a>Big-Data-Project</h1><p>Hadoop2.x、Zookeeper、Flume、Hive、Hbase、Kafka、Spark2.x、SparkStreaming、MySQL、Hue、J2EE、websoket、Echarts</p><h2 id="项目名称：新闻日志大数据处理系统"><a href="#项目名称：新闻日志大数据处理系统" class="headerlink" title="项目名称：新闻日志大数据处理系统"></a>项目名称：新闻日志大数据处理系统</h2><h3 id="项目简介"><a href="#项目简介" class="headerlink" title="项目简介"></a>项目简介</h3><p><strong>目标</strong></p><p>1、完成大数据项目的架构设计，安装部署，架构继承与开发、用户可视化交互设计</p><p>2、完成实时在线数据分析</p><p>3、完成离线数据分析</p><p><strong>具体功能</strong></p><p>1）捕获用户浏览日志信息</p><p>2）实时分析前20名流量最高的新闻话题</p><p>3）实时统计当前线上已曝光的新闻话题</p><p>4）统计哪个时段用户浏览量最高</p><p>5）报表</p><h3 id="项目技术点"><a href="#项目技术点" class="headerlink" title="项目技术点"></a>项目技术点</h3><p>Hadoop2.x、Zookeeper、Flume、Hive、Hbase</p><p>Kafka、Spark2.x、SparkStreaming</p><p>MySQL、Hue、J2EE、websoket、Echarts</p><h3 id="开发工具"><a href="#开发工具" class="headerlink" title="开发工具"></a>开发工具</h3><p>虚拟机：  VMware、centos</p><p>虚拟机ssh:  SecureCRT（在windows上链接多个虚拟机）</p><p>修改源码：idea</p><p>查看各种数据：notepad++（安装NppFTP插件，修改虚拟机中配置文件，好用的一批）</p><p>所有软件下载地址：</p><p>链接：<a href="https://pan.baidu.com/s/1aF_VmdXJVIjeB0WzAtfeEQ" target="_blank" rel="noopener">https://pan.baidu.com/s/1aF_VmdXJVIjeB0WzAtfeEQ</a> </p><p>提取码：cuao </p><h3 id="项目架构"><a href="#项目架构" class="headerlink" title="项目架构"></a>项目架构</h3><p>图片来自于卡夫卡公司<br><img src="http://ww1.sinaimg.cn/large/005BOtkIly1fyccyao7f3j30op0ee10a.jpg" alt=""></p><h3 id="集群资源规划"><a href="#集群资源规划" class="headerlink" title="集群资源规划"></a>集群资源规划</h3><p>利用VMware虚拟机+centos完成，基本要求笔记本电脑内存在8G以上。<br>最低要去克隆出3台虚拟机，每台给2G内存。<br><img src="http://ww1.sinaimg.cn/large/005BOtkIly1fycdbmkr58j30m20ckq81.jpg" alt=""></p><h3 id="项目实现步骤"><a href="#项目实现步骤" class="headerlink" title="项目实现步骤"></a>项目实现步骤</h3><p>1、第一章：项目需求分析与设计</p><p><a href="https://www.willxu.xyz/2018/12/19/project/1%E3%80%81%E9%A1%B9%E7%9B%AE%E9%9C%80%E6%B1%82/" target="_blank" rel="noopener">https://www.willxu.xyz/2018/12/19/project/1%E3%80%81%E9%A1%B9%E7%9B%AE%E9%9C%80%E6%B1%82/</a></p><p>2、第二章：linux环境准备与设置</p><p><a href="https://www.willxu.xyz/2018/12/19/project/2%E3%80%81linux%E9%85%8D%E7%BD%AE/" target="_blank" rel="noopener">https://www.willxu.xyz/2018/12/19/project/2%E3%80%81linux%E9%85%8D%E7%BD%AE/</a></p><p>3、第三章：Hadoop2.X分布式集群部署</p><p><a href="https://www.willxu.xyz/2018/12/19/project/3%E3%80%81hadoop%E9%83%A8%E7%BD%B2/" target="_blank" rel="noopener">https://www.willxu.xyz/2018/12/19/project/3%E3%80%81hadoop%E9%83%A8%E7%BD%B2/</a></p><p>4、第四章：Zookeeper分布式集群部署</p><p><a href="https://www.willxu.xyz/2018/12/29/project/4%E3%80%81zk%E9%83%A8%E7%BD%B2/" target="_blank" rel="noopener">https://www.willxu.xyz/2018/12/29/project/4%E3%80%81zk%E9%83%A8%E7%BD%B2/</a></p><p>5、第五章：hadoop的高可用配置（HA）</p><p><a href="https://www.willxu.xyz/2018/12/29/project/5%E3%80%81ha%E5%AE%9E%E7%8E%B0/" target="_blank" rel="noopener">https://www.willxu.xyz/2018/12/29/project/5%E3%80%81ha%E5%AE%9E%E7%8E%B0/</a></p><p>6、第六章：hadoop的HA下的高可用HBase部署</p><p><a href="https://www.willxu.xyz/2018/12/30/project/6%E3%80%81hbase%E9%83%A8%E7%BD%B2/" target="_blank" rel="noopener">https://www.willxu.xyz/2018/12/30/project/6%E3%80%81hbase%E9%83%A8%E7%BD%B2/</a></p><p>7、第七章：Kafka简介和分布式部署</p><p><a href="https://www.willxu.xyz/2019/01/01/project/7%E3%80%81kafka%E9%83%A8%E7%BD%B2/" target="_blank" rel="noopener">https://www.willxu.xyz/2019/01/01/project/7%E3%80%81kafka%E9%83%A8%E7%BD%B2/</a></p><p>8、第八章：Flume简介和分布式部署</p><p><a href="https://www.willxu.xyz/2019/01/01/project/8%E3%80%81flume%E9%83%A8%E7%BD%B2/" target="_blank" rel="noopener">https://www.willxu.xyz/2019/01/01/project/8%E3%80%81flume%E9%83%A8%E7%BD%B2/</a></p><p>9、第九章：Flume源码修改与HBase+Kafka集成</p><p><a href="https://www.willxu.xyz/2019/01/20/project/9%E3%80%81flume-hbase-kfk%E9%85%8D%E7%BD%AE/" target="_blank" rel="noopener">https://www.willxu.xyz/2019/01/20/project/9%E3%80%81flume-hbase-kfk%E9%85%8D%E7%BD%AE/</a></p><p>10、第十章：Flume+HBase+Kafka集成全流程测试</p><p><a href="https://www.willxu.xyz/2019/01/20/project/10%E3%80%81flume-hbase-kfk%E8%81%94%E8%B0%83/" target="_blank" rel="noopener">https://www.willxu.xyz/2019/01/20/project/10%E3%80%81flume-hbase-kfk%E8%81%94%E8%B0%83/</a></p><p>11、第十一章：mysql、Hive安装与集成</p><p><a href="https://www.willxu.xyz/2019/01/22/project/11%E3%80%81mysql-hive/" target="_blank" rel="noopener">https://www.willxu.xyz/2019/01/22/project/11%E3%80%81mysql-hive/</a></p><p>12、第十二章：Hive与Hbase集成</p><p><a href="https://www.willxu.xyz/2019/01/23/project/12%E3%80%81hive-hbase/" target="_blank" rel="noopener">https://www.willxu.xyz/2019/01/23/project/12%E3%80%81hive-hbase/</a></p><p>13、第十三章：Cloudera HUE大数据可视化分析</p><p><a href="https://www.willxu.xyz/2019/01/26/project/13%E3%80%81hue/" target="_blank" rel="noopener">https://www.willxu.xyz/2019/01/26/project/13%E3%80%81hue/</a></p><p>14、第十四章：Spark2.X集群安装与spark on yarn部署</p><p><a href="https://www.willxu.xyz/2019/01/30/project/14%E3%80%81spark%20on%20yarn/" target="_blank" rel="noopener">https://www.willxu.xyz/2019/01/30/project/14%E3%80%81spark%20on%20yarn/</a></p><p>15、第十五章：基于IDEA环境下的Spark2.X程序开发</p><p><a href="https://www.willxu.xyz/2019/01/30/project/15%E3%80%81spark-idea/" target="_blank" rel="noopener">https://www.willxu.xyz/2019/01/30/project/15%E3%80%81spark-idea/</a></p><p>16、第十六章：Spark Streaming实时数据处理</p><p><a href="https://www.willxu.xyz/2019/02/03/project/16%E3%80%81spark-streaming1/" target="_blank" rel="noopener">https://www.willxu.xyz/2019/02/03/project/16%E3%80%81spark-streaming1/</a></p><h3 id="项目配套视频"><a href="#项目配套视频" class="headerlink" title="项目配套视频"></a>项目配套视频</h3><p>链接：<a href="https://pan.baidu.com/s/1-PQta6SCgps91oFNTkl6Qg" target="_blank" rel="noopener">https://pan.baidu.com/s/1-PQta6SCgps91oFNTkl6Qg</a> </p><p>提取码：sh8x </p>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="大数据" scheme="https://icocos.github.io/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="视频" scheme="https://icocos.github.io/tags/%E8%A7%86%E9%A2%91/"/>
    
  </entry>
  
  <entry>
    <title>大数据面试之Zookeeper</title>
    <link href="https://icocos.github.io/2020/03/12/%E5%A4%A7%E6%95%B0%E6%8D%AE%E9%9D%A2%E8%AF%95%E4%B9%8BZookeeper/"/>
    <id>https://icocos.github.io/2020/03/12/大数据面试之Zookeeper/</id>
    <published>2020-03-11T23:10:01.000Z</published>
    <updated>2020-04-09T14:45:08.819Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h2 id="zookeeper是什么-有什么功能"><a href="#zookeeper是什么-有什么功能" class="headerlink" title="zookeeper是什么,有什么功能"></a>zookeeper是什么,有什么功能</h2><p>Zookeeper 是 一个典型的分布式数据一致性的解决方案.</p><p><strong>Zookeeper的典型应用场景</strong>:</p><ul><li>数据发布/订阅</li><li>负载均衡</li><li>命名服务</li><li>分布式协调/通知</li><li>集群管理</li><li>Master</li><li>分布式锁</li><li>分布式队列</li></ul><h2 id="zk-有几种部署模式"><a href="#zk-有几种部署模式" class="headerlink" title="zk 有几种部署模式"></a>zk 有几种部署模式</h2><p>zookeeper有两种运行模式: 集群模式和单机模式,还有一种伪集群模式,在单机模式下模拟集群的zookeeper服务</p><h2 id="zk是怎样保证主从节点的状态同步"><a href="#zk是怎样保证主从节点的状态同步" class="headerlink" title="zk是怎样保证主从节点的状态同步"></a>zk是怎样保证主从节点的状态同步</h2><p>zookeeper 的核心是原子广播，这个机制保证了各个 server 之间的同步。实现这个机制的协议叫做 zab 协议。 zab 协议有两种模式，分别是恢复模式（选主）和广播模式（同步）。当服务启动或者在领导者崩溃后，zab 就进入了恢复模式，当领导者被选举出来，且大多数 server 完成了和 leader 的状态同步以后，恢复模式就结束了。状态同步保证了 leader 和 server 具有相同的系统状态。</p><h2 id="说一下zk的通知机制"><a href="#说一下zk的通知机制" class="headerlink" title="说一下zk的通知机制"></a>说一下zk的通知机制</h2><p>客户端端会对某个 znode 建立一个 watcher 事件，当该 znode 发生变化时，这些客户端会收到 zookeeper 的通知，然后客户端可以根据 znode 变化来做出业务上的改变</p><h2 id="zk的分布式锁实现方式"><a href="#zk的分布式锁实现方式" class="headerlink" title="zk的分布式锁实现方式"></a>zk的分布式锁实现方式</h2><p>使用zookeeper实现分布式锁的算法流程，假设锁空间的根节点为/lock：</p><ol><li>客户端连接zookeeper，并在/lock下创建<strong>临时的</strong>且<strong>有序的</strong>子节点，第一个客户端对应的子节点为/lock/lock-0000000000，第二个为/lock/lock-0000000001，以此类推。</li><li>客户端获取/lock下的子节点列表，判断自己创建的子节点是否为当前子节点列表中<strong>序号最小</strong>的子节点，如果是则认为获得锁，否则<strong>监听刚好在自己之前一位的子节点删除消息</strong>，获得子节点变更通知后重复此步骤直至获得锁；</li><li>执行业务代码；</li><li>完成业务流程后，删除对应的子节点释放锁。</li></ol><p><a href="http://www.dengshenyu.com/java/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/2017/10/23/zookeeper-distributed-lock.html" target="_blank" rel="noopener">参考文章</a></p><h2 id="zk-采用的哪种分布式一致性协议-还有哪些分布式一致性协议"><a href="#zk-采用的哪种分布式一致性协议-还有哪些分布式一致性协议" class="headerlink" title="zk 采用的哪种分布式一致性协议? 还有哪些分布式一致性协议"></a>zk 采用的哪种分布式一致性协议? 还有哪些分布式一致性协议</h2><p>常见的分布式一致性协议有: 两阶段提交协议，三阶段提交协议，向量时钟，RWN协议，paxos协议，Raft协议. zk采用的是paxos协议.</p><ul><li><h4 id="两阶段提交协议-2PC"><a href="#两阶段提交协议-2PC" class="headerlink" title="两阶段提交协议(2PC)"></a>两阶段提交协议(2PC)</h4><p>两阶段提交协议，简称2PC，是比较常用的解决分布式事务问题的方式，要么所有参与进程都提交事务，要么都取消事务，即实现ACID中的原子性(A)的常用手段。</p></li><li><h4 id="三阶段提交协议-3PC"><a href="#三阶段提交协议-3PC" class="headerlink" title="三阶段提交协议(3PC)"></a>三阶段提交协议(3PC)</h4><p>3PC就是在2PC基础上将2PC的提交阶段细分位两个阶段：预提交阶段和提交阶段</p></li><li><h4 id="向量时钟"><a href="#向量时钟" class="headerlink" title="向量时钟"></a>向量时钟</h4><p>通过向量空间祖先继承的关系比较, 使数据保持最终一致性,这就是向量时钟的基本定义。</p></li><li><h4 id="NWR协议"><a href="#NWR协议" class="headerlink" title="NWR协议"></a>NWR协议</h4><p>NWR是一种在分布式存储系统中用于控制一致性级别的一种策略。在Amazon的Dynamo云存储系统中，就应用NWR来控制一致性。<br>让我们先来看看这三个字母的含义：<br>N：在分布式存储系统中，有多少份备份数据<br>W：代表一次成功的更新操作要求至少有w份数据写入成功<br>R： 代表一次成功的读数据操作要求至少有R份数据成功读取<br>NWR值的不同组合会产生不同的一致性效果，当W+R&gt;N的时候，整个系统对于客户端来讲能保证强一致性。当W+R 以常见的N=3、W=2、R=2为例：<br>N=3，表示，任何一个对象都必须有三个副本（Replica），W=2表示，对数据的修改操作（Write）只需要在3个Replica中的2个上面完成就返回，R=2表示，从三个对象中要读取到2个数据对象，才能返回。<br>在分布式系统中，数据的单点是不允许存在的。即线上正常存在的Replica数量是1的情况是非常危险的，因为一旦这个Replica再次错误，就 可能发生数据的永久性错误。假如我们把N设置成为2，那么，只要有一个存储节点发生损坏，就会有单点的存在。所以N必须大于2。N约高，系统的维护和整体 成本就越高。工业界通常把N设置为3。<br>当W是2、R是2的时候，W+R&gt;N，这种情况对于客户端就是强一致性的。</p></li><li><h4 id="paxos协议"><a href="#paxos协议" class="headerlink" title="paxos协议"></a>paxos协议</h4><p><a href="http://chuansong.me/n/2189245" target="_blank" rel="noopener">架构师需要了解的Paxos原理，历程及实践</a></p></li><li><h4 id="Raft协议"><a href="#Raft协议" class="headerlink" title="Raft协议"></a>Raft协议</h4><p><a href="http://thesecretlivesofdata.com/raft/" target="_blank" rel="noopener">Raft协议的动画</a></p></li></ul><p><a href="https://blog.csdn.net/chdhust/article/details/52651741" target="_blank" rel="noopener">参考文章</a></p><h2 id="讲一下leader-选举过程"><a href="#讲一下leader-选举过程" class="headerlink" title="讲一下leader 选举过程"></a>讲一下leader 选举过程</h2><p>　　这里选取3台机器组成的服务器集群为例。在集群初始化阶段，当有一台服务器Server1启动时，其单独无法进行和完成Leader选举，当第二台服务器Server2启动时，此时两台机器可以相互通信，每台机器都试图找到Leader，于是进入Leader选举过程。选举过程如下</p><p>　　<strong>(1) 每个Server发出一个投票</strong>。由于是初始情况，Server1和Server2都会将自己作为Leader服务器来进行投票，每次投票会包含所推举的服务器的myid和ZXID，使用(myid, ZXID)来表示，此时Server1的投票为(1, 0)，Server2的投票为(2, 0)，然后各自将这个投票发给集群中其他机器。</p><p>　　<strong>(2) 接受来自各个服务器的投票</strong>。集群的每个服务器收到投票后，首先判断该投票的有效性，如检查是否是本轮投票、是否来自LOOKING状态的服务器。</p><p>　　<strong>(3) 处理投票</strong>。针对每一个投票，服务器都需要将别人的投票和自己的投票进行PK，PK规则如下</p><p>　　　　<strong>· 优先检查ZXID</strong>。ZXID比较大的服务器优先作为Leader。</p><p>　　　　<strong>· 如果ZXID相同，那么就比较myid</strong>。myid较大的服务器作为Leader服务器。</p><p>　　对于Server1而言，它的投票是(1, 0)，接收Server2的投票为(2, 0)，首先会比较两者的ZXID，均为0，再比较myid，此时Server2的myid最大，于是更新自己的投票为(2, 0)，然后重新投票，对于Server2而言，其无须更新自己的投票，只是再次向集群中所有机器发出上一次投票信息即可。</p><p>　　<strong>(4) 统计投票</strong>。每次投票后，服务器都会统计投票信息，判断是否已经有过半机器接受到相同的投票信息，对于Server1、Server2而言，都统计出集群中已经有两台机器接受了(2, 0)的投票信息，此时便认为已经选出了Leader。</p><p>　　<strong>(5) 改变服务器状态</strong>。一旦确定了Leader，每个服务器就会更新自己的状态，如果是Follower，那么就变更为FOLLOWING，如果是Leader，就变更为LEADING。</p><h3 id="Leader选举算法分析"><a href="#Leader选举算法分析" class="headerlink" title="Leader选举算法分析"></a>Leader选举算法分析</h3><p>在3.4.0后的Zookeeper的版本只保留了TCP版本的FastLeaderElection选举算法。当一台机器进入Leader选举时，当前集群可能会处于以下两种状态</p><p>　　　　· 集群中已经存在Leader。</p><p>　　　　· 集群中不存在Leader。</p><p>　　对于集群中已经存在Leader而言，此种情况一般都是某台机器启动得较晚，在其启动之前，集群已经在正常工作，对这种情况，该机器试图去选举Leader时，会被告知当前服务器的Leader信息，对于该机器而言，仅仅需要和Leader机器建立起连接，并进行状态同步即可。而在集群中不存在Leader情况下则会相对复杂，其步骤如下</p><p>　　(1) <strong>第一次投票</strong>。无论哪种导致进行Leader选举，集群的所有机器都处于试图选举出一个Leader的状态，即LOOKING状态，LOOKING机器会向所有其他机器发送消息，该消息称为投票。投票中包含了SID（服务器的唯一标识）和ZXID（事务ID），(SID, ZXID)形式来标识一次投票信息。假定Zookeeper由5台机器组成，SID分别为1、2、3、4、5，ZXID分别为9、9、9、8、8，并且此时SID为2的机器是Leader机器，某一时刻，1、2所在机器出现故障，因此集群开始进行Leader选举。在第一次投票时，每台机器都会将自己作为投票对象，于是SID为3、4、5的机器投票情况分别为(3, 9)，(4, 8)， (5, 8)。</p><p>　　(2) <strong>变更投票</strong>。每台机器发出投票后，也会收到其他机器的投票，每台机器会根据一定规则来处理收到的其他机器的投票，并以此来决定是否需要变更自己的投票，这个规则也是整个Leader选举算法的核心所在，其中术语描述如下</p><p>　　　　<strong>· vote_sid</strong>：接收到的投票中所推举Leader服务器的SID。</p><p>　　　　<strong>· vote_zxid</strong>：接收到的投票中所推举Leader服务器的ZXID。</p><p>　　　　<strong>· self_sid</strong>：当前服务器自己的SID。</p><p>　　　　<strong>· self_zxid</strong>：当前服务器自己的ZXID。</p><p>　　每次对收到的投票的处理，都是对(vote_sid, vote_zxid)和(self_sid, self_zxid)对比的过程。</p><p>　　　　规则一：如果vote_zxid大于self_zxid，就认可当前收到的投票，并再次将该投票发送出去。</p><p>　　　　规则二：如果vote_zxid小于self_zxid，那么坚持自己的投票，不做任何变更。</p><p>　　　　规则三：如果vote_zxid等于self_zxid，那么就对比两者的SID，如果vote_sid大于self_sid，那么就认可当前收到的投票，并再次将该投票发送出去。</p><p>　　　　规则四：如果vote_zxid等于self_zxid，并且vote_sid小于self_sid，那么坚持自己的投票，不做任何变更。</p><p>　　结合上面规则，给出下面的集群变更过程。</p><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/leader选举算法.png" alt=""></p><p>​    (3) <strong>确定Leader</strong>。经过第二轮投票后，集群中的每台机器都会再次接收到其他机器的投票，然后开始统计投票，如果一台机器收到了超过半数的相同投票，那么这个投票对应的SID机器即为Leader。此时Server3将成为Leader。</p><p>　　由上面规则可知，通常那台服务器上的数据越新（ZXID会越大），其成为Leader的可能性越大，也就越能够保证数据的恢复。如果ZXID相同，则SID越大机会越大。</p><p><a href="https://www.cnblogs.com/leesf456/p/6107600.html" target="_blank" rel="noopener">参考文章</a></p>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="Zookeeper" scheme="https://icocos.github.io/categories/Zookeeper/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="面试" scheme="https://icocos.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
    
      <category term="Zookeeper" scheme="https://icocos.github.io/tags/Zookeeper/"/>
    
  </entry>
  
  <entry>
    <title>大数据面试之Kafka</title>
    <link href="https://icocos.github.io/2020/03/10/%E5%A4%A7%E6%95%B0%E6%8D%AE%E9%9D%A2%E8%AF%95%E4%B9%8Bkafka/"/>
    <id>https://icocos.github.io/2020/03/10/大数据面试之kafka/</id>
    <published>2020-03-10T10:32:12.000Z</published>
    <updated>2020-04-09T14:45:08.819Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h2 id="讲一下kafka-的架构"><a href="#讲一下kafka-的架构" class="headerlink" title="讲一下kafka 的架构"></a>讲一下kafka 的架构</h2><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/kafka架构图.png" alt=""></p><ul><li><p><strong>Producer</strong>：消息生产者</p><ul><li><p>Producer可以发送消息到Topic</p></li><li><ul><li>Topic的消息存放在不同Partition中，不同Partition存放在不同Broker中</li><li>Producer只需要指定Topic的名字、要连接到的Broker，这样Kafka就可以自动地把消息数据路由到合适的Broker（不一定是指定连接的Broker）</li></ul></li></ul></li></ul><ul><li><p>Producer发送消息后，可以选择是否要确认消息写入成功（ACK，Acknowledgment）</p></li><li><ul><li>ACK=0：Producer不会等待ACK（消息可能丢失）</li><li>ACK=1：Producer会等待Leader Partition的ACK（Follower Partition消息可能丢失）</li><li>ACK=all：Producer会等待Leader Partition和Follower Partition的ACK（消息不会丢失）</li></ul></li><li><p>消息key：Producer可以给消息加上key，带相同key的消息会被分发到同一个Partition，这样就可以保证带相同key的消息的消费是有序的</p></li></ul><ul><li><p><strong>Broker</strong>：每个Broker里包含了不同Topic的不同Partition，Partition中包含了有序的消息</p><ul><li>一个Kafka集群由多个Broker（server）组成</li><li>每个Broker都有ID标识</li><li>每个Broker里保存一定数量的Partition</li><li>客户端只要连接上任意一个Broker，就可以连接上整个Kafka集群</li><li>大多数Kafka集群刚开始的时候建议使用至少3个Broker，集群大了可以有上百个Broker</li></ul></li><li><p><strong>Consumer</strong>：消息消费者</p><ul><li><p>Consumer可以从Topic读取消息进行消费</p></li><li><ul><li>Topic的消息存放在不同Partition中，不同Partition存放在不同Broker中</li><li>Consumer只需要指定Topic的名字、要连接到的Broker，这样Kafka就可以自动地把Consumer路由到合适的Broker拉取消息进行消费（不一定是指定连接的Broker）</li><li>每一个Partition中的消息都会被有序消费</li></ul></li><li><p>Consumer Group：</p></li><li><ul><li>Consumer Group由多个Consumer组成</li><li>Consumer Group里的每个Consumer都会从不同的Partition中读取消息</li><li>如果Consumer的数量大于Partition的数量，那么多出来的Consumer就会空闲下来（浪费资源）</li></ul></li><li><p>Consumer offset：</p></li><li><ul><li>Kafka会为Consumer Group要消费的每个Partion保存一个offset，这个offset标记了该Consumer Group最后消费消息的位置</li><li>这个offset保存在Kafka里一个名为“__consumer_offsets”的Topic中；当Consumer从Kafka拉取消息消费时，同时也要对这个offset提交修改更新操作。这样若一个Consumer消费消息时挂了，其他Consumer可以通过这个offset值重新找到上一个消息再进行处理</li></ul></li></ul></li></ul><p><a href="https://zhuanlan.zhihu.com/p/48896367" target="_blank" rel="noopener">参考文章</a></p><h2 id="kafka-与其他消息组件对比"><a href="#kafka-与其他消息组件对比" class="headerlink" title="kafka 与其他消息组件对比"></a>kafka 与其他消息组件对比</h2><p><a href="https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/why-mq.md" target="_blank" rel="noopener">推荐阅读文章</a></p><table><thead><tr><th>特性</th><th>ActiveMQ</th><th>RabbitMQ</th><th>RocketMQ</th><th>Kafka</th></tr></thead><tbody><tr><td>单机吞吐量</td><td>万级，比 RocketMQ、Kafka 低一个数量级</td><td>同 ActiveMQ</td><td>10 万级，支撑高吞吐</td><td>10 万级，高吞吐，一般配合大数据类的系统来进行实时数据计算、日志采集等场景</td></tr><tr><td>topic 数量对吞吐量的影响</td><td></td><td></td><td>topic 可以达到几百/几千的级别，吞吐量会有较小幅度的下降，这是 RocketMQ 的一大优势，在同等机器下，可以支撑大量的 topic</td><td>topic 从几十到几百个时候，吞吐量会大幅度下降，在同等机器下，Kafka 尽量保证 topic 数量不要过多，如果要支撑大规模的 topic，需要增加更多的机器资源</td></tr><tr><td>时效性</td><td>ms 级</td><td>微秒级，这是 RabbitMQ 的一大特点，延迟最低</td><td>ms 级</td><td>延迟在 ms 级以内</td></tr><tr><td>可用性</td><td>高，基于主从架构实现高可用</td><td>同 ActiveMQ</td><td>非常高，分布式架构</td><td>非常高，分布式，一个数据多个副本，少数机器宕机，不会丢失数据，不会导致不可用</td></tr><tr><td>消息可靠性</td><td>有较低的概率丢失数据</td><td>基本不丢</td><td>经过参数优化配置，可以做到 0 丢失</td><td>同 RocketMQ</td></tr><tr><td>功能支持</td><td>MQ 领域的功能极其完备</td><td>基于 erlang 开发，并发能力很强，性能极好，延时很低</td><td>MQ 功能较为完善，还是分布式的，扩展性好</td><td>功能较为简单，主要支持简单的 MQ 功能，在大数据领域的实时计算以及日志采集被大规模使用</td></tr></tbody></table><h2 id="kafka-实现高吞吐的原理"><a href="#kafka-实现高吞吐的原理" class="headerlink" title="kafka 实现高吞吐的原理"></a>kafka 实现高吞吐的原理</h2><ul><li>读写文件依赖OS文件系统的页缓存，而不是在JVM内部缓存数据，利用OS来缓存，内存利用率高</li><li>sendfile技术（零拷贝），避免了传统网络IO四步流程</li><li>支持End-to-End的压缩</li><li>顺序IO以及常量时间get、put消息</li><li>Partition 可以很好的横向扩展和提供高并发处理</li></ul><p><a href="https://www.jianshu.com/p/d6a73be9d803" target="_blank" rel="noopener">参考文章1</a></p><p><a href="https://blog.csdn.net/stark_summer/article/details/50144591" target="_blank" rel="noopener">参考文章2</a></p><h2 id="kafka怎样保证不重复消费"><a href="#kafka怎样保证不重复消费" class="headerlink" title="kafka怎样保证不重复消费"></a>kafka怎样保证不重复消费</h2><p>此问题其实等价于保证消息队列消费的幂等性</p><p>主要需要结合实际业务来操作:</p><ul><li>比如你拿个数据要写库，你先根据主键查一下，如果这数据都有了，你就别插入了，update 一下好吧。</li><li>比如你是写 Redis，那没问题了，反正每次都是 set，天然幂等性。</li><li>比如你不是上面两个场景，那做的稍微复杂一点，你需要让生产者发送每条数据的时候，里面加一个全局唯一的 id，类似订单 id 之类的东西，然后你这里消费到了之后，先根据这个 id 去比如 Redis 里查一下，之前消费过吗？如果没有消费过，你就处理，然后这个 id 写 Redis。如果消费过了，那你就别处理了，保证别重复处理相同的消息即可。</li><li>比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了，重复数据插入只会报错，不会导致数据库中出现脏数据。</li></ul><p><a href="https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md" target="_blank" rel="noopener">参考文章</a></p><h2 id="kafka怎样保证不丢失消息"><a href="#kafka怎样保证不丢失消息" class="headerlink" title="kafka怎样保证不丢失消息"></a>kafka怎样保证不丢失消息</h2><h4 id="消费端弄丢了数据"><a href="#消费端弄丢了数据" class="headerlink" title="消费端弄丢了数据"></a>消费端弄丢了数据</h4><p>唯一可能导致消费者弄丢数据的情况，就是说，你消费到了这个消息，然后消费者那边<strong>自动提交了 offset</strong>，让 Kafka 以为你已经消费好了这个消息，但其实你才刚准备处理这个消息，你还没处理，你自己就挂了，此时这条消息就丢咯。</p><p>这不是跟 RabbitMQ 差不多吗，大家都知道 Kafka 会自动提交 offset，那么只要<strong>关闭自动提交</strong> offset，在处理完之后自己手动提交 offset，就可以保证数据不会丢。但是此时确实还是<strong>可能会有重复消费</strong>，比如你刚处理完，还没提交 offset，结果自己挂了，此时肯定会重复消费一次，自己保证幂等性就好了。</p><p>生产环境碰到的一个问题，就是说我们的 Kafka 消费者消费到了数据之后是写到一个内存的 queue 里先缓冲一下，结果有的时候，你刚把消息写入内存 queue，然后消费者会自动提交 offset。然后此时我们重启了系统，就会导致内存 queue 里还没来得及处理的数据就丢失了。</p><h4 id="Kafka-弄丢了数据"><a href="#Kafka-弄丢了数据" class="headerlink" title="Kafka 弄丢了数据"></a>Kafka 弄丢了数据</h4><p>这块比较常见的一个场景，就是 Kafka 某个 broker 宕机，然后重新选举 partition 的 leader。大家想想，要是此时其他的 follower 刚好还有些数据没有同步，结果此时 leader 挂了，然后选举某个 follower 成 leader 之后，不就少了一些数据？这就丢了一些数据啊。</p><p>生产环境也遇到过，我们也是，之前 Kafka 的 leader 机器宕机了，将 follower 切换为 leader 之后，就会发现说这个数据就丢了。</p><p>所以此时一般是要求起码设置如下 4 个参数：</p><ul><li>给 topic 设置 <code>replication.factor</code> 参数：这个值必须大于 1，要求每个 partition 必须有至少 2 个副本。</li><li>在 Kafka 服务端设置 <code>min.insync.replicas</code> 参数：这个值必须大于 1，这个是要求一个 leader 至少感知到有至少一个 follower 还跟自己保持联系，没掉队，这样才能确保 leader 挂了还有一个 follower 吧。</li><li>在 producer 端设置 <code>acks=all</code>：这个是要求每条数据，必须是<strong>写入所有 replica 之后，才能认为是写成功了</strong>。</li><li>在 producer 端设置 <code>retries=MAX</code>（很大很大很大的一个值，无限次重试的意思）：这个是<strong>要求一旦写入失败，就无限重试</strong>，卡在这里了。</li></ul><p>我们生产环境就是按照上述要求配置的，这样配置之后，至少在 Kafka broker 端就可以保证在 leader 所在 broker 发生故障，进行 leader 切换时，数据不会丢失。</p><h4 id="生产者会不会弄丢数据？"><a href="#生产者会不会弄丢数据？" class="headerlink" title="生产者会不会弄丢数据？"></a>生产者会不会弄丢数据？</h4><p>如果按照上述的思路设置了 <code>acks=all</code>，一定不会丢，要求是，你的 leader 接收到消息，所有的 follower 都同步到了消息之后，才认为本次写成功了。如果没满足这个条件，生产者会自动不断的重试，重试无限次。</p><p><a href="https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md" target="_blank" rel="noopener">参考文章</a></p><h2 id="kafka-与-spark-streaming-集成-如何保证-exactly-once-语义"><a href="#kafka-与-spark-streaming-集成-如何保证-exactly-once-语义" class="headerlink" title="kafka 与 spark streaming 集成,如何保证 exactly once 语义"></a>kafka 与 spark streaming 集成,如何保证 exactly once 语义</h2><ul><li><h3 id="Spark-Streaming上游对接kafka时保证Exactly-Once"><a href="#Spark-Streaming上游对接kafka时保证Exactly-Once" class="headerlink" title="Spark Streaming上游对接kafka时保证Exactly Once"></a>Spark Streaming上游对接kafka时保证Exactly Once</h3><p>Spark Streaming使用Direct模式对接上游kafka。无论kafka有多少个partition， 使用Direct模式总能保证SS中有相同数量的partition与之相对， 也就是说SS中的KafkaRDD的并发数量在Direct模式下是由上游kafka决定的。 在这个模式下，kafka的offset是作为KafkaRDD的一部分存在，会存储在checkpoints中， 由于checkpoints只存储offset内容，而不存储数据，这就使得checkpoints是相对轻的操作。 这就使得SS在遇到故障时，可以从checkpoint中恢复上游kafka的offset，从而保证exactly once</p></li><li><h3 id="Spark-Streaming输出下游保证Exactly-once"><a href="#Spark-Streaming输出下游保证Exactly-once" class="headerlink" title="Spark Streaming输出下游保证Exactly once"></a>Spark Streaming输出下游保证Exactly once</h3><ul><li><p>第一种“鸵鸟做法”，就是期望下游（数据）具有幂等特性。</p><p>多次尝试总是写入相同的数据，例如，saveAs***Files 总是将相同的数据写入生成的文件</p></li></ul></li><li><ul><li><p>使用事务更新</p><p>所有更新都是事务性的，以便更新完全按原子进行。这样做的一个方法如下： 使用批处理时间(在foreachRDD中可用)和RDD的partitionIndex（分区索引）来创建identifier（标识符)。 该标识符唯一地标识streaming application 中的blob数据。 使用该identifier，blob 事务地更新到外部系统中。也就是说，如果identifier尚未提交，则以 (atomicall)原子方式提交分区数据和identifier。否则，如果已经提交，请跳过更新。</p></li></ul></li></ul><p><a href="http://www.aihacks.life/post/spark-streaming%E5%A6%82%E4%BD%95%E4%BF%9D%E8%AF%81exactly-once%E8%AF%AD%E4%B9%89/" target="_blank" rel="noopener">参考文章1</a></p><p><a href="https://www.jianshu.com/p/10de8f3b1be8" target="_blank" rel="noopener">参考文章2</a></p><p><a href="https://blog.csdn.net/cymvp/article/details/52605987" target="_blank" rel="noopener">参考文章3</a></p><h2 id="Ack-有哪几种-生产中怎样选择"><a href="#Ack-有哪几种-生产中怎样选择" class="headerlink" title="Ack 有哪几种, 生产中怎样选择?"></a>Ack 有哪几种, 生产中怎样选择?</h2><p>ack=0/1/-1的不同情况：</p><ul><li><p>Ack = 0</p><p>producer不等待broker的ack，broker一接收到还没有写入磁盘就已经返回，当broker故障时有可能丢失数据；</p></li><li><p>Ack = 1</p><p>producer等待broker的ack，partition的leader落盘成功后返回ack，如果在follower同步成功之前leader故障，那么将会丢失数据；</p></li><li><p>Ack = -1</p><p>producer等待broker的ack，partition的leader和follower全部落盘成功后才返回ack，数据一般不会丢失，延迟时间长但是可靠性高。</p></li></ul><p><strong>生产中主要以 Ack=-1为主,如果压力过大,可切换为Ack=1. Ack=0的情况只能在测试中使用.</strong></p><h2 id="如何通过offset寻找数据"><a href="#如何通过offset寻找数据" class="headerlink" title="如何通过offset寻找数据"></a>如何通过offset寻找数据</h2><p>如果consumer要找offset是1008的消息，那么，</p><p>1，按照二分法找到小于1008的segment，也就是00000000000000001000.log和00000000000000001000.index</p><p>2，用目标offset减去文件名中的offset得到消息在这个segment中的偏移量。也就是1008-1000=8，偏移量是8。</p><p>3，再次用二分法在index文件中找到对应的索引，也就是第三行6,45。</p><p>4，到log文件中，从偏移量45的位置开始（实际上这里的消息offset是1006），顺序查找，直到找到offset为1008的消息。查找期间kafka是按照log的存储格式来判断一条消息是否结束的。</p><p><a href="https://blog.csdn.net/lkforce/article/details/77854813" target="_blank" rel="noopener">参考文章</a></p><h2 id="如何清理过期数据"><a href="#如何清理过期数据" class="headerlink" title="如何清理过期数据"></a>如何清理过期数据</h2><ul><li><h4 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h4><p>log.cleanup.policy=delete启用删除策略</p><ul><li>直接删除，删除后的消息不可恢复。可配置以下两个策略：<br>清理超过指定时间清理：<br>log.retention.hours=16</li><li>超过指定大小后，删除旧的消息：<br>log.retention.bytes=1073741824<br>为了避免在删除时阻塞读操作，采用了copy-on-write形式的实现，删除操作进行时，读取操作的二分查找功能实际是在一个静态的快照副本上进行的，这类似于Java的CopyOnWriteArrayList。</li></ul></li><li><h4 id="压缩"><a href="#压缩" class="headerlink" title="压缩"></a>压缩</h4><p>将数据压缩，只保留每个key最后一个版本的数据。<br>首先在broker的配置中设置log.cleaner.enable=true启用cleaner，这个默认是关闭的。<br>在topic的配置中设置log.cleanup.policy=compact启用压缩策略。</p><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/kafka压缩.png" alt=""></p><p>如上图，在整个数据流中，每个Key都有可能出现多次，压缩时将根据Key将消息聚合，只保留最后一次出现时的数据。这样，无论什么时候消费消息，都能拿到每个Key的最新版本的数据。<br>压缩后的offset可能是不连续的，比如上图中没有5和7，因为这些offset的消息被merge了，当从这些offset消费消息时，将会拿到比这个offset大的offset对应的消息，比如，当试图获取offset为5的消息时，实际上会拿到offset为6的消息，并从这个位置开始消费。<br>这种策略只适合特俗场景，比如消息的key是用户ID，消息体是用户的资料，通过这种压缩策略，整个消息集里就保存了所有用户最新的资料。<br>压缩策略支持删除，当某个Key的最新版本的消息没有内容时，这个Key将被删除，这也符合以上逻辑。</p></li></ul><p><a href="https://blog.csdn.net/honglei915/article/details/49683065" target="_blank" rel="noopener">参考文章</a></p><h2 id="1条message中包含哪些信息"><a href="#1条message中包含哪些信息" class="headerlink" title="1条message中包含哪些信息"></a>1条message中包含哪些信息</h2><table><thead><tr><th><strong>Field</strong></th><th>Description</th></tr></thead><tbody><tr><td>Attributes</td><td>该字节包含有关消息的元数据属性。 最低的2位包含用于消息的压缩编解码器。 其他位应设置为0。</td></tr><tr><td>Crc</td><td>CRC是消息字节的其余部分的CRC32。 这用于检查代理和使用者上的消息的完整性。</td></tr><tr><td></td><td>key是用于分区分配的可选参数。 key可以为null。</td></tr><tr><td>MagicByte</td><td>这是用于允许向后兼容的消息二进制格式演变的版本ID。 当前值为0。</td></tr><tr><td>Offset</td><td>这是kafka中用作日志序列号的偏移量。 当producer发送消息时，它实际上并不知道偏移量，并且可以填写它喜欢的任何值。</td></tr><tr><td>Value</td><td>该值是实际的消息内容，作为不透明的字节数组。 Kafka支持递归消息，在这种情况下，它本身可能包含消息集。 消息可以为null。</td></tr></tbody></table><h2 id="讲一下zookeeper在kafka中的作用"><a href="#讲一下zookeeper在kafka中的作用" class="headerlink" title="讲一下zookeeper在kafka中的作用"></a>讲一下zookeeper在kafka中的作用</h2><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/kafka在zk中的存储结构.png" alt=""></p><h4 id="zk的作用主要有如下几点"><a href="#zk的作用主要有如下几点" class="headerlink" title="zk的作用主要有如下几点:"></a>zk的作用主要有如下几点:</h4><ol><li>kafka的元数据都存放在zk上面,由zk来管理</li><li>0.8之前版本的kafka, consumer的消费状态，group的管理以及 offset的值都是由zk管理的,现在offset会保存在本地topic文件里</li><li>负责borker的lead选举和管理</li></ol><h2 id="kafka-可以脱离-zookeeper-单独使用吗"><a href="#kafka-可以脱离-zookeeper-单独使用吗" class="headerlink" title="kafka 可以脱离 zookeeper 单独使用吗"></a>kafka 可以脱离 zookeeper 单独使用吗</h2><p>kafka 不能脱离 zookeeper 单独使用，因为 kafka 使用 zookeeper 管理和协调 kafka 的节点服务器。</p><h2 id="kafka-有几种数据保留策略"><a href="#kafka-有几种数据保留策略" class="headerlink" title="kafka 有几种数据保留策略"></a>kafka 有几种数据保留策略</h2><p>kafka 有两种数据保存策略：按照过期时间保留和按照存储的消息大小保留。</p><h2 id="kafka同时设置了7天和10G清除数据-到第5天的时候消息到达了10G-这个时候kafka如何处理"><a href="#kafka同时设置了7天和10G清除数据-到第5天的时候消息到达了10G-这个时候kafka如何处理" class="headerlink" title="kafka同时设置了7天和10G清除数据,到第5天的时候消息到达了10G,这个时候kafka如何处理?"></a>kafka同时设置了7天和10G清除数据,到第5天的时候消息到达了10G,这个时候kafka如何处理?</h2><p>这个时候 kafka 会执行数据清除工作，时间和大小不论那个满足条件，都会清空数据。</p>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="Kafka" scheme="https://icocos.github.io/categories/Kafka/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="面试" scheme="https://icocos.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
    
      <category term="Kafka" scheme="https://icocos.github.io/tags/Kafka/"/>
    
  </entry>
  
  <entry>
    <title>大数据面试之HBase</title>
    <link href="https://icocos.github.io/2020/03/08/%E5%A4%A7%E6%95%B0%E6%8D%AE%E9%9D%A2%E8%AF%95%E4%B9%8BHBase/"/>
    <id>https://icocos.github.io/2020/03/08/大数据面试之HBase/</id>
    <published>2020-03-08T10:21:09.000Z</published>
    <updated>2020-04-09T14:45:08.845Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h2 id="讲一下-Hbase-架构"><a href="#讲一下-Hbase-架构" class="headerlink" title="讲一下 Hbase 架构"></a>讲一下 Hbase 架构</h2><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/hbase架构图.png" alt=""></p><p><strong>Hbase主要包含HMaster/HRegionServer/Zookeeper</strong></p><ul><li><p><strong>HRegionServer 负责实际数据的读写. 当访问数据时, 客户端直接与RegionServer通信.</strong></p><p>HBase的表根据Row Key的区域分成多个Region, 一个Region包含这这个区域内所有数据. 而Region server负责管理多个Region, 负责在这个Region server上的所有region的读写操作. </p></li><li><p><strong>HMaster 负责管理Region的位置, DDL(新增和删除表结构)</strong></p><ul><li>协调RegionServer</li><li>在集群处于数据恢复或者动态调整负载时,分配Region到某一个RegionServer中</li><li>管控集群,监控所有Region Server的状态</li><li>提供DDL相关的API, 新建(create),删除(delete)和更新(update)表结构.</li></ul></li><li><p><strong>Zookeeper 负责维护和记录整个Hbase集群的状态</strong></p><p>zookeeper探测和记录Hbase集群中服务器的状态信息.如果zookeeper发现服务器宕机,它会通知Hbase的master节点.</p></li></ul><h2 id="hbase-如何设计rowkey"><a href="#hbase-如何设计rowkey" class="headerlink" title="hbase 如何设计rowkey"></a>hbase 如何设计rowkey</h2><ul><li><p><strong>RowKey长度原则</strong></p><p>Rowkey是一个二进制码流，Rowkey的长度被很多开发者建议说设计在10~100个字节，不过建议是越短越好，不要超过16个字节。</p><p>原因如下：</p><ul><li><p>数据的持久化文件HFile中是按照KeyValue存储的，如果Rowkey过长比如100个字节，1000万列数据光Rowkey就要占用100*1000万=10亿个字节，将近1G数据，这会极大影响HFile的存储效率；</p></li><li><p>MemStore将缓存部分数据到内存，如果Rowkey字段过长内存的有效利用率会降低，系统将无法缓存更多的数据，这会降低检索效率。因此Rowkey的字节长度越短越好。</p></li><li><p>目前操作系统是都是64位系统，内存8字节对齐。控制在16个字节，8字节的整数倍利用操作系统的最佳特性。</p></li></ul></li><li><p><strong>RowKey散列原则</strong></p><p>如果Rowkey是按时间戳的方式递增，不要将时间放在二进制码的前面，建议将Rowkey的高位作为散列字段，由程序循环生成，低位放时间字段，这样将提高数据均衡分布在每个Regionserver实现负载均衡的几率。如果没有散列字段，首字段直接是时间信息将产生所有新数据都在一个RegionServer上堆积的热点现象，这样在做数据检索的时候负载将会集中在个别RegionServer，降低查询效率。</p></li><li><p><strong>RowKey唯一原则</strong></p><p>必须在设计上保证其唯一性。</p></li></ul><p><a href="https://zhuanlan.zhihu.com/p/30074408" target="_blank" rel="noopener">参考文章1</a></p><p><a href="http://www.nosqlnotes.com/technotes/hbase/hbase-rowkey-design/" target="_blank" rel="noopener">参考文章2</a></p><h2 id="讲一下hbase的存储结构-这样的存储结构有什么优缺点"><a href="#讲一下hbase的存储结构-这样的存储结构有什么优缺点" class="headerlink" title="讲一下hbase的存储结构,这样的存储结构有什么优缺点"></a>讲一下hbase的存储结构,这样的存储结构有什么优缺点</h2><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures\hbase逻辑结构.png" alt=""></p><p><strong>Hbase的优点及应用场景</strong>:</p><ol><li>半结构化或非结构化数据:<br>对于数据结构字段不够确定或杂乱无章非常难按一个概念去进行抽取的数据适合用HBase，因为HBase支持动态添加列。</li><li>记录很稀疏：<br>RDBMS的行有多少列是固定的。为null的列浪费了存储空间。HBase为null的Column不会被存储，这样既节省了空间又提高了读性能。</li><li>多版本号数据：<br>依据Row key和Column key定位到的Value能够有随意数量的版本号值，因此对于须要存储变动历史记录的数据，用HBase是很方便的。比方某个用户的Address变更，用户的Address变更记录也许也是具有研究意义的。</li><li>仅要求最终一致性：<br>对于数据存储事务的要求不像金融行业和财务系统这么高，只要保证最终一致性就行。（比如HBase+elasticsearch时，可能出现数据不一致）</li><li>高可用和海量数据以及很大的瞬间写入量：<br>WAL解决高可用，支持PB级数据，put性能高<br>适用于插入比查询操作更频繁的情况。比如，对于历史记录表和日志文件。（HBase的写操作更加高效）</li><li>业务场景简单：<br>不需要太多的关系型数据库特性，列入交叉列，交叉表，事务，连接等。</li></ol><p><strong>Hbase的缺点：</strong></p><ol><li>单一RowKey固有的局限性决定了它不可能有效地支持多条件查询</li><li>不适合于大范围扫描查询</li><li>不直接支持 SQL 的语句查询</li></ol><p><a href="https://www.iteye.com/blog/forlan-2364661" target="_blank" rel="noopener">参考文章1</a></p><p><a href="https://blog.csdn.net/liaynling/article/details/81199238" target="_blank" rel="noopener">参考文章2</a></p><p><a href="https://juejin.im/post/5c31cf486fb9a04a102f6f89#heading-2" target="_blank" rel="noopener">参考文章3</a></p><h2 id="hbase的HA实现-zookeeper在其中的作用"><a href="#hbase的HA实现-zookeeper在其中的作用" class="headerlink" title="hbase的HA实现,zookeeper在其中的作用"></a>hbase的HA实现,zookeeper在其中的作用</h2><p> HBase中可以启动多个HMaster，通过Zookeeper的Master Election机制保证总有一个Master运行。<br>配置HBase高可用，只需要启动两个HMaster，让Zookeeper自己去选择一个Master Acitve即可</p><p>zk的在这里起到的作用就是用来管理master节点,以及帮助hbase做master选举</p><h2 id="HMaster宕机的时候-哪些操作还能正常工作"><a href="#HMaster宕机的时候-哪些操作还能正常工作" class="headerlink" title="HMaster宕机的时候,哪些操作还能正常工作"></a>HMaster宕机的时候,哪些操作还能正常工作</h2><p>对表内数据的增删查改是可以正常进行的,因为hbase client 访问数据只需要通过 zookeeper 来找到 rowkey 的具体 region 位置即可. 但是对于创建表/删除表等的操作就无法进行了,因为这时候是需要HMaster介入, 并且region的拆分,合并,迁移等操作也都无法进行了</p><h2 id="讲一下hbase的写数据的流程"><a href="#讲一下hbase的写数据的流程" class="headerlink" title="讲一下hbase的写数据的流程"></a>讲一下hbase的写数据的流程</h2><ol><li>Client先访问zookeeper，从.META.表获取相应region信息，然后从meta表获取相应region信息 </li><li>根据namespace、表名和rowkey根据meta表的数据找到写入数据对应的region信息 </li><li>找到对应的regionserver 把数据先写到WAL中，即HLog，然后写到MemStore上 </li><li>MemStore达到设置的阈值后则把数据刷成一个磁盘上的StoreFile文件。 </li><li>当多个StoreFile文件达到一定的大小后(这个可以称之为小合并，合并数据可以进行设置，必须大于等于2，小于10——hbase.hstore.compaction.max和hbase.hstore.compactionThreshold，默认为10和3)，会触发Compact合并操作，合并为一个StoreFile，（这里同时进行版本的合并和数据删除。） </li><li>当Storefile大小超过一定阈值后，会把当前的Region分割为两个（Split）【可称之为大合并，该阈值通过hbase.hregion.max.filesize设置，默认为10G】，并由Hmaster分配到相应的HRegionServer，实现负载均衡</li></ol><h2 id="讲一下hbase读数据的流程"><a href="#讲一下hbase读数据的流程" class="headerlink" title="讲一下hbase读数据的流程"></a>讲一下hbase读数据的流程</h2><ol><li><p>首先，客户端需要获知其想要读取的信息的Region的位置，这个时候，Client访问hbase上数据时并不需要Hmaster参与（HMaster仅仅维护着table和Region的元数据信息，负载很低），只需要访问zookeeper，从meta表获取相应region信息(地址和端口等)。【Client请求ZK获取.META.所在的RegionServer的地址。】</p></li><li><p>客户端会将该保存着RegionServer的位置信息的元数据表.META.进行缓存。然后在表中确定待检索rowkey所在的RegionServer信息（得到持有对应行键的.META表的服务器名）。【获取访问数据所在的RegionServer地址】</p></li><li><p>根据数据所在RegionServer的访问信息，客户端会向该RegionServer发送真正的数据读取请求。服务器端接收到该请求之后需要进行复杂的处理。</p></li><li><p>先从MemStore找数据，如果没有，再到StoreFile上读(为了读取的效率)。</p></li></ol><p><a href="https://blog.csdn.net/HaixWang/article/details/79520141#%E8%AF%BB%E6%B5%81%E7%A8%8B%E6%A6%82%E8%A7%88" target="_blank" rel="noopener">参考文章1</a></p><p><a href="http://hbasefly.com/2016/12/21/hbase-getorscan/?rkfcfo=fy6gy1" target="_blank" rel="noopener">参考文章2</a></p>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="HBase" scheme="https://icocos.github.io/categories/HBase/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="面试" scheme="https://icocos.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
    
      <category term="HBase" scheme="https://icocos.github.io/tags/HBase/"/>
    
  </entry>
  
  <entry>
    <title>大数据面试之Flink</title>
    <link href="https://icocos.github.io/2020/03/05/%E5%A4%A7%E6%95%B0%E6%8D%AE%E9%9D%A2%E8%AF%95%E4%B9%8BFlink/"/>
    <id>https://icocos.github.io/2020/03/05/大数据面试之Flink/</id>
    <published>2020-03-05T14:29:37.000Z</published>
    <updated>2020-04-09T14:45:07.755Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h2 id="讲一下flink的运行架构"><a href="#讲一下flink的运行架构" class="headerlink" title="讲一下flink的运行架构"></a>讲一下flink的运行架构</h2><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/flink架构图.png" alt=""></p><p>当 Flink 集群启动后，首先会启动一个 JobManger 和一个或多个的 TaskManager。由 Client 提交任务给 JobManager，JobManager 再调度任务到各个 TaskManager 去执行，然后 TaskManager 将心跳和统计信息汇报给 JobManager。TaskManager 之间以流的形式进行数据的传输。上述三者均为独立的 JVM 进程。</p><ul><li><strong>Client</strong> 为提交 Job 的客户端，可以是运行在任何机器上（与 JobManager 环境连通即可）。提交 Job 后，Client 可以结束进程（Streaming的任务），也可以不结束并等待结果返回。</li><li><strong>JobManager</strong> 主要负责调度 Job 并协调 Task 做 checkpoint，职责上很像 Storm 的 Nimbus。从 Client 处接收到 Job 和 JAR 包等资源后，会生成优化后的执行计划，并以 Task 的单元调度到各个 TaskManager 去执行。</li><li><strong>TaskManager</strong> 在启动的时候就设置好了槽位数（Slot），每个 slot 能启动一个 Task，Task 为线程。从 JobManager 处接收需要部署的 Task，部署启动后，与自己的上游建立 Netty 连接，接收数据并处理。</li></ul><p><a href="http://wuchong.me/blog/2016/05/03/flink-internals-overview/" target="_blank" rel="noopener">参考文章1</a></p><p><a href="http://shiyanjun.cn/archives/1508.html" target="_blank" rel="noopener">参考文章2</a></p><h2 id="讲一下flink的作业执行流程"><a href="#讲一下flink的作业执行流程" class="headerlink" title="讲一下flink的作业执行流程"></a>讲一下flink的作业执行流程</h2><p> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/flinkRuntime.png" alt=""></p><p><strong>以yarn模式Per-job方式为例概述作业提交执行流程</strong></p><ol><li><p>当执行executor() 之后,会首先在本地client 中将代码转化为可以提交的 JobGraph</p><p>如果提交为Per-Job模式,则首先需要启动AM, client会首先向资源系统申请资源, 在yarn下即为申请container开启AM, 如果是Session模式的话则不需要这个步骤</p></li><li><p>Yarn分配资源, 开启AM</p></li><li>Client将Job提交给Dispatcher</li><li>Dispatcher 会开启一个新的 JobManager线程</li><li>JM 向Flink 自己的 Resourcemanager申请slot资源来执行任务</li><li>RM 向 Yarn申请资源来启动 TaskManger (Session模式跳过此步)</li><li>Yarn 分配 Container 来启动 taskManger (Session模式跳过此步)</li><li>Flink 的 RM 向 TM 申请 slot资源来启动 task</li><li>TM 将待分配的 slot 提供给 JM</li><li>JM 提交 task, TM 会启动新的线程来执行任务,开始启动后就可以通过 shuffle模块进行 task之间的数据交换</li></ol><p><a href="https://www.bilibili.com/video/av52394455?t=343" target="_blank" rel="noopener">参考视频</a></p><h2 id="flink具体是如何实现exactly-once-语义"><a href="#flink具体是如何实现exactly-once-语义" class="headerlink" title="flink具体是如何实现exactly once 语义"></a>flink具体是如何实现exactly once 语义</h2><p>在谈到 flink 所实现的 exactly-once语义时,主要是2个层面上的,首先 flink在0.9版本以后已经实现了基于state的内部一致性语义, 在1.4版本以后也可以实现端到端 Exactly-Once语义</p><ul><li><h4 id="状态-Exactly-Once"><a href="#状态-Exactly-Once" class="headerlink" title="状态 Exactly-Once"></a>状态 Exactly-Once</h4><p>Flink 提供 exactly-once 的状态（state）投递语义，这为有状态的（stateful）计算提供了准确性保证。也就是状态是不会重复使用的,有且仅有一次消费</p></li></ul><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/flink故障恢复.png" alt=""></p><p>​    这里需要注意的一点是如何理解state语义的exactly-once,并不是说在flink中的所有事件均只会处理一次,而是所有的事件所影响生成的state只有作用一次.</p><p>​    在上图中, 假设每两条消息后出发一次checkPoint操作,持久化一次state. TaskManager 在 处理完 event c 之后被shutdown, 这时候当 JobManager重启task之后, TaskManager  会从 checkpoint 1 处恢复状态,重新执行流处理,也就是说 此时 event c 事件 的的确确是会被再一次处理的. 那么 这里所说的一致性语义是何意思呢? 本身,flink每处理完一条数据都会记录当前进度到 state中, 也就是说在 故障前, 处理完 event c 这件事情已经记录到了state中,但是,由于在checkPoint 2 之前, 就已经发生了宕机,那么 event c 对于state的影响并没有被记录下来,对于整个flink内部系统来说就好像没有发生过一样, 在 故障恢复后, 当触发 checkpoint 2 时, event c 的 state才最终被保存下来. <strong>所以说,可以这样理解, 进入flink 系统中的 事件 永远只会被 一次state记录并checkpoint下来,而state是永远不会发生重复被消费的, 这也就是 flink内部的一致性语义,就叫做 状态 Exactly once.</strong></p><ul><li><h4 id="端到端（end-to-end）Exactly-Once"><a href="#端到端（end-to-end）Exactly-Once" class="headerlink" title="端到端（end-to-end）Exactly-Once"></a>端到端（end-to-end）Exactly-Once</h4></li></ul><p>2017年12月份发布的Apache Flink 1.4版本，引进了一个重要的特性：TwoPhaseCommitSinkFunction.，它抽取了两阶段提交协议的公共部分，使得构建端到端Excatly-Once的Flink程序变为了可能。这些外部系统包括Kafka0.11及以上的版本，以及一些其他的数据输入（data sources）和数据接收(data sink)。它提供了一个抽象层，需要用户自己手动去实现Exactly-Once语义.</p><p>为了提供端到端Exactly-Once语义，除了Flink应用程序本身的状态，Flink写入的外部存储也需要满足这个语义。也就是说，这些外部系统必须提供提交或者回滚的方法，然后通过Flink的checkpoint来协调</p><p><a href="https://www.whitewood.me/2018/10/16/Flink-Exactly-Once-%E6%8A%95%E9%80%92%E5%AE%9E%E7%8E%B0%E6%B5%85%E6%9E%90/" target="_blank" rel="noopener">参考文章1</a></p><p><a href="https://my.oschina.net/u/992559/blog/1819948" target="_blank" rel="noopener">参考文章2</a></p><h2 id="flink-的-window-实现机制"><a href="#flink-的-window-实现机制" class="headerlink" title="flink 的 window 实现机制"></a>flink 的 window 实现机制</h2><p>Flink 中定义一个窗口主要需要以下三个组件。</p><ul><li><p><strong>Window Assigner：</strong>用来决定某个元素被分配到哪个/哪些窗口中去。</p></li><li><p><strong>Trigger：</strong>触发器。决定了一个窗口何时能够被计算或清除，每个窗口都会拥有一个自己的Trigger。</p></li><li><p><strong>Evictor：</strong>可以译为“驱逐者”。在Trigger触发之后，在窗口被处理之前，Evictor（如果有Evictor的话）会用来剔除窗口中不需要的元素，相当于一个filter。</p></li></ul><h4 id="Window-的实现"><a href="#Window-的实现" class="headerlink" title="Window 的实现"></a>Window 的实现</h4><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/flink中window的实现.png" alt=""></p><p>首先上图中的组件都位于一个算子（window operator）中，数据流源源不断地进入算子，每一个到达的元素都会被交给 WindowAssigner。WindowAssigner 会决定元素被放到哪个或哪些窗口（window），可能会创建新窗口。因为一个元素可以被放入多个窗口中，所以同时存在多个窗口是可能的。注意，<code>Window</code>本身只是一个ID标识符，其内部可能存储了一些元数据，如<code>TimeWindow</code>中有开始和结束时间，但是并不会存储窗口中的元素。窗口中的元素实际存储在 Key/Value State 中，key为<code>Window</code>，value为元素集合（或聚合值）。为了保证窗口的容错性，该实现依赖了 Flink 的 State 机制（参见 <a href="https://ci.apache.org/projects/flink/flink-docs-master/apis/streaming/state.html" target="_blank" rel="noopener">state 文档</a>）。</p><p>每一个窗口都拥有一个属于自己的 Trigger，Trigger上会有定时器，用来决定一个窗口何时能够被计算或清除。每当有元素加入到该窗口，或者之前注册的定时器超时了，那么Trigger都会被调用。Trigger的返回结果可以是 continue（不做任何操作），fire（处理窗口数据），purge（移除窗口和窗口中的数据），或者 fire + purge。一个Trigger的调用结果只是fire的话，那么会计算窗口并保留窗口原样，也就是说窗口中的数据仍然保留不变，等待下次Trigger fire的时候再次执行计算。一个窗口可以被重复计算多次知道它被 purge 了。在purge之前，窗口会一直占用着内存。</p><p>当Trigger fire了，窗口中的元素集合就会交给<code>Evictor</code>（如果指定了的话）。Evictor 主要用来遍历窗口中的元素列表，并决定最先进入窗口的多少个元素需要被移除。剩余的元素会交给用户指定的函数进行窗口的计算。如果没有 Evictor 的话，窗口中的所有元素会一起交给函数进行计算。</p><p>计算函数收到了窗口的元素（可能经过了 Evictor 的过滤），并计算出窗口的结果值，并发送给下游。窗口的结果值可以是一个也可以是多个。DataStream API 上可以接收不同类型的计算函数，包括预定义的<code>sum()</code>,<code>min()</code>,<code>max()</code>，还有 <code>ReduceFunction</code>，<code>FoldFunction</code>，还有<code>WindowFunction</code>。WindowFunction 是最通用的计算函数，其他的预定义的函数基本都是基于该函数实现的。</p><p>Flink 对于一些聚合类的窗口计算（如sum,min）做了优化，因为聚合类的计算不需要将窗口中的所有数据都保存下来，只需要保存一个result值就可以了。每个进入窗口的元素都会执行一次聚合函数并修改result值。这样可以大大降低内存的消耗并提升性能。但是如果用户定义了 Evictor，则不会启用对聚合窗口的优化，因为 Evictor 需要遍历窗口中的所有元素，必须要将窗口中所有元素都存下来。</p><p><a href="http://wuchong.me/blog/2016/05/25/flink-internals-window-mechanism/" target="_blank" rel="noopener">参考文章</a></p><h2 id="flink-的-window-分类"><a href="#flink-的-window-分类" class="headerlink" title="flink 的 window 分类"></a>flink 的 window 分类</h2><p><strong>flink中的窗口主要分为3大类共5种窗口</strong>:</p><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/flink中window分类.png" alt=""></p><ul><li><p><strong>Time Window 时间窗口</strong></p><ul><li><p><strong>Tumbing Time Window 滚动时间窗口</strong></p><p>实现统计每一分钟(或其他长度)窗口内 计算的效果</p></li><li><p><strong>Sliding Time Window 滑动时间窗口</strong></p><p>实现每过xxx时间 统计 xxx时间窗口的效果. 比如，我们可以每30秒计算一次最近一分钟用户购买的商品总数。</p></li></ul></li><li><p><strong>Count Window 计数窗口</strong></p><ul><li><p><strong>Tumbing Count Window  滚动计数窗口</strong></p><p>当我们想要每100个用户购买行为事件统计购买总数，那么每当窗口中填满100个元素了，就会对窗口进行计算，这种窗口我们称之为翻滚计数窗口（Tumbling Count Window）</p></li><li><p><strong>Sliding Count Window   滑动计数窗口</strong></p><p>和Sliding Time Window含义是类似的，例如计算每10个元素计算一次最近100个元素的总和</p></li></ul></li><li><p><strong>Session Window  会话窗口</strong></p><p>在这种用户交互事件流中，我们首先想到的是将事件聚合到会话窗口中（一段用户持续活跃的周期），由非活跃的间隙分隔开。如上图所示，就是需要计算每个用户在活跃期间总共购买的商品数量，如果用户30秒没有活动则视为会话断开（假设raw data stream是单个用户的购买行为流）</p></li></ul><h2 id="flink-的-state-是存储在哪里的"><a href="#flink-的-state-是存储在哪里的" class="headerlink" title="flink 的 state 是存储在哪里的"></a>flink 的 state 是存储在哪里的</h2><p>Apache Flink内部有四种state的存储实现，具体如下：</p><ul><li><strong>基于内存的HeapStateBackend</strong> - 在debug模式使用，不 建议在生产模式下应用；</li><li><strong>基于HDFS的FsStateBackend</strong> - 分布式文件持久化，每次读写都产生网络IO，整体性能不佳；</li><li><strong>基于RocksDB的RocksDBStateBackend</strong> - 本地文件+异步HDFS持久化；</li><li><strong>基于Niagara(Alibaba内部实现)NiagaraStateBackend</strong> - 分布式持久化- 在Alibaba生产环境应用；</li></ul><p><a href="https://juejin.im/post/5c87dbdbe51d45494c77d607" target="_blank" rel="noopener">参考文章</a></p><h2 id="flink是如何实现反压的"><a href="#flink是如何实现反压的" class="headerlink" title="flink是如何实现反压的"></a>flink是如何实现反压的</h2><p>flink的反压经历了两个发展阶段,分别是基于TCP的反压(&lt;1.5)和基于credit的反压(&gt;1.5)</p><ul><li><h4 id="基于-TCP-的反压"><a href="#基于-TCP-的反压" class="headerlink" title="基于 TCP 的反压"></a>基于 TCP 的反压</h4><p>flink中的消息发送通过RS(ResultPartition),消息接收通过IC(InputGate),两者的数据都是以 LocalBufferPool的形式来存储和提取,进一步的依托于Netty的NetworkBufferPool,之后更底层的便是依托于TCP的滑动窗口机制,当IC端的buffer池满了之后,两个task之间的滑动窗口大小便为0,此时RS端便无法再发送数据</p><p>基于TCP的反压最大的问题是会造成整个TaskManager端的反压,所有的task都会受到影响</p></li><li><h4 id="基于-Credit-的反压"><a href="#基于-Credit-的反压" class="headerlink" title="基于 Credit 的反压"></a>基于 Credit 的反压</h4><p>RS与IC之间通过backlog和credit来确定双方可以发送和接受的数据量的大小以提前感知,而不是通过TCP滑动窗口的形式来确定buffer的大小之后再进行反压</p><p><img src="D:\Note\big-data-interview\BigData-Interview\pictures\flink基于credit的反压.png" alt=""></p></li></ul><p><a href="https://www.bilibili.com/video/av55487329" target="_blank" rel="noopener">参考视频</a></p><p><a href="https://blog.csdn.net/u010376788/article/details/92086752" target="_blank" rel="noopener">参考文章1</a></p><p><a href="https://blog.csdn.net/u010376788/article/details/95047250" target="_blank" rel="noopener">参考文章2</a></p><h2 id="flink的部署模式都有哪些"><a href="#flink的部署模式都有哪些" class="headerlink" title="flink的部署模式都有哪些"></a>flink的部署模式都有哪些</h2><p><strong>flink可以以多种方式部署,包括standlone模式/yarn/Mesos/Kubernetes/Docker/AWS/Google Compute Engine/MAPR等</strong></p><p>一般公司中主要采用 on yarn模式</p><h2 id="讲一下flink-on-yarn的部署"><a href="#讲一下flink-on-yarn的部署" class="headerlink" title="讲一下flink on yarn的部署"></a>讲一下flink on yarn的部署</h2><p>Flink作业提交有两种类型:</p><ul><li><h4 id="yarn-session"><a href="#yarn-session" class="headerlink" title="yarn session"></a>yarn session</h4><p>需要先启动集群，然后在提交作业，接着会向yarn申请一块空间后，资源永远保持不变。如果资源满了，下一个作业就无法提交，只能等到yarn中的其中一个作业执行完成后，释放了资源，那下一个作业才会正常提交.</p><ul><li><p>客户端模式</p><p>对于客户端模式而言，你可以启动多个yarn session，一个yarn session模式对应一个JobManager,并按照需求提交作业，同一个Session中可以提交多个Flink作业。如果想要停止Flink Yarn Application，需要通过yarn application -kill命令来停止.</p></li><li><p>分离式模式</p><p>对于分离式模式，并不像客户端那样可以启动多个yarn session，如果启动多个，会出现下面的session一直处在等待状态。JobManager的个数只能是一个，同一个Session中可以提交多个Flink作业。如果想要停止Flink Yarn Application，需要通过yarn application -kill命令来停止</p></li></ul></li><li><h4 id="Flink-run-Per-Job"><a href="#Flink-run-Per-Job" class="headerlink" title="Flink run(Per-Job)"></a>Flink run(Per-Job)</h4><p>直接在YARN上提交运行Flink作业(Run a Flink job on YARN)，这种方式的好处是一个任务会对应一个job,即没提交一个作业会根据自身的情况，向yarn申请资源，直到作业执行完成，并不会影响下一个作业的正常运行，除非是yarn上面没有任何资源的情况下</p></li></ul><table><thead><tr><th>Session</th><th></th></tr></thead><tbody><tr><td>共享Dispatcher和Resource Manager</td><td>Dispatcher和Resource Manager</td></tr><tr><td>共享资源(即 TaskExecutor)</td><td>按需要申请资源 (即 TaskExecutor)</td></tr><tr><td>适合规模小,执行时间短的作业</td></tr></tbody></table><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/flinkOnYarn.png" alt=""></p><h2 id="flink中的时间概念-eventTime-和-processTime的区别"><a href="#flink中的时间概念-eventTime-和-processTime的区别" class="headerlink" title="flink中的时间概念 , eventTime 和 processTime的区别"></a>flink中的时间概念 , eventTime 和 processTime的区别</h2><p>Flink中有三种时间概念,分别是 Processing Time、Event Time 和 Ingestion Time</p><ul><li><h4 id="Processing-Time"><a href="#Processing-Time" class="headerlink" title="Processing Time"></a>Processing Time</h4><p>Processing Time 是指事件被处理时机器的系统时间。</p><p>当流程序在 Processing Time 上运行时，所有基于时间的操作(如时间窗口)将使用当时机器的系统时间。每小时 Processing Time 窗口将包括在系统时钟指示整个小时之间到达特定操作的所有事件</p></li><li><h4 id="Event-Time"><a href="#Event-Time" class="headerlink" title="Event Time"></a>Event Time</h4><p>Event Time 是事件发生的时间，一般就是数据本身携带的时间。这个时间通常是在事件到达 Flink 之前就确定的，并且可以从每个事件中获取到事件时间戳。在 Event Time 中，时间取决于数据，而跟其他没什么关系。Event Time 程序必须指定如何生成 Event Time 水印，这是表示 Event Time 进度的机制</p></li><li><h4 id="Ingestion-Time"><a href="#Ingestion-Time" class="headerlink" title="Ingestion Time"></a>Ingestion Time</h4><p>Ingestion Time 是事件进入 Flink 的时间。 在源操作处，每个事件将源的当前时间作为时间戳，并且基于时间的操作（如时间窗口）会利用这个时间戳</p><p>Ingestion Time 在概念上位于 Event Time 和 Processing Time 之间。 与 Processing Time 相比，它稍微贵一些，但结果更可预测。因为 Ingestion Time 使用稳定的时间戳（在源处分配一次），所以对事件的不同窗口操作将引用相同的时间戳，而在 Processing Time 中，每个窗口操作符可以将事件分配给不同的窗口（基于机器系统时间和到达延迟）</p><p>与 Event Time 相比，Ingestion Time 程序无法处理任何无序事件或延迟数据，但程序不必指定如何生成水印</p></li></ul><p><a href="https://zhuanlan.zhihu.com/p/55322400" target="_blank" rel="noopener">参考文章</a></p><h2 id="flink中的session-Window怎样使用"><a href="#flink中的session-Window怎样使用" class="headerlink" title="flink中的session Window怎样使用"></a>flink中的session Window怎样使用</h2><p>会话窗口主要是将某段时间内活跃度较高的数据聚合成一个窗口进行计算,窗口的触发条件是 Session Gap, 是指在规定的时间内如果没有数据活跃接入,则认为窗口结束,然后触发窗口结果</p><p>Session Windows窗口类型比较适合非连续性数据处理或周期性产生数据的场景,根据用户在线上某段时间内的活跃度对用户行为进行数据统计</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">val sessionWindowStream = inputStream</span><br><span class="line">.keyBy(_.id)</span><br><span class="line">//使用EventTimeSessionWindow 定义 Event Time 滚动窗口</span><br><span class="line">.window(EventTimeSessionWindow.withGap(Time.milliseconds(10)))</span><br><span class="line">.process(......)</span><br></pre></td></tr></table></figure><p>Session Window 本质上没有固定的起止时间点,因此底层计算逻辑和Tumbling窗口及Sliding 窗口有一定的区别,</p><p>Session Window 为每个进入的数据都创建了一个窗口,最后再将距离窗口Session Gap 最近的窗口进行合并,然后计算窗口结果</p>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="Flink" scheme="https://icocos.github.io/categories/Flink/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="面试" scheme="https://icocos.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
    
      <category term="Flink" scheme="https://icocos.github.io/tags/Flink/"/>
    
  </entry>
  
  <entry>
    <title>大数据面试之Spark</title>
    <link href="https://icocos.github.io/2020/03/04/%E5%A4%A7%E6%95%B0%E6%8D%AE%E9%9D%A2%E8%AF%95%E4%B9%8BSpark/"/>
    <id>https://icocos.github.io/2020/03/04/大数据面试之Spark/</id>
    <published>2020-03-04T15:56:50.000Z</published>
    <updated>2020-04-09T14:45:08.820Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h2 id="讲一下spark-的运行架构"><a href="#讲一下spark-的运行架构" class="headerlink" title="讲一下spark 的运行架构"></a>讲一下spark 的运行架构</h2><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/spark架构图.jpg" alt=""></p><ul><li><p><strong>Cluster Manager(Master)</strong>：在standalone模式中即为Master主节点，控制整个集群，监控worker。在YARN模式中为资源管理器</p></li><li><p><strong>Worker节点</strong>：从节点，负责控制计算节点，启动Executor或者Driver。</p></li><li><p><strong>Driver</strong>： 运行Application 的main()函数</p></li><li><p><strong>Executor</strong>：执行器，是为某个Application运行在worker node上的一个进程</p></li></ul><p><a href="https://juejin.im/post/5a73c8386fb9a0635e3cafaa" target="_blank" rel="noopener">参考文章</a></p><h2 id="一个spark程序的执行流程"><a href="#一个spark程序的执行流程" class="headerlink" title="一个spark程序的执行流程"></a>一个spark程序的执行流程</h2><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/spark程序执行流程.jpg" alt=""></p><ul><li><strong>A -&gt;</strong> 当 Driver 进程被启动之后,首先它将发送请求到Master节点上,进行Spark应用程序的注册</li><li><strong>B -&gt;</strong> Master在接受到Spark应用程序的注册申请之后,会发送给Worker,让其进行资源的调度和分配.</li><li><strong>C -&gt;</strong> Worker 在接受Master的请求之后,会为Spark应用程序启动Executor, 来分配资源</li><li><strong>D -&gt;</strong> Executor启动分配资源好后,就会想Driver进行反注册,这是Driver已经知道哪些Executor为他服务了</li><li><strong>E -&gt;</strong> 当Driver得到注册了Executor之后,就可以开始正式执行spark应用程序了. 首先第一步,就是创建初始RDD,读取数据源,再执行之后的一系列算子. HDFS文件内容被读取到多个worker节点上,形成内存中的分布式数据集,也就是初始RDD</li><li><strong>F -&gt;</strong> Driver就会根据 Job 任务任务中的算子形成对应的task,最后提交给 Executor, 来分配给task进行计算的线程</li><li><strong>G -&gt;</strong> task就会去调用对应的任务数据来计算,并task会对调用过来的RDD的partition数据执行指定的算子操作,形成新的RDD的partition,这时一个大的循环就结束了</li><li>后续的RDD的partition数据又通过Driver形成新的一批task提交给Executor执行,循环这个操作,直到所有的算子结束</li></ul><p><a href="https://zhuanlan.zhihu.com/p/35713084" target="_blank" rel="noopener">参考文章</a></p><h2 id="spark的shuffle介绍"><a href="#spark的shuffle介绍" class="headerlink" title="spark的shuffle介绍"></a>spark的shuffle介绍</h2><p><strong>spark中的shuffle主要有3种:</strong></p><ul><li><p><strong>Hash Shuffle</strong> 2.0以后移除</p><p><img src="D:\Note\big-data-interview\BigData-Interview\pictures\spark-shuffle-v1.png" alt=""></p><p>在map阶段(shuffle write)，每个map都会为下游stage的每个partition写一个临时文件，假如下游stage有1000个partition，那么每个map都会生成1000个临时文件，一般来说一个executor上会运行多个map task，这样下来，一个executor上会有非常多的临时文件，假如一个executor上运行M个map task，下游stage有N个partition，那么一个executor上会生成M<em>N个文件。另一方面，如果一个executor上有K个core，那么executor同时可运行K个task，这样一来，就会同时申请K</em>N个文件描述符，一旦partition数较多，势必会耗尽executor上的文件描述符，同时生成K*N个write handler也会带来大量内存的消耗。</p><p>在reduce阶段(shuffle read)，每个reduce task都会拉取所有map对应的那部分partition数据，那么executor会打开所有临时文件准备网络传输，这里又涉及到大量文件描述符，另外，如果reduce阶段有combiner操作，那么它会把网络中拉到的数据保存在一个<code>HashMap</code>中进行合并操作，如果数据量较大，很容易引发OOM操作。</p></li><li><p><strong>Sort Shuffle</strong> 1.1开始(sort shuffle也经历过优化升级,详细见参考文章1)</p><p><img src="D:\Note\big-data-interview\BigData-Interview\pictures\spark-shuffle-v3.png" alt=""></p><p>在map阶段(shuffle write)，会按照partition id以及key对记录进行排序，将所有partition的数据写在同一个文件中，该文件中的记录首先是按照partition id排序一个一个分区的顺序排列，每个partition内部是按照key进行排序存放，map task运行期间会顺序写每个partition的数据，并通过一个索引文件记录每个partition的大小和偏移量。这样一来，每个map task一次只开两个文件描述符，一个写数据，一个写索引，大大减轻了Hash Shuffle大量文件描述符的问题，即使一个executor有K个core，那么最多一次性开K*2个文件描述符。</p><p>在reduce阶段(shuffle read)，reduce task拉取数据做combine时不再是采用<code>HashMap</code>，而是采用<code>ExternalAppendOnlyMap</code>，该数据结构在做combine时，如果内存不足，会刷写磁盘，很大程度的保证了鲁棒性，避免大数据情况下的OOM。</p></li><li><p><strong>Unsafe Shuffle</strong> 1.5开始, 1.6与Sort shuffle合并</p><p>从spark 1.5.0开始，spark开始了钨丝计划(Tungsten)，目的是优化内存和CPU的使用，进一步提升spark的性能。为此，引入Unsafe Shuffle，它的做法是将数据记录用二进制的方式存储，直接在序列化的二进制数据上sort而不是在java 对象上，这样一方面可以减少memory的使用和GC的开销，另一方面避免shuffle过程中频繁的序列化以及反序列化。在排序过程中，它提供cache-efficient sorter，使用一个8 bytes的指针，把排序转化成了一个指针数组的排序，极大的优化了排序性能.</p></li></ul><hr><p><strong>现在2.1 分为三种writer， 分为 BypassMergeSortShuffleWriter， SortShuffleWriter 和 UnsafeShuffleWriter</strong></p><h4 id="三种Writer的分类"><a href="#三种Writer的分类" class="headerlink" title="三种Writer的分类"></a>三种Writer的分类</h4><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/sparkShuffleWriter.jpg" alt=""></p><p>上面是使用哪种 writer 的判断依据， 是否开启 mapSideCombine 这个判断，是因为有些算子会在 map 端先进行一次 combine， 减少传输数据。 因为 BypassMergeSortShuffleWriter 会临时输出Reducer个（分区数目）小文件，所以分区数必须要小于一个阀值，默认是小于200</p><p>UnsafeShuffleWriter需要Serializer支持relocation，Serializer支持relocation：原始数据首先被序列化处理，并且再也不需要反序列，在其对应的元数据被排序后，需要Serializer支持relocation，在指定位置读取对应数据</p><p><a href="http://sharkdtu.com/posts/spark-shuffle.html" target="_blank" rel="noopener">参考文章1</a></p><p><a href="http://spark.coolplayer.net/?p=576" target="_blank" rel="noopener">参考文章2</a></p><h2 id="Spark的-partitioner-都有哪些"><a href="#Spark的-partitioner-都有哪些" class="headerlink" title="Spark的 partitioner 都有哪些?"></a>Spark的 partitioner 都有哪些?</h2><p><strong>Partitioner主要有两个实现类：HashPartitioner和RangePartitioner,HashPartitioner是大部分transformation的默认实现，sortBy、sortByKey使用RangePartitioner实现，也可以自定义Partitioner.</strong></p><ul><li><p><strong>HashPartitioner</strong></p><p>numPartitions方法返回传入的分区数，getPartition方法使用key的hashCode值对分区数取模得到PartitionId，写入到对应的bucket中。</p></li><li><p><strong>RangePartitioner</strong></p><p>RangePartitioner是先根据所有partition中数据的分布情况，尽可能均匀地构造出重分区的分隔符，再将数据的key值根据分隔符进行重新分区</p><ul><li>使用reservoir Sample方法对每个Partition进行分别抽样</li><li>对数据量大(大于sampleSizePerPartition)的分区进行重新抽样</li><li>由权重信息计算出分区分隔符rangeBounds</li><li>由rangeBounds计算分区数和key的所属分区</li></ul></li></ul><p><a href="https://blog.csdn.net/qq_34842671/article/details/83685179" target="_blank" rel="noopener">参考文章</a></p><h2 id="spark有哪几种join"><a href="#spark有哪几种join" class="headerlink" title="spark有哪几种join"></a>spark有哪几种join</h2><p><strong>Spark 中和 join 相关的算子有这几个</strong>：<code>join</code>、<code>fullOuterJoin</code>、<code>leftOuterJoin</code>、<code>rightOuterJoin</code></p><ul><li><p><strong>join</strong></p><p>join函数会输出两个RDD中key相同的所有项，并将它们的value联结起来，它联结的key要求在两个表中都存在，类似于SQL中的INNER JOIN。但它不满足交换律，a.join(b)与b.join(a)的结果不完全相同，值插入的顺序与调用关系有关。</p></li><li><p><strong>leftOuterJoin</strong></p><p>leftOuterJoin会保留对象的所有key，而用None填充在参数RDD other中缺失的值，因此调用顺序会使结果完全不同。如下面展示的结果，</p></li><li><p><strong>rightOuterJoin</strong></p><p>rightOuterJoin与leftOuterJoin基本一致，区别在于它的结果保留的是参数other这个RDD中所有的key。</p></li><li><p><strong>fullOuterJoin</strong></p><p>fullOuterJoin会保留两个RDD中所有的key，因此所有的值列都有可能出现缺失的情况，所有的值列都会转为Some对象。</p></li></ul><p><a href="http://www.neilron.xyz/join-in-spark/" target="_blank" rel="noopener">参考文章</a></p><h2 id="RDD有哪些特点"><a href="#RDD有哪些特点" class="headerlink" title="RDD有哪些特点"></a>RDD有哪些特点</h2><ol><li><p><strong>A list of partitions</strong><br>RDD是一个由多个partition（某个节点里的某一片连续的数据）组成的的list；将数据加载为RDD时，一般会遵循数据的本地性（一般一个hdfs里的block会加载为一个partition）。</p></li><li><p><strong>A function for computing each split</strong><br>RDD的每个partition上面都会有function，也就是函数应用，其作用是实现RDD之间partition的转换。</p></li><li><p><strong>A list of dependencies on other RDDs</strong><br>RDD会记录它的依赖 ，为了容错（重算，cache，checkpoint），也就是说在内存中的RDD操作时出错或丢失会进行重算。</p></li><li><p><strong>Optionally,a Partitioner for Key-value RDDs</strong><br>  可选项，如果RDD里面存的数据是key-value形式，则可以传递一个自定义的Partitioner进行重新分区，例如这里自定义的Partitioner是基于key进行分区，那则会将不同RDD里面的相同key的数据放到同一个partition里面</p></li><li><p><strong>Optionally, a list of preferred locations to compute each split on</strong></p><p>最优的位置去计算，也就是数据的本地性。</p></li></ol><h2 id="讲一下宽依赖和窄依赖"><a href="#讲一下宽依赖和窄依赖" class="headerlink" title="讲一下宽依赖和窄依赖"></a>讲一下宽依赖和窄依赖</h2><p>区别宽窄依赖的核心点是 <strong>子RDD的partition与父RDD的partition是否是1对多的关系</strong>,如果是这样的关系的话,</p><p>说明多个父rdd的partition需要经过shuffle过程汇总到一个子rdd的partition,这样就是一次宽依赖,在DAGScheduler中会产生stage的切分.</p><h2 id="Spark中的算子都有哪些"><a href="#Spark中的算子都有哪些" class="headerlink" title="Spark中的算子都有哪些"></a>Spark中的算子都有哪些</h2><p>总的来说,spark分为两大类算子:</p><ul><li><p><strong>Transformation 变换/转换算子：这种变换并不触发提交作业，完成作业中间过程处理</strong></p><p>Transformation 操作是延迟计算的，也就是说从一个RDD 转换生成另一个 RDD 的转换操作不是马上执行，需要等到有 Action 操作的时候才会真正触发运算</p></li><li><p><strong>Action 行动算子：这类算子会触发 SparkContext 提交 Job 作业</strong></p><p>Action 算子会触发 Spark 提交作业（Job），并将数据输出 Spark系统</p><hr></li></ul><h4 id="1-Value数据类型的Transformation算子"><a href="#1-Value数据类型的Transformation算子" class="headerlink" title="1. Value数据类型的Transformation算子"></a>1. Value数据类型的Transformation算子</h4><ul><li><p>输入分区与输出分区一对一型</p><ul><li>map算子</li><li>flatMap算子</li><li>mapPartitions算子</li><li>glom算子</li></ul></li><li><p>输入分区与输出分区多对一型</p><ul><li>union算子</li><li>cartesian算子</li></ul></li><li><p>输入分区与输出分区多对多型</p><ul><li>grouBy算子</li></ul></li><li><p>输出分区为输入分区子集型</p><ul><li>filter算子</li><li>distinct算子</li><li>subtract算子</li><li>sample算子</li><li>takeSample算子</li></ul></li><li><p>Cache型</p><ul><li>cache算子</li><li>persist算子</li></ul></li></ul><h4 id="2-Key-Value数据类型的Transfromation算子"><a href="#2-Key-Value数据类型的Transfromation算子" class="headerlink" title="2. Key-Value数据类型的Transfromation算子"></a>2. Key-Value数据类型的Transfromation算子</h4><ul><li><p>输入分区与输出分区一对一</p><ul><li>mapValues算子</li></ul></li><li><p>对单个RDD或两个RDD聚集</p><ul><li>combineByKey算子</li><li>reduceByKey算子</li><li>partitionBy算子</li><li>Cogroup算子</li></ul></li><li><p>连接</p><ul><li>join算子</li><li>leftOutJoin 和 rightOutJoin算子</li></ul></li></ul><h4 id="3-Action算子"><a href="#3-Action算子" class="headerlink" title="3. Action算子"></a>3. Action算子</h4><ul><li><p>无输出</p><ul><li>foreach算子</li></ul></li><li><p>HDFS算子</p><ul><li>saveAsTextFile算子</li><li>saveAsObjectFile算子</li></ul></li><li><p>Scala集合和数据类型</p><ul><li>collect算子</li><li>collectAsMap算子</li><li>reduceByKeyLocally算子</li><li>lookup算子</li><li>count算子</li><li>top算子</li><li>reduce算子</li><li>fold算子</li><li>aggregate算子</li><li>countByValue</li><li>countByKey</li></ul></li></ul><p><a href="https://www.cnblogs.com/kpsmile/p/10434390.html" target="_blank" rel="noopener">参考文章</a></p><h2 id="RDD的缓存级别都有哪些"><a href="#RDD的缓存级别都有哪些" class="headerlink" title="RDD的缓存级别都有哪些"></a>RDD的缓存级别都有哪些</h2><p>NONE :什么类型都不是<br>DISK_ONLY：磁盘<br>DISK_ONLY_2：磁盘；双副本<br>MEMORY_ONLY： 内存；反序列化；把RDD作为反序列化的方式存储，假如RDD的内容存不下，剩余的分区在以后需要时会重新计算，不会刷到磁盘上。<br>MEMORY_ONLY_2：内存；反序列化；双副本<br>MEMORY_ONLY_SER：内存；序列化；这种序列化方式，每一个partition以字节数据存储，好处是能带来更好的空间存储，但CPU耗费高<br>MEMORY_ONLY_SER_2 : 内存；序列化；双副本<br>MEMORY_AND_DISK：内存 + 磁盘；反序列化；双副本；RDD以反序列化的方式存内存，假如RDD的内容存不下，剩余的会存到磁盘<br>MEMORY_AND_DISK_2 : 内存 + 磁盘；反序列化；双副本<br>MEMORY_AND_DISK_SER：内存 + 磁盘；序列化<br>MEMORY_AND_DISK_SER_2：内存 + 磁盘；序列化；双副本</p><h2 id="RDD懒加载是什么意思"><a href="#RDD懒加载是什么意思" class="headerlink" title="RDD懒加载是什么意思"></a>RDD懒加载是什么意思</h2><p>Transformation 操作是延迟计算的，也就是说从一个RDD 转换生成另一个 RDD 的转换操作不是马上执行，需要等到有 Acion 操作的时候才会真正触发运算,这也就是懒加载.</p><h2 id="讲一下spark的几种部署方式"><a href="#讲一下spark的几种部署方式" class="headerlink" title="讲一下spark的几种部署方式"></a>讲一下spark的几种部署方式</h2><p><strong>目前,除了local模式为本地调试模式以为, Spark支持三种分布式部署方式，分别是standalone、spark on mesos和 spark on YARN</strong></p><ul><li><p><strong>Standalone模式</strong></p><p>即独立模式，自带完整的服务，可单独部署到一个集群中，无需依赖任何其他资源管理系统。从一定程度上说，该模式是其他两种的基础。目前Spark在standalone模式下是没有任何单点故障问题的，这是借助zookeeper实现的，思想类似于Hbase master单点故障解决方案。将Spark standalone与MapReduce比较，会发现它们两个在架构上是完全一致的： </p><ul><li>都是由master/slaves服务组成的，且起初master均存在单点故障，后来均通过zookeeper解决（Apache MRv1的JobTracker仍存在单点问题，但CDH版本得到了解决）； </li><li>各个节点上的资源被抽象成粗粒度的slot，有多少slot就能同时运行多少task。不同的是，MapReduce将slot分为map slot和reduce slot，它们分别只能供Map Task和Reduce Task使用，而不能共享，这是MapReduce资源利率低效的原因之一，而Spark则更优化一些，它不区分slot类型，只有一种slot，可以供各种类型的Task使用，这种方式可以提高资源利用率，但是不够灵活，不能为不同类型的Task定制slot资源。总之，这两种方式各有优缺点。 </li></ul></li><li><p><strong>Spark On YARN模式</strong></p><p><strong>spark on yarn 的支持两种模式：</strong> </p><ul><li>yarn-cluster：适用于生产环境； </li><li>yarn-client：适用于交互、调试，希望立即看到app的输出 </li></ul><p>yarn-cluster和yarn-client的区别在于yarn appMaster，每个yarn app实例有一个appMaster进程，是为app启动的第一个container；负责从ResourceManager请求资源，获取到资源后，告诉NodeManager为其启动container。yarn-cluster和yarn-client模式内部实现还是有很大的区别。如果你需要用于生产环境，那么请选择yarn-cluster；而如果你仅仅是Debug程序，可以选择yarn-client。</p></li><li><p><strong>Spark On Mesos模式</strong></p><p>Spark运行在Mesos上会比运行在YARN上更加灵活，更加自然。目前在Spark On Mesos环境中，用户可选择两种调度模式之一运行自己的应用程序</p><ul><li><p>粗粒度模式（Coarse-grained Mode）：每个应用程序的运行环境由一个Dirver和若干个Executor组成，其中，每个Executor占用若干资源，内部可运行多个Task（对应多少个“slot”）。应用程序的各个任务正式运行之前，需要将运行环境中的资源全部申请好，且运行过程中要一直占用这些资源，即使不用，最后程序运行结束后，回收这些资源。</p></li><li><p>细粒度模式（Fine-grained Mode）：鉴于粗粒度模式会造成大量资源浪费，Spark On Mesos还提供了另外一种调度模式：细粒度模式，这种模式类似于现在的云计算，思想是按需分配。与粗粒度模式一样，应用程序启动时，先会启动executor，但每个executor占用资源仅仅是自己运行所需的资源，不需要考虑将来要运行的任务，之后，mesos会为每个executor动态分配资源，每分配一些，便可以运行一个新任务，单个Task运行完之后可以马上释放对应的资源。</p></li></ul><h2 id="spark-on-yarn-模式下的-cluster模式和-client模式有什么区别"><a href="#spark-on-yarn-模式下的-cluster模式和-client模式有什么区别" class="headerlink" title="spark on yarn 模式下的 cluster模式和 client模式有什么区别"></a>spark on yarn 模式下的 cluster模式和 client模式有什么区别</h2></li></ul><ol><li>yarn-cluster 适用于生产环境。而 yarn-client 适用于交互和调试，也就是希望快速地看到 application 的输出.</li><li>yarn-cluster 和 yarn-client 模式的区别其实就是 <strong>Application Master 进程</strong>的区别，yarn-cluster 模式下，driver 运行在 AM(Application Master)中，它负责向 YARN 申请资源，并监督作业的运行状况。当用户提交了作业之后，就可以关掉 Client，作业会继续在 YARN 上运行。然而 yarn-cluster 模式不适合运行交互类型的作业。而 yarn-client 模式下，Application Master 仅仅向 YARN 请求 executor，Client 会和请求的container 通信来调度他们工作，也就是说 Client 不能离开。</li></ol><h2 id="spark运行原理-从提交一个jar到最后返回结果-整个过程"><a href="#spark运行原理-从提交一个jar到最后返回结果-整个过程" class="headerlink" title="spark运行原理,从提交一个jar到最后返回结果,整个过程"></a>spark运行原理,从提交一个jar到最后返回结果,整个过程</h2><ol><li><code>spark-submit</code> 提交代码，执行 <code>new SparkContext()</code>，在 SparkContext 里构造 <code>DAGScheduler</code> 和 <code>TaskScheduler</code>。</li><li>TaskScheduler 会通过后台的一个进程，连接 Master，向 Master 注册 Application。</li><li>Master 接收到 Application 请求后，会使用相应的资源调度算法，在 Worker 上为这个 Application 启动多个 Executer。</li><li>Executor 启动后，会自己反向注册到 TaskScheduler 中。 所有 Executor 都注册到 Driver 上之后，SparkContext 结束初始化，接下来往下执行我们自己的代码。</li><li>每执行到一个 Action，就会创建一个 Job。Job 会提交给 DAGScheduler。</li><li>DAGScheduler 会将 Job划分为多个 stage，然后每个 stage 创建一个 TaskSet。</li><li>TaskScheduler 会把每一个 TaskSet 里的 Task，提交到 Executor 上执行。</li><li>Executor 上有线程池，每接收到一个 Task，就用 TaskRunner 封装，然后从线程池里取出一个线程执行这个 task。(TaskRunner 将我们编写的代码，拷贝，反序列化，执行 Task，每个 Task 执行 RDD 里的一个 partition)</li></ol><h2 id="spark的stage是如何划分的"><a href="#spark的stage是如何划分的" class="headerlink" title="spark的stage是如何划分的"></a>spark的stage是如何划分的</h2><p><strong>stage的划分依据就是看是否产生了shuflle(即宽依赖),遇到一个shuffle操作就划分为前后两个stage.</strong></p><p><img src="D:\Note\big-data-interview\BigData-Interview\pictures\stageDivide.jpg" alt=""></p><h2 id="spark2-0为什么放弃了akka-而用netty"><a href="#spark2-0为什么放弃了akka-而用netty" class="headerlink" title="spark2.0为什么放弃了akka 而用netty"></a>spark2.0为什么放弃了akka 而用netty</h2><ol><li>很多Spark用户也使用Akka，但是由于Akka不同版本之间无法互相通信，这就要求用户必须使用跟Spark完全一样的Akka版本，导致用户无法升级Akka。</li><li>Spark的Akka配置是针对Spark自身来调优的，可能跟用户自己代码中的Akka配置冲突。</li><li>Spark用的Akka特性很少，这部分特性很容易自己实现。同时，这部分代码量相比Akka来说少很多，debug比较容易。如果遇到什么bug，也可以自己马上fix，不需要等Akka上游发布新版本。而且，Spark升级Akka本身又因为第一点会强制要求用户升级他们使用的Akka，对于某些用户来说是不现实的。</li></ol><p><a href="https://www.zhihu.com/question/61638635" target="_blank" rel="noopener">参考文章</a></p><h2 id="spark的各种HA-master-worker-executor的ha"><a href="#spark的各种HA-master-worker-executor的ha" class="headerlink" title="spark的各种HA,  master/worker/executor的ha"></a>spark的各种HA,  master/worker/executor的ha</h2><ul><li><h4 id="Master异常"><a href="#Master异常" class="headerlink" title="Master异常"></a>Master异常</h4><p>spark可以在集群运行时启动一个或多个standby Master,当 Master 出现异常时,会根据规则启动某个standby master接管,在standlone模式下有如下几种配置</p><ul><li><p>ZOOKEEPER</p><p>集群数据持久化到zk中,当master出现异常时,zk通过选举机制选出新的master,新的master接管是需要从zk获取持久化信息</p></li><li><p>FILESYSTEM</p><p>集群元数据信息持久化到本地文件系统, 当master出现异常时,只需要在该机器上重新启动master,启动后新的master获取持久化信息并根据这些信息恢复集群状态</p></li><li><p>CUSTOM</p><p>自定义恢复方式,对 standloneRecoveryModeFactory 抽象类 进行实现并把该类配置到系统中,当master出现异常时,会根据用户自定义行为恢复集群</p></li><li><p>None</p><p>不持久化集群的元数据, 当 master出现异常时, 新启动的Master 不进行恢复集群状态,而是直接接管集群</p></li></ul></li><li><h4 id="Worker异常"><a href="#Worker异常" class="headerlink" title="Worker异常"></a>Worker异常</h4><p>Worker 以定时发送心跳给 Master, 让 Master 知道 Worker 的实时状态,当worker出现超时时,Master 调用 timeOutDeadWorker 方法进行处理,在处理时根据 Worker 运行的是 Executor 和 Driver 分别进行处理</p><ul><li>如果是Executor, Master先把该 Worker 上运行的Executor 发送信息ExecutorUpdate给对应的Driver,告知Executor已经丢失,同时把这些Executor从其应用程序列表删除, 另外, 相关Executor的异常也需要处理</li><li>如果是Driver, 则判断是否设置重新启动,如果需要,则调用Master.shedule方法进行调度,分配合适节点重启Driver, 如果不需要重启, 则删除该应用程序</li></ul></li><li><h4 id="Executor异常"><a href="#Executor异常" class="headerlink" title="Executor异常"></a>Executor异常</h4><ol><li>Executor发生异常时由ExecutorRunner捕获该异常并发送ExecutorStateChanged信息给Worker</li><li>Worker接收到消息时, 在Worker的 handleExecutorStateChanged 方法中, 根据Executor状态进行信息更新,同时把Executor状态发送给Master</li><li>Master在接受Executor状态变化消息之后,如果发现其是异常退出,会尝试可用的Worker节点去启动Executor</li></ol></li></ul><h2 id="spark的内存管理机制"><a href="#spark的内存管理机制" class="headerlink" title="spark的内存管理机制"></a>spark的内存管理机制</h2><p><strong>spark的内存结构分为3大块:storage/execution/系统自留</strong></p><ul><li><p><strong>storage 内存</strong>：用于缓存 RDD、展开 partition、存放 Direct Task Result、存放广播变量。在 Spark Streaming receiver 模式中，也用来存放每个 batch 的 blocks</p></li><li><p><strong>execution 内存</strong>：用于 shuffle、join、sort、aggregation 中的缓存、buffer</p></li><li><p><strong>系统自留</strong>:</p><ul><li><p>在 spark 运行过程中使用：比如序列化及反序列化使用的内存，各个对象、元数据、临时变量使用的内存，函数调用使用的堆栈等</p></li><li><p>作为误差缓冲：由于 storage 和 execution 中有很多内存的使用是估算的，存在误差。当 storage 或 execution 内存使用超出其最大限制时，有这样一个安全的误差缓冲在可以大大减小 OOM 的概率</p></li></ul></li></ul><hr><h4 id="1-6版本以前的问题"><a href="#1-6版本以前的问题" class="headerlink" title="1.6版本以前的问题"></a>1.6版本以前的问题</h4><ul><li>旧方案最大的问题是 storage 和 execution 的内存大小都是固定的，不可改变，即使 execution 有大量的空闲内存且 storage 内存不足，storage 也无法使用 execution 的内存，只能进行 spill，反之亦然。所以，在很多情况下存在资源浪费</li><li>旧方案中，只有 execution 内存支持 off heap，storage 内存不支持 off heap</li></ul><h4 id="新方案的改进"><a href="#新方案的改进" class="headerlink" title="新方案的改进"></a>新方案的改进</h4><ul><li>新方案 storage 和 execution 内存可以互相借用，当一方内存不足可以向另一方借用内存，提高了整体的资源利用率</li><li>新方案中 execution 内存和 storage 内存均支持 off heap</li></ul><h2 id="spark中的广播变量"><a href="#spark中的广播变量" class="headerlink" title="spark中的广播变量"></a>spark中的广播变量</h2><p><a href="https://www.jianshu.com/p/6ef7f0a44fbf" target="_blank" rel="noopener">图片来源</a> /<a href="https://github.com/JerryLead/SparkInternals/blob/master/markdown/7-Broadcast.md" target="_blank" rel="noopener">文字来源</a></p><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/spark中的广播变量.png" alt=""></p><p><strong>顾名思义，broadcast 就是将数据从一个节点发送到其他各个节点上去。这样的场景很多，比如 driver 上有一张表，其他节点上运行的 task 需要 lookup 这张表，那么 driver 可以先把这张表 copy 到这些节点，这样 task 就可以在本地查表了。如何实现一个可靠高效的 broadcast 机制是一个有挑战性的问题。先看看 Spark 官网上的一段话：</strong></p><p>Broadcast variables allow the programmer to keep a <strong>read-only</strong> variable cached on each <strong>machine</strong> rather than shipping a copy of it with <strong>tasks</strong>. They can be used, for example, to give every node a copy of a <strong>large input dataset</strong> in an efficient manner. Spark also attempts to distribute broadcast variables using <strong>efficient</strong> broadcast algorithms to reduce communication cost.</p><h3 id="问题：为什么只能-broadcast-只读的变量？"><a href="#问题：为什么只能-broadcast-只读的变量？" class="headerlink" title="问题：为什么只能 broadcast 只读的变量？"></a>问题：为什么只能 broadcast 只读的变量？</h3><p>这就涉及一致性的问题，如果变量可以被更新，那么一旦变量被某个节点更新，其他节点要不要一块更新？如果多个节点同时在更新，更新顺序是什么？怎么做同步？还会涉及 fault-tolerance 的问题。为了避免维护数据一致性问题，Spark 目前只支持 broadcast 只读变量。</p><h3 id="问题：broadcast-到节点而不是-broadcast-到每个-task？"><a href="#问题：broadcast-到节点而不是-broadcast-到每个-task？" class="headerlink" title="问题：broadcast 到节点而不是 broadcast 到每个 task？"></a>问题：broadcast 到节点而不是 broadcast 到每个 task？</h3><p>因为每个 task 是一个线程，而且同在一个进程运行 tasks 都属于同一个 application。因此每个节点（executor）上放一份就可以被所有 task 共享。</p><h3 id="问题：-具体怎么用-broadcast？"><a href="#问题：-具体怎么用-broadcast？" class="headerlink" title="问题： 具体怎么用 broadcast？"></a>问题： 具体怎么用 broadcast？</h3><p>driver program 例子：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">val data = List(1, 2, 3, 4, 5, 6)</span><br><span class="line">val bdata = sc.broadcast(data)</span><br><span class="line"></span><br><span class="line">val rdd = sc.parallelize(1 to 6, 2)</span><br><span class="line">val observedSizes = rdd.map(_ =&gt; bdata.value.size)</span><br></pre></td></tr></table></figure><p>driver 使用 <code>sc.broadcast()</code> 声明要 broadcast 的 data，bdata 的类型是 Broadcast。</p><p>当 <code>rdd.transformation(func)</code> 需要用 bdata 时，直接在 func 中调用，比如上面的例子中的 map() 就使用了 bdata.value.size。</p><h3 id="问题：怎么实现-broadcast？"><a href="#问题：怎么实现-broadcast？" class="headerlink" title="问题：怎么实现 broadcast？"></a>问题：怎么实现 broadcast？</h3><p>broadcast 的实现机制很有意思：</p><h4 id="1-分发-task-的时候先分发-bdata-的元信息"><a href="#1-分发-task-的时候先分发-bdata-的元信息" class="headerlink" title="1. 分发 task 的时候先分发 bdata 的元信息"></a>1. 分发 task 的时候先分发 bdata 的元信息</h4><p>Driver 先建一个本地文件夹用以存放需要 broadcast 的 data，并启动一个可以访问该文件夹的 HttpServer。当调用<code>val bdata = sc.broadcast(data)</code>时就把 data 写入文件夹，同时写入 driver 自己的 blockManger 中（StorageLevel 为内存＋磁盘），获得一个 blockId，类型为 BroadcastBlockId。当调用<code>rdd.transformation(func)</code>时，如果 func 用到了 bdata，那么 driver submitTask() 的时候会将 bdata 一同 func 进行序列化得到 serialized task，<strong>注意序列化的时候不会序列化 bdata 中包含的 data。</strong>上一章讲到 serialized task 从 driverActor 传递到 executor 时使用 Akka 的传消息机制，消息不能太大，而实际的 data 可能很大，所以这时候还不能 broadcast data。</p><blockquote><p>driver 为什么会同时将 data 放到磁盘和 blockManager 里面？放到磁盘是为了让 HttpServer 访问到，放到 blockManager 是为了让 driver program 自身使用 bdata 时方便（其实我觉得不放到 blockManger 里面也行）。</p></blockquote><p><strong>那么什么时候传送真正的 data？</strong>在 executor 反序列化 task 的时候，会同时反序列化 task 中的 bdata 对象，这时候会调用 bdata 的 readObject() 方法。该方法先去本地 blockManager 那里询问 bdata 的 data 在不在 blockManager 里面，如果不在就使用下面的两种 fetch 方式之一去将 data fetch 过来。得到 data 后，将其存放到 blockManager 里面，这样后面运行的 task 如果需要 bdata 就不需要再去 fetch data 了。如果在，就直接拿来用了。</p><p>下面探讨 broadcast data 时候的两种实现方式：</p><h4 id="2-HttpBroadcast"><a href="#2-HttpBroadcast" class="headerlink" title="2. HttpBroadcast"></a>2. HttpBroadcast</h4><p>顾名思义，HttpBroadcast 就是每个 executor 通过的 http 协议连接 driver 并从 driver 那里 fetch data。</p><p>Driver 先准备好要 broadcast 的 data，调用<code>sc.broadcast(data)</code>后会调用工厂方法建立一个 HttpBroadcast 对象。该对象做的第一件事就是将 data 存到 driver 的 blockManager 里面，StorageLevel 为内存＋磁盘，blockId 类型为 BroadcastBlockId。</p><p>同时 driver 也会将 broadcast 的 data 写到本地磁盘，例如写入后得到 <code>/var/folders/87/grpn1_fn4xq5wdqmxk31v0l00000gp/T/spark-6233b09c-3c72-4a4d-832b-6c0791d0eb9c/broadcast_0</code>， 这个文件夹作为 HttpServer 的文件目录。</p><blockquote><p>Driver 和 executor 启动的时候，都会生成 broadcastManager 对象，调用 HttpBroadcast.initialize()，driver 会在本地建立一个临时目录用来存放 broadcast 的 data，并启动可以访问该目录的 httpServer。</p></blockquote><p><strong>Fetch data：</strong>在 executor 反序列化 task 的时候，会同时反序列化 task 中的 bdata 对象，这时候会调用 bdata 的 readObject() 方法。该方法先去本地 blockManager 那里询问 bdata 的 data 在不在 blockManager 里面，<strong>如果不在就使用 http 协议连接 driver 上的 httpServer，将 data fetch 过来。</strong>得到 data 后，将其存放到 blockManager 里面，这样后面运行的 task 如果需要 bdata 就不需要再去 fetch data 了。如果在，就直接拿来用了。</p><p>HttpBroadcast 最大的问题就是 <strong>driver 所在的节点可能会出现网络拥堵</strong>，因为 worker 上的 executor 都会去 driver 那里 fetch 数据。</p><h4 id="3-TorrentBroadcast"><a href="#3-TorrentBroadcast" class="headerlink" title="3. TorrentBroadcast"></a>3. TorrentBroadcast</h4><p>为了解决 HttpBroadast 中 driver 单点网络瓶颈的问题，Spark 又设计了一种 broadcast 的方法称为 TorrentBroadcast，<strong>这个类似于大家常用的 BitTorrent 技术。</strong>基本思想就是将 data 分块成 data blocks，然后假设有 executor fetch 到了一些 data blocks，那么这个 executor 就可以被当作 data server 了，随着 fetch 的 executor 越来越多，有更多的 data server 加入，data 就很快能传播到全部的 executor 那里去了。</p><p>HttpBroadcast 是通过传统的 http 协议和 httpServer 去传 data，在 TorrentBroadcast 里面使用在上一章介绍的 blockManager.getRemote() =&gt; NIO ConnectionManager 传数据的方法来传递，读取数据的过程与读取 cached rdd 的方式类似，可以参阅 <a href="https://github.com/JerryLead/SparkInternals/blob/master/markdown/6-CacheAndCheckpoint.md" target="_blank" rel="noopener">CacheAndCheckpoint</a> 中的最后一张图。</p><p>下面讨论 TorrentBroadcast 的一些细节：</p><p><a href="https://github.com/JerryLead/SparkInternals/blob/master/markdown/PNGfigures/TorrentBroadcast.png" target="_blank" rel="noopener"><img src="https://github.com/JerryLead/SparkInternals/raw/master/markdown/PNGfigures/TorrentBroadcast.png" alt="TorrentBroadcast"></a></p><h4 id="driver-端："><a href="#driver-端：" class="headerlink" title="driver 端："></a>driver 端：</h4><p>Driver 先把 data 序列化到 byteArray，然后切割成 BLOCK_SIZE（由 <code>spark.broadcast.blockSize = 4MB</code> 设置）大小的 data block，每个 data block 被 TorrentBlock 对象持有。切割完 byteArray 后，会将其回收，因此内存消耗虽然可以达到 2 * Size(data)，但这是暂时的。</p><p>完成分块切割后，就将分块信息（称为 meta 信息）存放到 driver 自己的 blockManager 里面，StorageLevel 为内存＋磁盘，同时会通知 driver 自己的 blockManagerMaster 说 meta 信息已经存放好。<strong>通知 blockManagerMaster 这一步很重要，因为 blockManagerMaster 可以被 driver 和所有 executor 访问到，信息被存放到 blockManagerMaster 就变成了全局信息。</strong></p><p>之后将每个分块 data block 存放到 driver 的 blockManager 里面，StorageLevel 为内存＋磁盘。存放后仍然通知 blockManagerMaster 说 blocks 已经存放好。到这一步，driver 的任务已经完成。</p><h4 id="Executor-端："><a href="#Executor-端：" class="headerlink" title="Executor 端："></a>Executor 端：</h4><p>executor 收到 serialized task 后，先反序列化 task，这时候会反序列化 serialized task 中包含的 bdata 类型是 TorrentBroadcast，也就是去调用 TorrentBroadcast.readObject()。这个方法首先得到 bdata 对象，<strong>然后发现 bdata 里面没有包含实际的 data。怎么办？</strong>先询问所在的 executor 里的 blockManager 是会否包含 data（通过查询 data 的 broadcastId），包含就直接从本地 blockManager 读取 data。否则，就通过本地 blockManager 去连接 driver 的 blockManagerMaster 获取 data 分块的 meta 信息，获取信息后，就开始了 BT 过程。</p><p><strong>BT 过程：</strong>task 先在本地开一个数组用于存放将要 fetch 过来的 data blocks <code>arrayOfBlocks = new Array[TorrentBlock](totalBlocks)</code>，TorrentBlock 是对 data block 的包装。然后打乱要 fetch 的 data blocks 的顺序，比如如果 data block 共有 5 个，那么打乱后的 fetch 顺序可能是 3-1-2-4-5。然后按照打乱后的顺序去 fetch 一个个 data block。fetch 的过程就是通过 “本地 blockManager －本地 connectionManager－driver/executor 的 connectionManager－driver/executor 的 blockManager－data” 得到 data，这个过程与 fetch cached rdd 类似。<strong>每 fetch 到一个 block 就将其存放到 executor 的 blockManager 里面，同时通知 driver 上的 blockManagerMaster 说该 data block 多了一个存储地址。</strong>这一步通知非常重要，意味着 blockManagerMaster 知道 data block 现在在 cluster 中有多份，下一个不同节点上的 task 再去 fetch 这个 data block 的时候，可以有两个选择了，而且会随机选择一个去 fetch。这个过程持续下去就是 BT 协议，随着下载的客户端越来越多，data block 服务器也越来越多，就变成 p2p下载了。关于 BT 协议，Wikipedia 上有一个<a href="http://zh.wikipedia.org/wiki/BitTorrent_(%E5%8D%8F%E8%AE%AE" target="_blank" rel="noopener">动画</a>)。</p><p>整个 fetch 过程结束后，task 会开一个大 Array[Byte]，大小为 data 的总大小，然后将 data block 都 copy 到这个 Array，然后对 Array 中 bytes 进行反序列化得到原始的 data，这个过程就是 driver 序列化 data 的反过程。</p><p>最后将 data 存放到 task 所在 executor 的 blockManager 里面，StorageLevel 为内存＋磁盘。显然，这时候 data 在 blockManager 里存了两份，不过等全部 executor 都 fetch 结束，存储 data blocks 那份可以删掉了。</p><h3 id="问题：broadcast-RDD-会怎样"><a href="#问题：broadcast-RDD-会怎样" class="headerlink" title="问题：broadcast RDD 会怎样?"></a>问题：broadcast RDD 会怎样?</h3><p><a href="http://weibo.com/u/1410938285" target="_blank" rel="noopener">@Andrew-Xia</a> 回答道：不会怎样，就是这个rdd在每个executor中实例化一份。</p><h2 id="Discussion"><a href="#Discussion" class="headerlink" title="Discussion"></a>Discussion</h2><p>公共数据的 broadcast 是很实用的功能，在 Hadoop 中使用 DistributedCache，比如常用的<code>-libjars</code>就是使用 DistributedCache 来将 task 依赖的 jars 分发到每个 task 的工作目录。不过分发前 DistributedCache 要先将文件上传到 HDFS。这种方式的主要问题是<strong>资源浪费</strong>，如果某个节点上要运行来自同一 job 的 4 个 mapper，那么公共数据会在该节点上存在 4 份（每个 task 的工作目录会有一份）。但是通过 HDFS 进行 broadcast 的好处在于<strong>单点瓶颈不明显</strong>，因为公共 data 首先被分成多个 block，然后不同的 block 存放在不同的节点。这样，只要所有的 task 不是同时去同一个节点 fetch 同一个 block，网络拥塞不会很严重。</p><p>对于 Spark 来讲，broadcast 时考虑的不仅是如何将公共 data 分发下去的问题，还要考虑如何让同一节点上的 task 共享 data。</p><p>对于第一个问题，Spark 设计了两种 broadcast 的方式，传统存在单点瓶颈问题的 HttpBroadcast，和类似 BT 方式的 TorrentBroadcast。HttpBroadcast 使用传统的 client-server 形式的 HttpServer 来传递真正的 data，而 TorrentBroadcast 使用 blockManager 自带的 NIO 通信方式来传递 data。TorrentBroadcast 存在的问题是<strong>慢启动</strong>和<strong>占内存</strong>，慢启动指的是刚开始 data 只在 driver 上有，要等 executors fetch 很多轮 data block 后，data server 才会变得可观，后面的 fetch 速度才会变快。executor 所占内存的在 fetch 完 data blocks 后进行反序列化时需要将近两倍 data size 的内存消耗。不管哪一种方式，driver 在分块时会有两倍 data size 的内存消耗。</p><p>对于第二个问题，每个 executor 都包含一个 blockManager 用来管理存放在 executor 里的数据，将公共数据存放在 blockManager 中（StorageLevel 为内存＋磁盘），可以保证在 executor 执行的 tasks 能够共享 data。</p><p>其实 Spark 之前还尝试了一种称为 TreeBroadcast 的机制，详情可以见技术报告 <a href="http://www.cs.berkeley.edu/~agearh/cs267.sp10/files/mosharaf-spark-bc-report-spring10.pdf" target="_blank" rel="noopener">Performance and Scalability of Broadcast in Spark</a>。</p><p>更深入点，broadcast 可以用多播协议来做，不过多播使用 UDP，不是可靠的，仍然需要应用层的设计一些可靠性保障机制。</p><h2 id="什么是数据倾斜-怎样去处理数据倾斜"><a href="#什么是数据倾斜-怎样去处理数据倾斜" class="headerlink" title="什么是数据倾斜,怎样去处理数据倾斜"></a>什么是数据倾斜,怎样去处理数据倾斜</h2><p>数据倾斜是一种很常见的问题（依据二八定律），简单来说，比方WordCount中某个Key对应的数据量非常大的话，就会产生数据倾斜，导致两个后果：</p><ul><li>OOM（单或少数的节点）；</li><li>拖慢整个Job执行时间（其他已经完成的节点都在等这个还在做的节点）</li></ul><h4 id="数据倾斜主要分为两类-聚合倾斜-和-join倾斜"><a href="#数据倾斜主要分为两类-聚合倾斜-和-join倾斜" class="headerlink" title="数据倾斜主要分为两类: 聚合倾斜 和 join倾斜"></a>数据倾斜主要分为两类: 聚合倾斜 和 join倾斜</h4><ul><li><p><strong>聚合倾斜</strong></p><ul><li><p><strong>双重聚合（局部聚合+全局聚合）</strong></p><p><strong>场景</strong>: 对RDD进行reduceByKey等聚合类shuffle算子，SparkSQL的groupBy做分组聚合这两种情况<br> 思路：首先通过map给每个key打上n以内的随机数的前缀并进行局部聚合，即(hello, 1) (hello, 1) (hello, 1) (hello, 1)变为(1_hello, 1) (1_hello, 1) (2_hello, 1)，并进行reduceByKey的局部聚合，然后再次map将key的前缀随机数去掉再次进行全局聚合；<br> <strong>原理</strong>: 对原本相同的key进行随机数附加，变成不同key，让原本一个task处理的数据分摊到多个task做局部聚合，规避单task数据过量。之后再去随机前缀进行全局聚合；<br> 优点：效果非常好（对聚合类Shuffle操作的倾斜问题）；<br> 缺点：范围窄（仅适用于聚合类的Shuffle操作，join类的Shuffle还需其它方案）</p></li></ul></li><li><p><strong>join倾斜</strong></p><ul><li><p><strong>将reduce join转为map join</strong></p><p><strong>场景</strong>: 对RDD或Spark SQL使用join类操作或语句，且join操作的RDD或表比较小（百兆或1,2G）； 思路：使用broadcast和map类算子实现join的功能替代原本的join，彻底规避shuffle。对较小RDD直接collect到内存，并创建broadcast变量；并对另外一个RDD执行map类算子，在该算子的函数中，从broadcast变量（collect出的较小RDD）与当前RDD中的每条数据依次比对key，相同的key执行你需要方式的join；</p><p><strong>原理</strong>: 若RDD较小，可采用广播小的RDD，并对大的RDD进行map，来实现与join同样的效果。简而言之，用broadcast-map代替join，规避join带来的shuffle（无Shuffle无倾斜）； 优点：效果很好（对join操作导致的倾斜），根治； </p><p><strong>缺点</strong>：适用场景小（大表+小表），广播（driver和executor节点都会驻留小表数据）小表也耗内存</p></li><li><p><strong>采样倾斜key并分拆join操作</strong></p><p><strong>场景</strong>: 两个较大的（无法采用方案五）RDD/Hive表进行join时，且一个RDD/Hive表中少数key数据量过大，另一个RDD/Hive表的key分布较均匀（RDD中两者之一有一个更倾斜）；<br><strong>思路</strong>:</p><ol><li>对更倾斜rdd1进行采样（RDD.sample）并统计出数据量最大的几个key；</li><li>对这几个倾斜的key从原本rdd1中拆出形成一个单独的rdd1_1，并打上0~n的随机数前缀，被拆分的原rdd1的另一部分（不包含倾斜key）又形成一个新rdd1_2；</li><li>对rdd2过滤出rdd1倾斜的key，得到rdd2_1，并将其中每条数据扩n倍，对每条数据按顺序附加0~n的前缀，被拆分出key的rdd2也独立形成另一个rdd2_2； 【个人认为，这里扩了n倍，最后union完还需要将每个倾斜key对应的value减去(n-1)】</li><li>将加了随机前缀的rdd1_1和rdd2_1进行join（此时原本倾斜的key被打散n份并被分散到更多的task中进行join）； 【个人认为，这里应该做两次join，两次join中间有一个map去前缀】</li><li>另外两个普通的RDD（rdd1_2、rdd2_2）照常join；</li><li>最后将两次join的结果用union结合得到最终的join结果。 原理：对join导致的倾斜是因为某几个key，可将原本RDD中的倾斜key拆分出原RDD得到新RDD，并以加随机前缀的方式打散n份做join，将倾斜key对应的大量数据分摊到更多task上来规避倾斜；</li></ol><p><strong>优点</strong>: 前提是join导致的倾斜（某几个key倾斜），避免占用过多内存（只需对少数倾斜key扩容n倍）；<br><strong>缺点</strong>: 对过多倾斜key不适用。</p></li><li><p><strong>用随机前缀和扩容RDD进行join</strong></p><p><strong>场景</strong>: RDD中有大量key导致倾斜； 思路：与方案六类似。</p><ol><li>查看RDD/Hive表中数据分布并找到造成倾斜的RDD/表；</li><li>对倾斜RDD中的每条数据打上n以内的随机数前缀；</li><li>对另外一个正常RDD的每条数据扩容n倍，扩容出的每条数据依次打上0到n的前缀；</li><li>对处理后的两个RDD进行join。</li></ol><p><strong>原理</strong>: 与方案六只有唯一不同在于这里对不倾斜RDD中所有数据进行扩大n倍，而不是找出倾斜key进行扩容；<br><strong>优点</strong>: 对join类的数据倾斜都可处理，效果非常显著；<br><strong>缺点</strong>: 缓解，扩容需要大内存</p></li></ul></li></ul><p><a href="https://juejin.im/post/5ccd5cc7f265da03474e1249#heading-9" target="_blank" rel="noopener">参考文章1</a></p><p><a href="https://blog.csdn.net/qq_35394891/article/details/82260907" target="_blank" rel="noopener">参考文章2</a></p><h2 id="分析一下一段spark代码中哪些部分在Driver端执行-哪些部分在Worker端执行"><a href="#分析一下一段spark代码中哪些部分在Driver端执行-哪些部分在Worker端执行" class="headerlink" title="分析一下一段spark代码中哪些部分在Driver端执行,哪些部分在Worker端执行"></a>分析一下一段spark代码中哪些部分在Driver端执行,哪些部分在Worker端执行</h2><p>Driver Program是用户编写的提交给Spark集群执行的application，它包含两部分</p><ul><li><strong>作为驱动</strong>： Driver与Master、Worker协作完成application进程的启动、DAG划分、计算任务封装、计算任务分发到各个计算节点(Worker)、计算资源的分配等。</li><li><strong>计算逻辑本身</strong>，当计算任务在Worker执行时，执行计算逻辑完成application的计算任务</li></ul><p>一般来说transformation算子均是在worker上执行的,其他类型的代码在driver端执行</p>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="Spark" scheme="https://icocos.github.io/categories/Spark/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="面试" scheme="https://icocos.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
    
      <category term="Spark" scheme="https://icocos.github.io/tags/Spark/"/>
    
  </entry>
  
  <entry>
    <title>大数据面试之Hive</title>
    <link href="https://icocos.github.io/2020/03/02/%E5%A4%A7%E6%95%B0%E6%8D%AE%E9%9D%A2%E8%AF%95%E4%B9%8BHive/"/>
    <id>https://icocos.github.io/2020/03/02/大数据面试之Hive/</id>
    <published>2020-03-02T13:46:11.000Z</published>
    <updated>2020-04-09T14:45:08.845Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h2 id="hive-内部表和外部表的区别"><a href="#hive-内部表和外部表的区别" class="headerlink" title="hive 内部表和外部表的区别"></a>hive 内部表和外部表的区别</h2><ul><li>建表时带有external关键字为外部表，否则为内部表</li><li>内部表和外部表建表时都可以自己指定location</li><li>删除表时，外部表不会删除对应的数据，只会删除元数据信息，内部表则会删除</li><li>其他用法是一样的</li></ul><h2 id="hive四种排序方式的区别"><a href="#hive四种排序方式的区别" class="headerlink" title="hive四种排序方式的区别"></a>hive四种排序方式的区别</h2><ul><li><p><strong>order by</strong> </p><pre><code>order by 是要对输出的结果进行全局排序，这就意味着**只有一个reducer**才能实现（多个reducer无法保证全局有序）但是当数据量过大的时候，效率就很低。如果在严格模式下（hive.mapred.mode=strict）,则必须配合limit使用</code></pre></li><li><p><strong>sort by</strong></p><pre><code>sort by 不是全局排序，只是在进入到reducer之前完成排序，只保证了每个reducer中数据按照指定字段的有序性，是局部排序。配置mapred.reduce.tasks=[nums]可以对输出的数据执行归并排序。可以配合limit使用，提高性能</code></pre></li><li><p><strong>distribute by</strong> </p><pre><code>distribute by 指的是按照指定的字段划分到不同的输出reduce文件中，和sort by一起使用时需要注意，</code></pre><p>distribute by必须放在前面</p></li><li><p><strong>cluster by</strong></p><p>cluster by 可以看做是一个特殊的distribute by+sort by，它具备二者的功能，但是只能实现倒序排序的方式,不能指定排序规则为asc 或者desc</p></li></ul><p><a href="https://blog.csdn.net/high2011/article/details/78012317" target="_blank" rel="noopener">参考文章</a></p><h2 id="hive的metastore的三种模式"><a href="#hive的metastore的三种模式" class="headerlink" title="hive的metastore的三种模式"></a>hive的metastore的三种模式</h2><ul><li><p><strong>内嵌Derby方式</strong></p><p>这个是Hive默认的启动模式，一般用于单元测试，这种存储方式有一个缺点：在同一时间只能有一个进程连接使用数据库。</p></li><li><p><strong>Local方式</strong></p><p>本地MySQL</p></li><li><p><strong>Remote方式</strong></p><p>远程MySQL,一般常用此种方式</p></li></ul><p><a href="https://blog.csdn.net/baolibin528/article/details/46710025" target="_blank" rel="noopener">参考文章</a></p><h2 id="hive中join都有哪些"><a href="#hive中join都有哪些" class="headerlink" title="hive中join都有哪些"></a>hive中join都有哪些</h2><p>Hive中除了支持和传统数据库中一样的内关联（JOIN）、左关联（LEFT JOIN）、右关联（RIGHT JOIN）、全关联（FULL JOIN），还支持左半关联（LEFT SEMI JOIN）</p><ul><li><p><strong>内关联（JOIN）</strong></p><p>只返回能关联上的结果。</p></li><li><p><strong>左外关联（LEFT [OUTER] JOIN）</strong></p><p>以LEFT [OUTER] JOIN关键字前面的表作为主表，和其他表进行关联，返回记录和主表的记录数一致，关联不上的字段置为NULL。</p></li><li><p><strong>右外关联（RIGHT [OUTER] JOIN）</strong></p><p>和左外关联相反，以RIGTH [OUTER] JOIN关键词后面的表作为主表，和前面的表做关联，返回记录数和主表一致，关联不上的字段为NULL。</p></li><li><p><strong>全外关联（FULL [OUTER] JOIN）</strong></p><p>以两个表的记录为基准，返回两个表的记录去重之和，关联不上的字段为NULL。</p></li><li><p><strong>LEFT SEMI JOIN</strong></p><p>以LEFT SEMI JOIN关键字前面的表为主表，返回主表的KEY也在副表中的记录</p></li><li><p><strong>笛卡尔积关联（CROSS JOIN）</strong></p><p>返回两个表的笛卡尔积结果，不需要指定关联键。</p></li></ul><p><a href="http://lxw1234.com/archives/2015/06/315.htm" target="_blank" rel="noopener">参考文章</a></p><h2 id="Impala-和-hive-的查询有哪些区别"><a href="#Impala-和-hive-的查询有哪些区别" class="headerlink" title="Impala 和 hive 的查询有哪些区别"></a>Impala 和 hive 的查询有哪些区别</h2><p><strong>Impala是基于Hive的大数据实时分析查询引擎</strong>，直接使用Hive的元数据库Metadata,意味着impala元数据都存储在Hive的metastore中。并且impala兼容Hive的sql解析，实现了Hive的SQL语义的子集，功能还在不断的完善中。</p><h4 id="Impala相对于Hive所使用的优化技术"><a href="#Impala相对于Hive所使用的优化技术" class="headerlink" title="Impala相对于Hive所使用的优化技术"></a>Impala相对于Hive所使用的优化技术</h4><ul><li>1、没有使用 MapReduce进行并行计算，虽然MapReduce是非常好的并行计算框架，但它更多的面向批处理模式，而不是面向交互式的SQL执行。与 MapReduce相比：Impala把整个查询分成一执行计划树，而不是一连串的MapReduce任务，在分发执行计划后，Impala使用拉式获取 数据的方式获取结果，把结果数据组成按执行树流式传递汇集，减少的了把中间结果写入磁盘的步骤，再从磁盘读取数据的开销。Impala使用服务的方式避免 每次执行查询都需要启动的开销，即相比Hive没了MapReduce启动时间。</li><li>2、使用LLVM产生运行代码，针对特定查询生成特定代码，同时使用Inline的方式减少函数调用的开销，加快执行效率。</li><li>3、充分利用可用的硬件指令（SSE4.2）。</li><li>4、更好的IO调度，Impala知道数据块所在的磁盘位置能够更好的利用多磁盘的优势，同时Impala支持直接数据块读取和本地代码计算checksum。</li><li>5、通过选择合适的数据存储格式可以得到最好的性能（Impala支持多种存储格式）。</li><li>6、最大使用内存，中间结果不写磁盘，及时通过网络以stream的方式传递。</li></ul><p><a href="https://cloud.tencent.com/developer/article/1175527" target="_blank" rel="noopener">参考文章</a></p><h2 id="Hive中大表join小表的优化方法"><a href="#Hive中大表join小表的优化方法" class="headerlink" title="Hive中大表join小表的优化方法"></a>Hive中大表join小表的优化方法</h2><p>在小表和大表进行join时，将<strong>小表放在前边</strong>，效率会高，hive会将小表进行缓存</p><h2 id="Hive-Sql-是怎样解析成MR-job的"><a href="#Hive-Sql-是怎样解析成MR-job的" class="headerlink" title="Hive Sql 是怎样解析成MR job的?"></a>Hive Sql 是怎样解析成MR job的?</h2><p><strong>主要分为6个阶段:</strong></p><ol><li><p><strong>Hive使用Antlr实现语法解析</strong>.根据Antlr制定的SQL语法解析规则,完成SQL语句的词法/语法解析,将SQL转为抽象语法树AST.</p></li><li><p><strong>遍历AST,生成基本查询单元QueryBlock</strong>.QueryBlock是一条SQL最基本的组成单元，包括三个部分：输入源，计算过程，输出.</p></li><li><strong>遍历QueryBlock,生成OperatorTree</strong>.Hive最终生成的MapReduce任务，Map阶段和Reduce阶段均由OperatorTree组成。Operator就是在Map阶段或者Reduce阶段完成单一特定的操作。QueryBlock生成Operator Tree就是遍历上一个过程中生成的QB和QBParseInfo对象的保存语法的属性.</li><li><strong>优化OperatorTree.</strong>大部分逻辑层优化器通过变换OperatorTree，合并操作符，达到减少MapReduce Job，减少shuffle数据量的目的</li><li><p><strong>OperatorTree生成MapReduce Job</strong>.遍历OperatorTree,翻译成MR任务.</p><ul><li>对输出表生成MoveTask</li><li>从OperatorTree的其中一个根节点向下深度优先遍历</li><li>ReduceSinkOperator标示Map/Reduce的界限，多个Job间的界限</li><li>遍历其他根节点，遇过碰到JoinOperator合并MapReduceTask</li><li>生成StatTask更新元数据</li><li>剪断Map与Reduce间的Operator的关系</li></ul></li><li><p><strong>优化任务.</strong> 使用物理优化器对MR任务进行优化,生成最终执行任务</p></li></ol><p><a href="https://www.cnblogs.com/Dhouse/p/7132476.html" target="_blank" rel="noopener">参考文章</a></p><h2 id="Hive-UDF简单介绍"><a href="#Hive-UDF简单介绍" class="headerlink" title="Hive UDF简单介绍"></a>Hive UDF简单介绍</h2><p>在Hive中，用户可以自定义一些函数，用于扩展HiveQL的功能，而这类函数叫做UDF（用户自定义函数）。UDF分为两大类：UDAF（用户自定义聚合函数）和UDTF（用户自定义表生成函数）。</p><p><strong>Hive有两个不同的接口编写UDF程序。一个是基础的UDF接口，一个是复杂的GenericUDF接口。</strong></p><ol><li>org.apache.hadoop.hive.ql. exec.UDF 基础UDF的函数读取和返回基本类型，即Hadoop和Hive的基本类型。如，Text、IntWritable、LongWritable、DoubleWritable等。</li><li>org.apache.hadoop.hive.ql.udf.generic.GenericUDF 复杂的GenericUDF可以处理Map、List、Set类型。</li></ol><p><a href="http://www.voidcn.com/article/p-suceexsl-vb.html" target="_blank" rel="noopener">参考文章</a></p><h2 id="Hive-SQL-按照学生科目取每个科目的TopN"><a href="#Hive-SQL-按照学生科目取每个科目的TopN" class="headerlink" title="Hive SQL : 按照学生科目取每个科目的TopN"></a>Hive SQL : 按照学生科目取每个科目的TopN</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">id,name,subject,score</span><br><span class="line">1,小明,语文,87</span><br><span class="line">2,张三,语文,27</span><br><span class="line">3,王五,语文,69</span><br><span class="line">4,李四,语文,99</span><br><span class="line">5,小明,数学,86</span><br><span class="line">6,马六,数学,33</span><br><span class="line">7,李四,数学,44</span><br><span class="line">8,小红,数学,50</span><br></pre></td></tr></table></figure><p><strong>按照各个科目的成绩排名 取 Top3</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">select a.* from</span><br><span class="line">(select id,name,subject,score,row_number() over(partition by subject order by score desc) rank from student) a</span><br><span class="line">where a.rank &lt;= 3</span><br></pre></td></tr></table></figure><p><a href="https://blog.csdn.net/WYpersist/article/details/80318305" target="_blank" rel="noopener">参考文章</a></p><h2 id="Hive-SQL-获取每个用户的前1-4次的数据"><a href="#Hive-SQL-获取每个用户的前1-4次的数据" class="headerlink" title="Hive SQL: 获取每个用户的前1/4次的数据"></a>Hive SQL: 获取每个用户的前1/4次的数据</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">cookieId  createTime    pv</span><br><span class="line">--------------------------</span><br><span class="line">cookie1 2015-04-10      1</span><br><span class="line">cookie1 2015-04-11      5</span><br><span class="line">cookie1 2015-04-12      7</span><br><span class="line">cookie1 2015-04-13      3</span><br><span class="line">cookie1 2015-04-14      2</span><br><span class="line">cookie1 2015-04-15      4</span><br><span class="line">cookie1 2015-04-16      4</span><br><span class="line">cookie2 2015-04-10      2</span><br><span class="line">cookie2 2015-04-11      3</span><br><span class="line">cookie2 2015-04-12      5</span><br><span class="line">cookie2 2015-04-13      6</span><br><span class="line">cookie2 2015-04-14      3</span><br><span class="line">cookie2 2015-04-15      9</span><br><span class="line">cookie2 2015-04-16      7</span><br></pre></td></tr></table></figure><p>获取每个用户前1/4次的访问记录</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">SELECT a.* from </span><br><span class="line">(SELECT cookieid,createtime,pv,NTILE(4)</span><br><span class="line">OVER(PARTITION BY cookieId ORDER BY createtime) AS rn</span><br><span class="line">from table ) a</span><br><span class="line">WHERE a.rn = 1</span><br></pre></td></tr></table></figure><p>NTILE(n)，用于将分组数据按照顺序切分成n片，返回当前切片值</p><p><a href="http://lxw1234.com/archives/2015/04/181.htm" target="_blank" rel="noopener">参考文章</a></p>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="Hive" scheme="https://icocos.github.io/categories/Hive/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="面试" scheme="https://icocos.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
    
      <category term="Hive" scheme="https://icocos.github.io/tags/Hive/"/>
    
  </entry>
  
  <entry>
    <title>大数据面试之Hadoop</title>
    <link href="https://icocos.github.io/2020/03/01/%E5%A4%A7%E6%95%B0%E6%8D%AE%E9%9D%A2%E8%AF%95%E4%B9%8BHadoop/"/>
    <id>https://icocos.github.io/2020/03/01/大数据面试之Hadoop/</id>
    <published>2020-03-01T15:16:41.000Z</published>
    <updated>2020-04-09T14:45:08.180Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h2 id="HDFS架构"><a href="#HDFS架构" class="headerlink" title="HDFS架构"></a>HDFS架构</h2><h3 id="1-HDFS-1-0-架构"><a href="#1-HDFS-1-0-架构" class="headerlink" title="1. HDFS 1.0 架构"></a>1. HDFS 1.0 架构</h3><p>HDFS 采用的是 Master/Slave 架构，一个 HDFS 集群包含一个单独的 NameNode 和多个 DataNode 节点</p><h4 id="NameNode"><a href="#NameNode" class="headerlink" title="NameNode"></a>NameNode</h4><p>NameNode 负责管理整个分布式系统的元数据，主要包括：</p><ul><li>目录树结构；</li><li>文件到数据库 Block 的映射关系；</li></ul><ul><li>Block 副本及其存储位置等管理数据；</li><li>DataNode 的状态监控，两者通过段时间间隔的心跳来传递管理信息和数据信息，通过这种方式的信息传递，NameNode 可以获知每个 DataNode 保存的 Block 信息、DataNode 的健康状况、命令 DataNode 启动停止等（如果发现某个 DataNode 节点故障，NameNode 会将其负责的 block 在其他 DataNode 上进行备份）。</li></ul><p>这些数据保存在内存中，同时在磁盘保存两个元数据管理文件：fsimage 和 editlog。</p><ul><li>fsimage：是内存命名空间元数据在外存的镜像文件；</li><li>editlog：则是各种元数据操作的 write-ahead-log 文件，在体现到内存数据变化前首先会将操作记入 editlog 中，以防止数据丢失。</li></ul><p>这两个文件相结合可以构造完整的内存数据。</p><h4 id="Secondary-NameNode"><a href="#Secondary-NameNode" class="headerlink" title="Secondary NameNode"></a>Secondary NameNode</h4><p>Secondary NameNode 并不是 NameNode 的热备机，而是定期从 NameNode 拉取 fsimage 和 editlog 文件，并对两个文件进行合并，形成新的 fsimage 文件并传回 NameNode，这样做的目的是减轻 NameNod 的工作压力，本质上 SNN 是一个提供检查点功能服务的服务点。</p><h4 id="DataNode"><a href="#DataNode" class="headerlink" title="DataNode"></a>DataNode</h4><p>负责数据块的实际存储和读写工作，Block 默认是64MB（HDFS2.0改成了128MB），当客户端上传一个大文件时，HDFS 会自动将其切割成固定大小的 Block，为了保证数据可用性，每个 Block 会以多备份的形式存储，默认是3份。</p><hr><h3 id="2-HDFS-2-0-的-HA-实现"><a href="#2-HDFS-2-0-的-HA-实现" class="headerlink" title="2. HDFS 2.0 的 HA 实现"></a>2. HDFS 2.0 的 HA 实现</h3><p><img src="D:\Note\big-data-interview\BigData-Interview\pictures\hdfs-ha.png" alt="2.0架构图"></p><ul><li><p><strong>Active NameNode 和 Standby NameNode</strong>：两台 NameNode 形成互备，一台处于 Active 状态，为主 NameNode，另外一台处于 Standby 状态，为备 NameNode，只有主 NameNode 才能对外提供读写服务；</p></li><li><p><strong>ZKFailoverController</strong>（主备切换控制器，FC）：ZKFailoverController 作为独立的进程运行，对 NameNode 的主备切换进行总体控制。ZKFailoverController 能及时检测到 NameNode 的健康状况，在主 NameNode 故障时借助 Zookeeper 实现自动的主备选举和切换（当然 NameNode 目前也支持不依赖于 Zookeeper 的手动主备切换）；</p></li><li><p><strong>Zookeeper 集群</strong>：为主备切换控制器提供主备选举支持；</p></li><li><p><strong>共享存储系统</strong>：共享存储系统是实现 NameNode 的高可用最为关键的部分，共享存储系统保存了 NameNode 在运行过程中所产生的 HDFS 的元数据。主 NameNode 和备 NameNode 通过共享存储系统实现元数据同步。在进行主备切换的时候，新的主 NameNode 在<strong>确认元数据完全同步之后才能继续对外提供服务</strong>。</p></li><li><p><strong>DataNode 节点</strong>：因为主 NameNode 和备 NameNode 需要共享 HDFS 的数据块和 DataNode 之间的映射关系，为了使故障切换能够快速进行，DataNode 会同时向主 NameNode 和备 NameNode 上报数据块的位置信息。</p></li></ul><p><a href="http://matt33.com/2018/07/15/hdfs-architecture-learn/#HDFS-2-0-%E7%9A%84-HA-%E5%AE%9E%E7%8E%B0" target="_blank" rel="noopener">-&gt;参考文章链接</a></p><h2 id="Yarn架构"><a href="#Yarn架构" class="headerlink" title="Yarn架构"></a>Yarn架构</h2><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/yarn.gif" alt=""></p><h3 id="1-ResourceManager（RM）"><a href="#1-ResourceManager（RM）" class="headerlink" title="1. ResourceManager（RM）"></a>1. ResourceManager（RM）</h3><p>RM 是一个全局的资源管理器，负责整个系统的资源管理和分配，它主要有两个组件构成：</p><ol><li>调度器：Scheduler；</li><li>应用程序管理器：Applications Manager，ASM。</li></ol><h4 id="调度器"><a href="#调度器" class="headerlink" title="调度器"></a>调度器</h4><p>调度器根据容量、队列等限制条件（如某个队列分配一定的资源，最多执行一定数量的作业等），将系统中的资源分配给各个正在运行的应用程序。要注意的是，该调度器是一个纯调度器，它不再从事任何与应用程序有关的工作，比如不负责重新启动（因应用程序失败或者硬件故障导致的失败），这些均交由应用程序相关的 ApplicationMaster 完成。调度器仅根据各个应用程序的资源需求进行资源分配，而资源分配单位用一个抽象概念 <strong>资源容器(Resource Container，也即 Container)</strong>，Container 是一个动态资源分配单位，它将内存、CPU、磁盘、网络等资源封装在一起，从而限定每个任务使用的资源量。此外，该调度器是一个可插拔的组件，用户可根据自己的需求设计新的调度器，YARN 提供了多种直接可用的调度器，比如 Fair Scheduler 和 Capacity Schedule 等。</p><h4 id="应用程序管理器"><a href="#应用程序管理器" class="headerlink" title="应用程序管理器"></a>应用程序管理器</h4><p>应用程序管理器负责管理整个系统中所有应用程序，包括应用程序提交、与调度器协商资源以 AM、监控 AM 运行状态并在失败时重新启动它等。</p><h3 id="2-NodeManager（NM）"><a href="#2-NodeManager（NM）" class="headerlink" title="2. NodeManager（NM）"></a>2. NodeManager（NM）</h3><p>NM 是每个节点上运行的资源和任务管理器，一方面，它会定时向 RM 汇报本节点上的资源使用情况和各个 Container 的运行状态；另一方面，它接收并处理来自 AM 的 Container 启动/停止等各种请求。</p><h3 id="3-ApplicationMaster（AM）"><a href="#3-ApplicationMaster（AM）" class="headerlink" title="3. ApplicationMaster（AM）"></a>3. ApplicationMaster（AM）</h3><p>提交的每个作业都会包含一个 AM，主要功能包括：</p><ol><li>与 RM 协商以获取资源（用 container 表示）；</li><li>将得到的任务进一步分配给内部的任务；</li><li>与 NM 通信以启动/停止任务；</li><li>监控所有任务的运行状态，当任务有失败时，重新为任务申请资源并重启任务。</li></ol><p>MapReduce 就是原生支持 ON YARN 的一种框架，可以在 YARN 上运行 MapReduce 作业。有很多分布式应用都开发了对应的应用程序框架，用于在 YARN 上运行任务，例如 Spark，Storm、Flink 等。</p><h3 id="4-Container"><a href="#4-Container" class="headerlink" title="4. Container"></a>4. Container</h3><p>Container 是 YARN 中的资源抽象，它封装了某个节点上的多维度资源，如内存、CPU、磁盘、网络等，当 AM 向 RM 申请资源时，RM 为 AM 返回的资源便是用 Container 表示的。 YARN 会为每个任务分配一个 Container 且该任务只能使用该 Container 中描述的资源。</p><h2 id="MapReduce过程"><a href="#MapReduce过程" class="headerlink" title="MapReduce过程"></a>MapReduce过程</h2><p>MapReduce分为两个阶段: <strong>Map</strong> 和  <strong>Ruduce</strong>.</p><p><strong>Map阶段:</strong></p><ol><li><strong>input</strong>. 在进行map计算之前，mapreduce会根据输入文件计算输入分片（input split），每个输入分片（input split）针对一个map任务</li><li><strong>map</strong>. 就是程序员编写好的map函数了，因此map函数效率相对好控制，而且一般map操作都是本地化操作也就是在数据存储节点上进行</li><li><strong>Partition</strong>. 需要计算每一个map的结果需要发到哪个reduce端,partition数等于reducer数.默认采用HashPartition.</li><li><p><strong>spill</strong>.此阶段分为sort和combine.首先分区过得数据会经过排序之后写入环形内存缓冲区.在达到阈值之后守护线程将数据溢出分区文件.</p><ul><li><strong>sort</strong>. 在写入环形缓冲区前,对数据排序.&lt;key,value,partition&gt;格式排序</li><li><strong>combine</strong>(可选). 在溢出文件之前,提前开始combine,相当于本地化的reduce操作</li></ul></li><li><p><strong>merge.</strong> spill结果会有很多个文件,但最终输出只有一个,故有一个merge操作会合并所有的本地文件,并且该文件会有一个对应的索引文件.</p></li></ol><p><strong>Reduce阶段:</strong></p><ol><li><strong>copy</strong>. 拉取数据,reduce启动数据copy线程(默认5个),通过Http请求对应节点的map task输出文件,copy的数据也会先放到内部缓冲区.之后再溢写,类似map端操作.</li><li><strong>merge</strong>. 合并多个copy的多个map端的数据.在一个reduce端先将多个map端的数据溢写到本地磁盘,之后再将多个文件合并成一个文件.  数据经过 <strong>内存-&gt;磁盘 , 磁盘-&gt;磁盘</strong>的过程.</li><li><strong>output</strong>.merge阶段最后会生成一个文件,将此文件转移到内存中,shuffle阶段结束</li><li><strong>reduce</strong>. 开始执行reduce任务,最后结果保留在hdfs上.</li></ol><h2 id="Yarn-调度MapReduce过程"><a href="#Yarn-调度MapReduce过程" class="headerlink" title="Yarn 调度MapReduce过程"></a>Yarn 调度MapReduce过程</h2><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/picturees/yarn调度mr过程.jpg" alt=""></p><ol><li>Mr程序提交到客户端所在的节点（MapReduce）</li><li>yarnrunner向Resourcemanager申请一个application。</li><li>rm将该应用程序的资源路径返回给yarnrunner</li><li>该程序将运行所需资源提交到HDFS上</li><li>程序资源提交完毕后，申请运行mrAppMaster</li><li>RM将用户的请求初始化成一个task</li><li>其中一个NodeManager领取到task任务。</li><li>该NodeManager创建容器Container，并产生MRAppmaster</li><li>Container从HDFS上拷贝资源到本地</li><li>MRAppmaster向RM申请运行maptask容器</li><li>RM将运行maptask任务分配给另外两个NodeManager，另两个NodeManager分别领取任务并创建容器.</li><li>MR向两个接收到任务的NodeManager发送程序启动脚本，这两个NodeManager分别启动maptask，maptask对数据分区排序。</li><li>MRAppmaster向RM申请2个容器，运行reduce task。</li><li>reduce task向maptask获取相应分区的数据。</li><li>程序运行完毕后，MR会向RM注销自己。</li></ol><p><a href="https://blog.csdn.net/qq_26442553/article/details/78699759" target="_blank" rel="noopener">参考文章</a></p><h2 id="hdfs写流程"><a href="#hdfs写流程" class="headerlink" title="hdfs写流程"></a>hdfs写流程</h2><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/hdfs写流程.png" alt=""></p><ol><li>Client 调用 DistributedFileSystem 对象的 <code>create</code> 方法，创建一个文件输出流（FSDataOutputStream）对象；</li><li>通过 DistributedFileSystem 对象与集群的 NameNode 进行一次 RPC 远程调用，在 HDFS 的 Namespace 中创建一个文件条目（Entry），此时该条目没有任何的 Block，NameNode 会返回该数据每个块需要拷贝的 DataNode 地址信息；</li><li>通过 FSDataOutputStream 对象，开始向 DataNode 写入数据，数据首先被写入 FSDataOutputStream 对象内部的数据队列中，数据队列由 DataStreamer 使用，它通过选择合适的 DataNode 列表来存储副本，从而要求 NameNode 分配新的 block；</li><li>DataStreamer 将数据包以流式传输的方式传输到分配的第一个 DataNode 中，该数据流将数据包存储到第一个 DataNode 中并将其转发到第二个 DataNode 中，接着第二个 DataNode 节点会将数据包转发到第三个 DataNode 节点；</li><li>DataNode 确认数据传输完成，最后由第一个 DataNode 通知 client 数据写入成功；</li><li>完成向文件写入数据，Client 在文件输出流（FSDataOutputStream）对象上调用 <code>close</code> 方法，完成文件写入；</li><li>调用 DistributedFileSystem 对象的 complete 方法，通知 NameNode 文件写入成功，NameNode 会将相关结果记录到 editlog 中。</li></ol><h2 id="hdfs读流程"><a href="#hdfs读流程" class="headerlink" title="hdfs读流程"></a>hdfs读流程</h2><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/hdfs读流程.png" alt=""></p><ol><li>Client 通过 DistributedFileSystem 对象与集群的 NameNode 进行一次 RPC 远程调用，获取文件 block 位置信息；</li><li>NameNode 返回存储的每个块的 DataNode 列表；</li><li>Client 将连接到列表中最近的 DataNode；</li><li>Client 开始从 DataNode 并行读取数据；</li><li>一旦 Client 获得了所有必须的 block，它就会将这些 block 组合起来形成一个文件。</li></ol><h2 id="hdfs创建一个文件的流程"><a href="#hdfs创建一个文件的流程" class="headerlink" title="hdfs创建一个文件的流程"></a>hdfs创建一个文件的流程</h2><ol><li>客户端通过ClientProtocol协议向RpcServer发起创建文件的RPC请求。</li><li>FSNamesystem封装了各种HDFS操作的实现细节，RpcServer调用FSNamesystem中的相关方法以创建目录。</li><li>进一步的，FSDirectory封装了各种目录树操作的实现细节，FSNamesystem调用FSDirectory中的相关方法在目录树中创建目标文件，并通过日志系统备份文件系统的修改。</li><li>最后，RpcServer将RPC响应返回给客户端。</li></ol><p><a href="https://monkeysayhi.github.io/2018/02/07/%E6%BA%90%E7%A0%81%7CHDFS%E4%B9%8BNameNode%EF%BC%9A%E5%88%9B%E5%BB%BA%E6%96%87%E4%BB%B6%EF%BC%881%EF%BC%89/" target="_blank" rel="noopener">参考文章</a></p><h2 id="hadoop1-x-和hadoop-2-x-的区别"><a href="#hadoop1-x-和hadoop-2-x-的区别" class="headerlink" title="hadoop1.x 和hadoop 2.x 的区别"></a>hadoop1.x 和hadoop 2.x 的区别</h2><ol><li><p><strong>资源调度方式的改变</strong></p><p>在1.x, 使用Jobtracker负责任务调度和资源管理,单点负担过重,在2.x中,新增了yarn作为集群的调度工具.在yarn中,使用ResourceManager进行 资源管理, 单独开启一个Container作为ApplicationMaster来进行任务管理.</p></li><li><p><strong>HA模式</strong></p><p>在1.x中没有HA模式,集群中只有一个NameNode,而在2.x中可以启用HA模式,存在一个Active NameNode 和Standby NameNode.</p></li><li><p><strong>HDFS Federation</strong></p><p>Hadoop 2.0中对HDFS进行了改进，使NameNode可以横向扩展成多个，每个NameNode分管一部分目录，进而产生了HDFS Federation，该机制的引入不仅增强了HDFS的扩展性，也使HDFS具备了隔离性</p></li></ol><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/hadoop1.jpg" alt=""></p><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/hadoop2.jpg" alt=""></p><h2 id="hadoop1-x的缺点"><a href="#hadoop1-x的缺点" class="headerlink" title="hadoop1.x的缺点"></a>hadoop1.x的缺点</h2><ol><li>JobTracker存在单点故障的隐患</li><li>任务调度和资源管理全部是JobTracker来完成,单点负担过重</li><li>TaskTracker以Map/Reduce数量表示资源太过简单</li><li>TaskTracker 分Map Slot 和 Reduce Slot, 如果任务只需要map任务可能会造成资源浪费</li></ol><h2 id="hadoop-HA介绍"><a href="#hadoop-HA介绍" class="headerlink" title="hadoop HA介绍"></a>hadoop HA介绍</h2><p><img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/hdfs-ha.png" alt=""></p><ol><li><strong>Active NameNode 和 Standby NameNode</strong>：两台 NameNode 形成互备，一台处于 Active 状态，为主 NameNode，另外一台处于 Standby 状态，为备 NameNode，只有主 NameNode 才能对外提供读写服务；</li><li><strong>ZKFailoverController（主备切换控制器，FC）</strong>：ZKFailoverController 作为独立的进程运行，对 NameNode 的主备切换进行总体控制。ZKFailoverController 能及时检测到 NameNode 的健康状况，在主 NameNode 故障时借助 Zookeeper 实现自动的主备选举和切换（当然 NameNode 目前也支持不依赖于 Zookeeper 的手动主备切换）；</li><li><strong>Zookeeper 集群</strong>：为主备切换控制器提供主备选举支持；</li><li><strong>共享存储系统</strong>：共享存储系统是实现 NameNode 的高可用最为关键的部分，共享存储系统保存了 NameNode 在运行过程中所产生的 HDFS 的元数据。主 NameNode 和备 NameNode 通过共享存储系统实现元数据同步。在进行主备切换的时候，新的主 NameNode 在<strong>确认元数据完全同步之后才能继续对外提供服务</strong>。</li><li><strong>DataNode 节点</strong>：因为主 NameNode 和备 NameNode 需要共享 HDFS 的数据块和 DataNode 之间的映射关系，为了使故障切换能够快速进行，DataNode 会同时向主 NameNode 和备 NameNode 上报数据块的位置信息。</li></ol><h2 id="hadoop的常用配置文件有哪些"><a href="#hadoop的常用配置文件有哪些" class="headerlink" title="hadoop的常用配置文件有哪些"></a>hadoop的常用配置文件有哪些</h2><ul><li><p><strong>hadoop-env.sh</strong>: 用于定义hadoop运行环境相关的配置信息，比如配置JAVA_HOME环境变量、为hadoop的JVM指定特定的选项、指定日志文件所在的目录路径以及master和slave文件的位置等；</p></li><li><p><strong>core-site.xml</strong>: 用于定义系统级别的参数，如HDFS URL、Hadoop的临时目录以及用于rack-aware集群中的配置文件的配置等，此中的参数定义会覆盖core-default.xml文件中的默认配置；</p></li><li><p><strong>hdfs-site.xml</strong>: HDFS的相关设定，如文件副本的个数、块大小及是否使用强制权限等，此中的参数定义会覆盖hdfs-default.xml文件中的默认配置；</p></li><li><p><strong>mapred-site.xml</strong>：HDFS的相关设定，如reduce任务的默认个数、任务所能够使用内存的默认上下限等，此中的参数定义会覆盖mapred-default.xml文件中的默认配置；</p></li></ul><h2 id="小文件过多会有什么危害-如何避免"><a href="#小文件过多会有什么危害-如何避免" class="headerlink" title="小文件过多会有什么危害,如何避免?"></a>小文件过多会有什么危害,如何避免?</h2><p>Hadoop上大量HDFS元数据信息存储在NameNode内存中,因此过多的小文件必定会压垮NameNode的内存.</p><p>每个元数据对象约占150byte，所以如果有1千万个小文件，每个文件占用一个block，则NameNode大约需要2G空间。如果存储1亿个文件，则NameNode需要20G空间.</p><p>显而易见的解决这个问题的方法就是合并小文件,可以选择在客户端上传时执行一定的策略先合并,或者是使用Hadoop的CombineFileInputFormat&lt;K,V&gt;实现小文件的合并</p><p><a href="https://blog.csdn.net/luofazha2012/article/details/80904791" target="_blank" rel="noopener">参考文章</a></p><h2 id="启动hadoop集群会分别启动哪些进程-各自的作用"><a href="#启动hadoop集群会分别启动哪些进程-各自的作用" class="headerlink" title="启动hadoop集群会分别启动哪些进程,各自的作用"></a>启动hadoop集群会分别启动哪些进程,各自的作用</h2><ul><li><p><strong>NameNode：</strong></p><ul><li>维护文件系统树及整棵树内所有的文件和目录。这些信息永久保存在本地磁盘的两个文件中：命名空间镜像文件、编辑日志文件</li><li>记录每个文件中各个块所在的数据节点信息，这些信息在内存中保存，每次启动系统时重建这些信息</li><li>负责响应客户端的   数据块位置请求  。也就是客户端想存数据，应该往哪些节点的哪些块存；客户端想取数据，应该到哪些节点取</li><li>接受记录在数据存取过程中，datanode节点报告过来的故障、损坏信息</li></ul></li><li><p><strong>SecondaryNameNode(非HA模式)：</strong></p><ul><li>实现namenode容错的一种机制。定期合并编辑日志与命名空间镜像，当namenode挂掉时，可通过一定步骤进行上顶。(<strong>注意 并不是NameNode的备用节点</strong>)</li></ul></li><li><strong>DataNode：</strong><ul><li>根据需要存取并检索数据块</li><li>定期向namenode发送其存储的数据块列表</li></ul></li><li><strong>ResourceManager：</strong><ul><li>负责Job的调度,将一个任务与一个NodeManager相匹配。也就是将一个MapReduce之类的任务分配给一个从节点的NodeManager来执行。</li></ul></li><li><p><strong>NodeManager：</strong></p><ul><li>运行ResourceManager分配的任务，同时将任务进度向application master报告</li></ul></li><li><p><strong>JournalNode(HA下启用):</strong></p><ul><li>高可用情况下存放namenode的editlog文件</li></ul></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="Hadoop" scheme="https://icocos.github.io/categories/Hadoop/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="面试" scheme="https://icocos.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
    
      <category term="Hadoop" scheme="https://icocos.github.io/tags/Hadoop/"/>
    
  </entry>
  
  <entry>
    <title>大数据面试题全套汇总+答案</title>
    <link href="https://icocos.github.io/2020/02/29/%E5%A4%A7%E6%95%B0%E6%8D%AE%E9%9D%A2%E8%AF%95%E9%A2%98%E5%85%A8%E5%A5%97%E6%B1%87%E6%80%BB+%E7%AD%94%E6%A1%88/"/>
    <id>https://icocos.github.io/2020/02/29/大数据面试题全套汇总+答案/</id>
    <published>2020-02-29T13:18:38.000Z</published>
    <updated>2020-04-09T14:45:08.819Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h4 id="大数据面试题全套汇总-答案"><a href="#大数据面试题全套汇总-答案" class="headerlink" title="大数据面试题全套汇总+答案"></a>大数据面试题全套汇总+答案</h4><table><br>    <tr><br>     <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/hadoop.jpg"></th><br>     <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/hive.jpg"></th><br>     <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/spark.jpg"></th><br>     <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/flink.png"></th><br>     <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/hbase.png"></th><br>     <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/kafka.png"></th><br>     <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/bd_Interview/pictures/zookeeper.jpg"></th><br>    </tr><br><tr><br>  <td align="center"><a href="#一hadoop">Hadoop</a></td><br>  <td align="center"><a href="#二hive">Hive</a></td><br>  <td align="center"><a href="#三spark">Spark</a></td><br>  <td align="center"><a href="#四flink">Flink</a></td><br>  <td align="center"><a href="#五hbase">HBase</a></td><br>  <td align="center"><a href="#六kafka">Kafka</a></td><br>  <td align="center"><a href="#七zookeeper">Zookeeper</a></td><br></tr><br>    </table><h2 id="一、Hadoop"><a href="#一、Hadoop" class="headerlink" title="一、Hadoop"></a>一、Hadoop</h2><ol><li>HDFS架构 </li><li>Yarn架构 </li><li>MapReduce过程 </li><li>Yarn 调度MapReduce </li><li>hdfs写流程 </li><li>hdfs读流程</li><li>hdfs创建一个文件的流程 </li><li>hadoop1.x 和hadoop 2.x 的区别 </li><li>hadoop1.x的缺点 </li><li>hadoop HA介绍 </li><li>hadoop的常用配置文件有哪些,自己实际改过哪些? </li><li>小文件过多会有什么危害,如何避免? </li><li>启动hadoop集群会分别启动哪些进程,各自的作用 </li></ol><h2 id="二、Hive"><a href="#二、Hive" class="headerlink" title="二、Hive"></a>二、Hive</h2><ol><li>hive 内部表和外部表的区别</li><li>hive中 sort by / order by / cluster by / distribute by 的区别</li><li>hive的metastore的三种模式</li><li>hive 中 join都有哪些</li><li>Impala 和 hive 的查询有哪些区别 </li><li>Hive中大表join小表的优化方法 </li><li>Hive Sql 是怎样解析成MR job的? </li><li>Hive UDF简单介绍 </li><li>SQL题: 按照学生科目分组, 取每个科目的TopN </li><li>SQL题: 获取每个用户的前1/4次的数据 </li></ol><h2 id="三、Spark"><a href="#三、Spark" class="headerlink" title="三、Spark"></a>三、Spark</h2><ol><li>讲一下spark 的运行架构 </li><li>一个spark程序的执行流程 </li><li>spark的shuffle介绍 </li><li>Spark的 partitioner 都有哪些? </li><li>spark 有哪几种join </li><li>RDD有哪些特点 </li><li>讲一下宽依赖和窄依赖 </li><li>Spark中的算子都有哪些 </li><li>RDD的缓存级别都有哪些 </li><li>RDD 懒加载是什么意思 </li><li>讲一下spark的几种部署方式 </li><li>spark on yarn 模式下的 cluster模式和 client模式有什么区别 </li><li>spark运行原理,从提交一个jar到最后返回结果,整个过程 </li><li>spark的stage是如何划分的 </li><li>spark的rpc: spark2.0为什么放弃了akka 而用netty? </li><li>spark的各种HA,  master/worker/executor/driver/task的ha </li><li>spark的内存管理机制,spark 1.6前后分析对比, spark2.0 做出来哪些优化 </li><li>讲一下spark 中的广播变量 </li><li>什么是数据倾斜,怎样去处理数据倾斜 </li><li>分析一下一段spark代码中哪些部分在Driver端执行,哪些部分在Worker端执行 </li></ol><h2 id="四、Flink"><a href="#四、Flink" class="headerlink" title="四、Flink"></a>四、Flink</h2><ol><li>讲一下flink的运行架构 </li><li>讲一下flink的作业执行流程 </li><li>flink具体是如何实现exactly once 语义 </li><li>flink 的 window 实现机制 </li><li>flink的window分类 </li><li>flink 的 state 是存储在哪里的 </li><li>flink是如何实现反压的 </li><li>flink的部署模式都有哪些 </li><li>讲一下flink on yarn的部署 </li><li>flink中的时间概念 , eventTime 和 processTime的区别 </li><li>flink中的session Window怎样使用 </li></ol><h2 id="五、HBase"><a href="#五、HBase" class="headerlink" title="五、HBase"></a>五、HBase</h2><ol><li>讲一下 Hbase 架构 </li><li>hbase 如何设计 rowkey </li><li>讲一下hbase的存储结构,这样的存储结构有什么优缺点 </li><li>hbase的HA实现,zookeeper在其中的作用 </li><li>HMaster宕机的时候,哪些操作还能正常工作 </li><li>讲一下hbase的写数据的流程 </li><li>讲一下hbase读数据的流程 </li></ol><h2 id="六、Kafka"><a href="#六、Kafka" class="headerlink" title="六、Kafka"></a>六、Kafka</h2><ol><li>讲一下 kafka 的架构 </li><li>kafka 与其他消息组件对比? </li><li>kafka 实现高吞吐的原理 </li><li>kafka怎样保证不重复消费 </li><li>kafka怎样保证不丢失消息 </li><li>kafka 与 spark streaming 集成,如何保证 exactly once 语义 </li><li>ack 有哪几种, 生产中怎样选择? </li><li>如何通过 offset 寻找数据 </li><li>如何清理过期数据 </li><li>1条message中包含哪些信息 </li><li>讲一下zookeeper在kafka中的作用 </li><li>kafka 可以脱离 zookeeper 单独使用吗 </li><li>kafka有几种数据保留策略 </li><li>kafka同时设置了7天和10G清除数据,到第5天的时候消息到达了10G,这个时候kafka如何处理? </li></ol><h2 id="七、Zookeeper"><a href="#七、Zookeeper" class="headerlink" title="七、Zookeeper"></a>七、Zookeeper</h2><ol><li>zookeeper是什么,都有哪些功能 </li><li>zk 有几种部署模式 </li><li>zk 是怎样保证主从节点的状态同步</li><li>说一下 zk 的通知机制</li><li>zk 的分布式锁实现方式</li><li>zk 采用的哪种分布式一致性协议? 还有哪些分布式一致性协议</li><li>讲一下leader 选举过程</li></ol>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="大数据" scheme="https://icocos.github.io/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="面试" scheme="https://icocos.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
    
  </entry>
  
  <entry>
    <title>大数据应用常用打包方式</title>
    <link href="https://icocos.github.io/2019/12/29/%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%BA%94%E7%94%A8%E5%B8%B8%E7%94%A8%E6%89%93%E5%8C%85%E6%96%B9%E5%BC%8F/"/>
    <id>https://icocos.github.io/2019/12/29/大数据应用常用打包方式/</id>
    <published>2019-12-29T13:18:38.000Z</published>
    <updated>2020-04-09T14:45:08.846Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h1 id="大数据应用常用打包方式"><a href="#大数据应用常用打包方式" class="headerlink" title="大数据应用常用打包方式"></a>大数据应用常用打包方式</h1><nav><br><a href="#一简介">一、简介</a><br><br><a href="#二mvn-package">二、mvn package</a><br><br><a href="#三maven-assembly-plugin插件">三、maven-assembly-plugin插件</a><br><br><a href="#四maven-shade-plugin插件">四、maven-shade-plugin插件</a><br><br><a href="#五其他打包需求">五、其他打包需求</a><br><br><a href="#1-使用非Maven仓库中的Jar">1. 使用非Maven仓库中的Jar</a><br><br><a href="#2-排除集群中已经存在的Jar">2. 排除集群中已经存在的Jar</a><br><br><a href="#3-打包Scala文件">3. 打包Scala文件</a><br><br></nav><h2 id="一、简介"><a href="#一、简介" class="headerlink" title="一、简介"></a>一、简介</h2><p>在提交大数据作业到集群上运行时，通常需要先将项目打成 JAR 包。这里以 Maven 为例，常用打包方式如下：</p><ul><li>不加任何插件，直接使用 mvn package 打包；</li><li>使用 maven-assembly-plugin 插件；</li><li>使用 maven-shade-plugin 插件；</li><li>使用 maven-jar-plugin 和 maven-dependency-plugin 插件；</li></ul><p>以下分别进行详细的说明。</p><h2 id="二、mvn-package"><a href="#二、mvn-package" class="headerlink" title="二、mvn package"></a>二、mvn package</h2><p>不在 POM 中配置任何插件，直接使用 <code>mvn package</code> 进行项目打包，这对于没有使用外部依赖包的项目是可行的。但如果项目中使用了第三方 JAR 包，就会出现问题，因为 <code>mvn package</code> 打的 JAR 包中是不含有依赖包，会导致作业运行时出现找不到第三方依赖的异常。这种方式局限性比较大，因为实际的项目往往很复杂，通常都会依赖第三方 JAR。</p><p>大数据框架的开发者也考虑到这个问题，所以基本所有的框架都支持在提交作业时使用 <code>--jars</code> 指定第三方依赖包，但是这种方式的问题同样很明显，就是你必须保持生产环境与开发环境中的所有 JAR 包版本一致，这是有维护成本的。</p><p>基于上面这些原因，最简单的是采用 <code>All In One</code> 的打包方式，把所有依赖都打包到一个 JAR 文件中，此时对环境的依赖性最小。要实现这个目的，可以使用 Maven 提供的 <code>maven-assembly-plugin</code> 或 <code>maven-shade-plugin</code> 插件。</p><h2 id="三、maven-assembly-plugin插件"><a href="#三、maven-assembly-plugin插件" class="headerlink" title="三、maven-assembly-plugin插件"></a>三、maven-assembly-plugin插件</h2><p><code>Assembly</code> 插件支持将项目的所有依赖、文件都打包到同一个输出文件中。目前支持输出以下文件类型：</p><ul><li>zip</li><li>tar</li><li>tar.gz (or tgz)</li><li>tar.bz2 (or tbz2)</li><li>tar.snappy</li><li>tar.xz (or txz)</li><li>jar</li><li>dir</li><li>war</li></ul><h3 id="3-1-基本使用"><a href="#3-1-基本使用" class="headerlink" title="3.1 基本使用"></a>3.1 基本使用</h3><p>在 POM.xml 中引入插件，指定打包格式的配置文件 <code>assembly.xml</code>(名称可自定义)，并指定作业的主入口类：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-assembly-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">descriptors</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">descriptor</span>&gt;</span>src/main/resources/assembly.xml<span class="tag">&lt;/<span class="name">descriptor</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">descriptors</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">archive</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">manifest</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">mainClass</span>&gt;</span>com.heibaiying.wordcount.ClusterWordCountApp<span class="tag">&lt;/<span class="name">mainClass</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">manifest</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">archive</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br></pre></td></tr></table></figure><p>assembly.xml 文件内容如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">assembly</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/ASSEMBLY/2.0.0"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/ASSEMBLY/2.0.0 </span></span></span><br><span class="line"><span class="tag"><span class="string">                              http://maven.apache.org/xsd/assembly-2.0.0.xsd"</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span>&gt;</span>jar-with-dependencies<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!--指明打包方式--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">formats</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">format</span>&gt;</span>jar<span class="tag">&lt;/<span class="name">format</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">formats</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">includeBaseDirectory</span>&gt;</span>false<span class="tag">&lt;/<span class="name">includeBaseDirectory</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencySets</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependencySet</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">outputDirectory</span>&gt;</span>/<span class="tag">&lt;/<span class="name">outputDirectory</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">useProjectArtifact</span>&gt;</span>true<span class="tag">&lt;/<span class="name">useProjectArtifact</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">unpack</span>&gt;</span>true<span class="tag">&lt;/<span class="name">unpack</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>runtime<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!--这里以排除 storm 环境中已经提供的 storm-core 为例，演示排除 Jar 包--&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">excludes</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">exclude</span>&gt;</span>org.apache.storm:storm-core<span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">excludes</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependencySet</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencySets</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">assembly</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="3-2-打包命令"><a href="#3-2-打包命令" class="headerlink" title="3.2  打包命令"></a>3.2  打包命令</h3><p>采用 maven-assembly-plugin 进行打包时命令如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> mvn assembly:assembly</span></span><br></pre></td></tr></table></figure><p>打包后会同时生成两个 JAR 包，其中后缀为 <code>jar-with-dependencies</code> 是含有第三方依赖的 JAR 包，后缀是由 <code>assembly.xml</code> 中 <code>&lt;id&gt;</code> 标签指定的，可以自定义修改。</p><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/storm-jar.png"> </div><h2 id="四、maven-shade-plugin插件"><a href="#四、maven-shade-plugin插件" class="headerlink" title="四、maven-shade-plugin插件"></a>四、maven-shade-plugin插件</h2><p><code>maven-shade-plugin</code> 比 <code>maven-assembly-plugin</code> 功能更为强大，比如你的工程依赖很多的 JAR 包，而被依赖的 JAR 又会依赖其他的 JAR 包，这样,当工程中依赖到不同的版本的 JAR 时，并且 JAR 中具有相同名称的资源文件时，shade 插件会尝试将所有资源文件打包在一起时，而不是和 assembly 一样执行覆盖操作。</p><p><strong>通常使用 <code>maven-shade-plugin</code> 就能够完成大多数的打包需求，其配置简单且适用性最广，因此建议优先使用此方式。</strong></p><h3 id="4-1-基本配置"><a href="#4-1-基本配置" class="headerlink" title="4.1  基本配置"></a>4.1  基本配置</h3><p>采用 <code>maven-shade-plugin</code> 进行打包时候，配置示例如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-shade-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">createDependencyReducedPom</span>&gt;</span>true<span class="tag">&lt;/<span class="name">createDependencyReducedPom</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">filters</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">filter</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifact</span>&gt;</span>*:*<span class="tag">&lt;/<span class="name">artifact</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">excludes</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">exclude</span>&gt;</span>META-INF/*.SF<span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">exclude</span>&gt;</span>META-INF/*.sf<span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">exclude</span>&gt;</span>META-INF/*.DSA<span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">exclude</span>&gt;</span>META-INF/*.dsa<span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">exclude</span>&gt;</span>META-INF/*.RSA<span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">exclude</span>&gt;</span>META-INF/*.rsa<span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">exclude</span>&gt;</span>META-INF/*.EC<span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">exclude</span>&gt;</span>META-INF/*.ec<span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">exclude</span>&gt;</span>META-INF/MSFTSIG.SF<span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">exclude</span>&gt;</span>META-INF/MSFTSIG.RSA<span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">excludes</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">filter</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">filters</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactSet</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">excludes</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">exclude</span>&gt;</span>org.apache.storm:storm-core<span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">excludes</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">artifactSet</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">executions</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">execution</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">phase</span>&gt;</span>package<span class="tag">&lt;/<span class="name">phase</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">goals</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">goal</span>&gt;</span>shade<span class="tag">&lt;/<span class="name">goal</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">goals</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">transformers</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">transformer</span></span></span><br><span class="line"><span class="tag">                       <span class="attr">implementation</span>=<span class="string">"org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"</span>/&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">transformer</span></span></span><br><span class="line"><span class="tag">                       <span class="attr">implementation</span>=<span class="string">"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">transformer</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">transformers</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">execution</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">executions</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br></pre></td></tr></table></figure><p>以上配置来源于 Storm Github，在上面的配置中，排除了部分文件，这是因为有些 JAR 包生成时，会使用 jarsigner 生成文件签名 (完成性校验)，分为两个文件存放在 META-INF 目录下：</p><ul><li>a signature file, with a .SF extension；</li><li>a signature block file, with a .DSA, .RSA, or .EC extension。</li></ul><p>如果某些包的存在重复引用，这可能会导致在打包时候出现 <code>Invalid signature file digest for Manifest main attributes</code> 异常，所以在配置中排除这些文件。</p><h3 id="4-2-打包命令"><a href="#4-2-打包命令" class="headerlink" title="4.2 打包命令"></a>4.2 打包命令</h3><p>使用 maven-shade-plugin 进行打包的时候，打包命令和普通打包一样：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> mvn package</span></span><br></pre></td></tr></table></figure><p>打包后会生成两个 JAR 包，提交到服务器集群时使用非 original 开头的 JAR。</p><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/storm-jar2.png"> </div><h2 id="五、其他打包需求"><a href="#五、其他打包需求" class="headerlink" title="五、其他打包需求"></a>五、其他打包需求</h2><h3 id="1-使用非Maven仓库中的Jar"><a href="#1-使用非Maven仓库中的Jar" class="headerlink" title="1. 使用非Maven仓库中的Jar"></a>1. 使用非Maven仓库中的Jar</h3><p>通常上面两种打包能够满足大多数的使用场景。但是如果你想把某些没有被 Maven 管理 JAR 包打入到最终的 JAR 中，比如你在 <code>resources/lib</code> 下引入的其他非 Maven 仓库中的 JAR，此时可以使用 <code>maven-jar-plugin</code> 和 <code>maven-dependency-plugin</code> 插件将其打入最终的 JAR 中。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-jar-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">archive</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">manifest</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">addClasspath</span>&gt;</span>true<span class="tag">&lt;/<span class="name">addClasspath</span>&gt;</span></span><br><span class="line">                          <span class="comment">&lt;!--指定 resources/lib 目录--&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">classpathPrefix</span>&gt;</span>lib/<span class="tag">&lt;/<span class="name">classpathPrefix</span>&gt;</span></span><br><span class="line">                          <span class="comment">&lt;!--应用的主入口类--&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">mainClass</span>&gt;</span>com.heibaiying.BigDataApp<span class="tag">&lt;/<span class="name">mainClass</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">manifest</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">archive</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-dependency-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">executions</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">execution</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">id</span>&gt;</span>copy<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">phase</span>&gt;</span>compile<span class="tag">&lt;/<span class="name">phase</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">goals</span>&gt;</span></span><br><span class="line">                         <span class="comment">&lt;!--将 resources/lib 目录所有 Jar 包打进最终的依赖中--&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">goal</span>&gt;</span>copy-dependencies<span class="tag">&lt;/<span class="name">goal</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">goals</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                         <span class="comment">&lt;!--将 resources/lib 目录所有 Jar 包一并拷贝到输出目录的 lib 目录下--&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">outputDirectory</span>&gt;</span></span><br><span class="line">                            $&#123;project.build.directory&#125;/lib</span><br><span class="line">                        <span class="tag">&lt;/<span class="name">outputDirectory</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">execution</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">executions</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="2-排除集群中已经存在的Jar"><a href="#2-排除集群中已经存在的Jar" class="headerlink" title="2. 排除集群中已经存在的Jar"></a>2. 排除集群中已经存在的Jar</h3><p>通常为了避免冲突，官方文档都会建议你排除集群中已经提供的 JAR 包，如下：</p><p>Spark 官方文档 Submitting Applications 章节: </p><blockquote><p>When creating assembly jars, list Spark and Hadoop as <code>provided</code> dependencies; these need not be bundled since they are provided by the cluster manager at runtime.</p></blockquote><p>Strom 官方文档 Running Topologies on a Production Cluster 章节：</p><blockquote><p>Then run mvn assembly:assembly to get an appropriately packaged jar. Make sure you exclude the Storm jars since the cluster already has Storm on the classpath.</p></blockquote><p>按照以上说明，排除 JAR 包的方式主要有两种：</p><ul><li>对需要排除的依赖添加 <code>&lt;scope&gt;provided&lt;/scope&gt;</code> 标签，此时该 JAR 包会被排除，但是不建议使用这种方式，因为此时你在本地运行也无法使用该 JAR 包；</li><li>建议直接在 <code>maven-assembly-plugin</code> 或 <code>maven-shade-plugin</code> 的配置文件中使用 <code>&lt;exclude&gt;</code> 进行排除。</li></ul><h3 id="3-打包Scala文件"><a href="#3-打包Scala文件" class="headerlink" title="3. 打包Scala文件"></a>3. 打包Scala文件</h3><p>如果你使用到 Scala 语言进行编程，此时需要特别注意 ：默认情况下 Maven 是不会把 <code>scala</code> 文件打入最终的 JAR 中，需要额外添加 <code>maven-scala-plugin</code> 插件，常用配置如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.scala-tools<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-scala-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.15.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">executions</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">execution</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">id</span>&gt;</span>scala-compile<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">goals</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">goal</span>&gt;</span>compile<span class="tag">&lt;/<span class="name">goal</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">goals</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">includes</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">include</span>&gt;</span>**/*.scala<span class="tag">&lt;/<span class="name">include</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">includes</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">execution</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">execution</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">id</span>&gt;</span>scala-test-compile<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">goals</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">goal</span>&gt;</span>testCompile<span class="tag">&lt;/<span class="name">goal</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">goals</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">execution</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">executions</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>关于 Maven 各个插件的详细配置可以查看其官方文档：</p><ul><li>maven-assembly-plugin : <a href="http://maven.apache.org/plugins/maven-assembly-plugin/" target="_blank" rel="noopener">http://maven.apache.org/plugins/maven-assembly-plugin/</a></li><li>maven-shade-plugin : <a href="http://maven.apache.org/plugins/maven-shade-plugin/" target="_blank" rel="noopener">http://maven.apache.org/plugins/maven-shade-plugin/</a></li><li>maven-jar-plugin : <a href="http://maven.apache.org/plugins/maven-jar-plugin/" target="_blank" rel="noopener">http://maven.apache.org/plugins/maven-jar-plugin/</a></li><li>maven-dependency-plugin : <a href="http://maven.apache.org/components/plugins/maven-dependency-plugin/" target="_blank" rel="noopener">http://maven.apache.org/components/plugins/maven-dependency-plugin/</a></li></ul><p>关于 maven-shade-plugin 的更多配置也可以参考该博客：  <a href="https://www.jianshu.com/p/7a0e20b30401" target="_blank" rel="noopener">maven-shade-plugin 入门指南</a></p>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="大数据" scheme="https://icocos.github.io/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="打包方式" scheme="https://icocos.github.io/tags/%E6%89%93%E5%8C%85%E6%96%B9%E5%BC%8F/"/>
    
  </entry>
  
  <entry>
    <title>大数据之资料与工具推荐</title>
    <link href="https://icocos.github.io/2019/12/01/%E5%A4%A7%E6%95%B0%E6%8D%AE%E4%B9%8B%E8%B5%84%E6%96%99%E4%B8%8E%E5%B7%A5%E5%85%B7%E6%8E%A8%E8%8D%90/"/>
    <id>https://icocos.github.io/2019/12/01/大数据之资料与工具推荐/</id>
    <published>2019-12-01T15:16:41.000Z</published>
    <updated>2020-04-09T14:45:08.092Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><p>这里分享一些自己学习过程中觉得不错的资料和开发工具。</p><h2 id="book-经典书籍"><a href="#book-经典书籍" class="headerlink" title=":book: 经典书籍"></a>:book: 经典书籍</h2><ul><li><a href="https://book.douban.com/subject/27115351/" target="_blank" rel="noopener">《hadoop 权威指南 (第四版)》</a> 2017 年</li><li><a href="https://book.douban.com/subject/27665114/" target="_blank" rel="noopener">《Kafka 权威指南》</a> 2017 年</li><li><a href="https://book.douban.com/subject/26292004/" target="_blank" rel="noopener">《从 Paxos 到 Zookeeper  分布式一致性原理与实践》</a>  2015 年</li></ul><ul><li><a href="https://book.douban.com/subject/26649141/" target="_blank" rel="noopener">《Spark 技术内幕  深入解析 Spark 内核架构设计与实现原理》</a> 2015 年</li><li><a href="https://book.douban.com/subject/27035127/" target="_blank" rel="noopener">《Spark.The.Definitive.Guide》</a> 2018 年</li><li><a href="https://book.douban.com/subject/10748460/" target="_blank" rel="noopener">《HBase 权威指南》</a> 2012 年</li><li><a href="https://book.douban.com/subject/25791255/" target="_blank" rel="noopener">《Hive 编程指南》</a> 2013 年</li><li><a href="https://book.douban.com/subject/27093751/" target="_blank" rel="noopener">《快学 Scala(第 2 版)》</a> 2017 年</li><li><a href="https://book.douban.com/subject/27591387/" target="_blank" rel="noopener">《Scala 编程》</a> 2018 年</li></ul><h2 id="computer-官方文档"><a href="#computer-官方文档" class="headerlink" title=":computer: 官方文档"></a>:computer: 官方文档</h2><p>上面的书籍我都列出了出版日期，可以看到大部分书籍的出版时间都比较久远了，虽然这些书籍比较经典，但是很多书籍在软件版本上已经滞后了很多。所以推荐优先选择各个框架的<strong>官方文档</strong>作为学习资料。大数据框架的官方文档都很全面，并且对知识点的讲解都做到了简明扼要。这里以 <a href="https://spark.apache.org/docs/latest/rdd-programming-guide.html" target="_blank" rel="noopener">Spark RDD 官方文档</a> 为例，你会发现不仅清晰的知识点导航，而且所有示例都给出了 Java，Scala，Python 三种语言的版本，除了官方文档，其他书籍很少能够做到这一点。</p><h2 id="orange-book-优秀博客"><a href="#orange-book-优秀博客" class="headerlink" title=":orange_book: 优秀博客"></a>:orange_book: 优秀博客</h2><ul><li>有态度的 HBase/Spark/BigData：<a href="http://hbasefly.com/" target="_blank" rel="noopener">http://hbasefly.com/</a></li><li>深入 Apache Spark 的设计和实现原理 ： <a href="https://github.com/JerryLead/SparkInternals" target="_blank" rel="noopener">https://github.com/JerryLead/SparkInternals</a></li><li>Jark’s Blog - Flink 系列文章：<a href="http://wuchong.me/categories/Flink/" target="_blank" rel="noopener">http://wuchong.me/categories/Flink/</a> </li></ul><h2 id="triangular-ruler-开发工具"><a href="#triangular-ruler-开发工具" class="headerlink" title=":triangular_ruler:开发工具"></a>:triangular_ruler:开发工具</h2><h4 id="1-VirtualBox"><a href="#1-VirtualBox" class="headerlink" title="1.  VirtualBox"></a>1.  VirtualBox</h4><p>一款开源、免费的虚拟机管理软件，虽然是轻量级软件，但功能很丰富，基本能够满足全部的使用需求。</p><p>官方网站：<a href="https://www.virtualbox.org/" target="_blank" rel="noopener">https://www.virtualbox.org/</a></p><h4 id="2-MobaXterm"><a href="#2-MobaXterm" class="headerlink" title="2. MobaXterm"></a>2. MobaXterm</h4><p>大数据的框架通常都部署在服务器上，这里推荐使用 MobaXterm 进行连接。同样是免费开源的，支持多种连接协议，支持拖拽上传文件，支持使用插件扩展。</p><p>官方网站：<a href="https://mobaxterm.mobatek.net/" target="_blank" rel="noopener">https://mobaxterm.mobatek.net/</a></p><h4 id="3-Translate-Man"><a href="#3-Translate-Man" class="headerlink" title="3. Translate Man"></a>3. Translate Man</h4><p>Translate Man 是一款浏览器上的翻译插件 (谷歌和火狐均支持)。它采用谷歌的翻译接口，准确性非常高，支持划词翻译，可以辅助进行官方文档的阅读。</p><h4 id="4-ProcessOn"><a href="#4-ProcessOn" class="headerlink" title="4. ProcessOn"></a>4. ProcessOn</h4><p>ProcessOn 式一个在线绘图平台，使用起来非常便捷，可以用于笔记或者博客配图的绘制。</p><p>官方网站：<a href="https://www.processon.com/" target="_blank" rel="noopener">https://www.processon.com/</a></p>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="大数据" scheme="https://icocos.github.io/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="资料" scheme="https://icocos.github.io/tags/%E8%B5%84%E6%96%99/"/>
    
      <category term="工具" scheme="https://icocos.github.io/tags/%E5%B7%A5%E5%85%B7/"/>
    
  </entry>
  
  <entry>
    <title>大数据常用软件安装指南</title>
    <link href="https://icocos.github.io/2019/11/17/%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B8%B8%E7%94%A8%E8%BD%AF%E4%BB%B6%E5%AE%89%E8%A3%85%E6%8C%87%E5%8D%97/"/>
    <id>https://icocos.github.io/2019/11/17/大数据常用软件安装指南/</id>
    <published>2019-11-17T13:19:57.000Z</published>
    <updated>2020-04-09T14:45:08.357Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h2 id="大数据常用软件安装指南"><a href="#大数据常用软件安装指南" class="headerlink" title="大数据常用软件安装指南"></a>大数据常用软件安装指南</h2><p>为方便大家查阅，本仓库所有软件的安装方式单独整理如下：</p><h3 id="一、基础软件安装"><a href="#一、基础软件安装" class="headerlink" title="一、基础软件安装"></a>一、基础软件安装</h3><ol><li><a href="installation/Linux下JDK安装.md">Linux 环境下 JDK 安装</a></li><li><a href="installation/Linux下Python安装.md">Linux 环境下 Python 安装</a></li><li><a href="installation/虚拟机静态IP及多IP配置.md">虚拟机静态 IP 及多 IP 配置</a></li></ol><h3 id="二、Hadoop"><a href="#二、Hadoop" class="headerlink" title="二、Hadoop"></a>二、Hadoop</h3><ol><li><a href="installation/Hadoop单机环境搭建.md">Hadoop 单机环境搭建</a></li><li><a href="installation/Hadoop集群环境搭建.md">Hadoop 集群环境搭建</a></li><li><a href="installation/基于Zookeeper搭建Hadoop高可用集群.md">基于 Zookeeper 搭建 Hadoop 高可用集群</a></li></ol><h3 id="三、Spark"><a href="#三、Spark" class="headerlink" title="三、Spark"></a>三、Spark</h3><ol><li><a href="installation/Spark开发环境搭建.md">Spark 开发环境搭建</a></li><li><a href="installation/Spark集群环境搭建.md">基于 Zookeeper 搭建 Spark 高可用集群</a></li></ol><h3 id="四、Flink"><a href="#四、Flink" class="headerlink" title="四、Flink"></a>四、Flink</h3><ol><li><a href="installation/Flink_Standalone_Cluster.md">Flink Standalone 集群部署</a></li></ol><h3 id="五、Storm"><a href="#五、Storm" class="headerlink" title="五、Storm"></a>五、Storm</h3><ol><li><a href="installation/Storm单机环境搭建.md">Storm 单机环境搭建</a></li><li><a href="installation/Storm集群环境搭建.md">Storm 集群环境搭建</a></li></ol><h3 id="六、HBase"><a href="#六、HBase" class="headerlink" title="六、HBase"></a>六、HBase</h3><ol><li><a href="installation/HBase单机环境搭建.md">HBase 单机环境搭建</a></li><li><a href="installation/HBase集群环境搭建.md">HBase 集群环境搭建</a></li></ol><h3 id="七、Flume"><a href="#七、Flume" class="headerlink" title="七、Flume"></a>七、Flume</h3><ol><li><a href="installation/Linux下Flume的安装.md">Linux 环境下 Flume 的安装部署</a></li></ol><h3 id="八、Azkaban"><a href="#八、Azkaban" class="headerlink" title="八、Azkaban"></a>八、Azkaban</h3><ol><li><a href="installation/Azkaban_3.x_编译及部署.md">Azkaban3.x 编译及部署</a></li></ol><h3 id="九、Hive"><a href="#九、Hive" class="headerlink" title="九、Hive"></a>九、Hive</h3><ol><li><a href="installation/Linux环境下Hive的安装部署.md">Linux 环境下 Hive 的安装部署</a></li></ol><h3 id="十、Zookeeper"><a href="#十、Zookeeper" class="headerlink" title="十、Zookeeper"></a>十、Zookeeper</h3><ol><li><a href="installation/Zookeeper单机环境和集群环境搭建.md">Zookeeper 单机环境和集群环境搭建</a> </li></ol><h3 id="十一、Kafka"><a href="#十一、Kafka" class="headerlink" title="十一、Kafka"></a>十一、Kafka</h3><ol><li><a href="installation/基于Zookeeper搭建Kafka高可用集群.md">基于 Zookeeper 搭建 Kafka 高可用集群</a></li></ol><h3 id="版本说明"><a href="#版本说明" class="headerlink" title="版本说明"></a>版本说明</h3><p>由于 Apache Hadoop 原有安装包之间兼容性比较差，所以如无特殊需求，本仓库一律选择 <strong>CDH</strong> (Cloudera’s Distribution, including Apache Hadoop) 版本的安装包。它基于稳定版本的 Apache Hadoop 构建，并做了兼容性测试，是目前生产环境中使用最为广泛的版本。</p><p>最新的 CDH 5 的下载地址为：<a href="http://archive.cloudera.com/cdh5/cdh/5/" target="_blank" rel="noopener">http://archive.cloudera.com/cdh5/cdh/5/</a>  。这个页面很大且加载速度比较慢，需要耐心等待页面加载完成。上半部分是文档链接，后半部分才是安装包。同一个 CDH 版本的不同框架间都做了集成测试，可以保证没有任何 JAR 包冲突。安装包包名通常如下所示，这里 CDH 版本都是 <code>5.15.2</code>  ，前面是各个软件自己的版本 ，未避免出现不必要的 JAR 包冲突，<strong>请务必保持 CDH 的版本一致</strong>。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">hadoop-2.6.0-cdh5.15.2.tar.gz </span><br><span class="line">hbase-1.2.0-cdh5.15.2</span><br><span class="line">hive-1.1.0-cdh5.15.2.tar.gz</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="大数据" scheme="https://icocos.github.io/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="软件安装" scheme="https://icocos.github.io/tags/%E8%BD%AF%E4%BB%B6%E5%AE%89%E8%A3%85/"/>
    
  </entry>
  
  <entry>
    <title>大数据完整学习目录</title>
    <link href="https://icocos.github.io/2019/11/12/%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%95%B4%E5%AD%A6%E4%B9%A0%E7%9B%AE%E5%BD%95/"/>
    <id>https://icocos.github.io/2019/11/12/大数据完整学习目录/</id>
    <published>2019-11-11T23:10:01.000Z</published>
    <updated>2020-04-09T14:45:08.473Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h2 id="大数据成神之路目录"><a href="#大数据成神之路目录" class="headerlink" title="大数据成神之路目录"></a>大数据成神之路目录</h2><h3 id="大数据开发基础篇"><a href="#大数据开发基础篇" class="headerlink" title="大数据开发基础篇"></a>大数据开发基础篇</h3><table><thead><tr><th style="text-align:center">Java基础</th><th style="text-align:center">NIO</th><th style="text-align:center">并发</th><th style="text-align:center">JVM</th><th style="text-align:center">分布式</th><th style="text-align:center">Zookeeper</th><th style="text-align:center">RPC</th><th style="text-align:center">Netty</th><th style="text-align:center">Linux</th></tr></thead><tbody><tr><td style="text-align:center"><a href="#一Java基础">Java基础</a></td><td style="text-align:center"><a href="#二NIO基础">NIO</a></td><td style="text-align:center"><a href="#三Java并发容器">并发容器</a></td><td style="text-align:center"><a href="#四JVM深度解析和面试点">JVM</a></td><td style="text-align:center"><a href="#五分布式理论基础和原理">分布式</a></td><td style="text-align:center"><a href="#六大数据框架开发基础-zookeeper">zookeeper</a></td><td style="text-align:center"><a href="#七大数据框架开发基础-RPC">RPC</a></td><td style="text-align:center"><a href="#八大数据框架基石之网路通信-Netty">Netty</a></td><td style="text-align:center"><a href="/九Linux基础/Linux基础和命令.md">Linux</a></td></tr></tbody></table><p><br></p><h3 id="大数据框架学习篇"><a href="#大数据框架学习篇" class="headerlink" title="大数据框架学习篇"></a>大数据框架学习篇</h3><p><table><br>    <tr><br>      <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/hadoop.jpg"></th><br>      <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/hive.jpg"></th><br>      <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/spark.jpg"></th><br>      <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/flink.png"></th><br>      <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/hbase.png"></th><br>      <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/kafka.png"></th><br>      <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/zookeeper.jpg"></th><br>      <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/flume.png"></th><br>      <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/sqoop.png"></th><br>      <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/azkaban.png"></th><br>    </tr><br>    <tr><br>      <td align="center"><a href="#一hadoop">Hadoop</a></td><br>      <td align="center"><a href="#二hive">Hive</a></td><br>      <td align="center"><a href="#三spark">Spark</a></td><br>      <td align="center"><a href="#四flink">Flink</a></td><br>      <td align="center"><a href="#五hbase">HBase</a></td><br>      <td align="center"><a href="#六kafka">Kafka</a></td><br>      <td align="center"><a href="#七zookeeper">Zookeeper</a></td><br>      <td align="center"><a href="#八flume">Flume</a></td><br>      <td align="center"><a href="#九sqoop">Sqoop</a></td><br>      <td align="center"><a href="#十azkaban">Azkaban</a></td><br>    </tr><br>  </table><br><br></p><h3 id="大数据开发实战进阶篇"><a href="#大数据开发实战进阶篇" class="headerlink" title="大数据开发实战进阶篇"></a>大数据开发实战进阶篇</h3><p>这里的文章主要是我平时发表在公众号，博客等的文章，精心挑选，以飨读者。</p><p><table><br>    <tr><br>      <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/flink.png"></th><br>      <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/spark.jpg"></th><br>      <th><img width="50px" src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/kafka.png"></th><br>    </tr><br>    <tr><br>      <td align="center"><a href="#Flink实战进阶文章合集">Flink实战进阶</a></td><br>      <td align="center"><a href="#Spark实战进阶文章合集">Spark实战进阶</a></td><br>      <td align="center"><a href="#Kafka实战进阶文章合集">Kafka实战进阶</a></td><br>    </tr><br>  </table><br><br></p><h2 id="第一部分-大数据开发基础篇"><a href="#第一部分-大数据开发基础篇" class="headerlink" title="第一部分: 大数据开发基础篇"></a>第一部分: 大数据开发基础篇</h2><h3 id="一、Java基础"><a href="#一、Java基础" class="headerlink" title="一、Java基础"></a>一、Java基础</h3><ul><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(%E5%A4%9A%E7%BA%BF%E7%A8%8B" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(多线程)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(Synchronized%E5%85%B3%E9%94%AE%E5%AD%97" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(Synchronized关键字)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(volatile%E5%85%B3%E9%94%AE%E5%AD%97" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(volatile关键字)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(%E9%94%81" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(锁)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(ArrayList/Vector)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA.md" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(LinkedList)</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(HashMap" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(HashMap)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(HashSet" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(HashSet)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(LinkedHashMap" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(LinkedHashMap)</a>.md)</li></ul><h3 id="二、NIO基础"><a href="#二、NIO基础" class="headerlink" title="二、NIO基础"></a>二、NIO基础</h3><ul><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA-NIO.md" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强-NIO大纲</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/NIO%E6%A6%82%E8%A7%88.md" target="_blank" rel="noopener">NIO概览</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/Java%20NIO%E4%B9%8BBuffer(%E7%BC%93%E5%86%B2%E5%8C%BA" target="_blank" rel="noopener">Java NIO之Buffer(缓冲区)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/Java%20NIO%E4%B9%8BChannel(%E9%80%9A%E9%81%93" target="_blank" rel="noopener">Java NIO之Channel(通道)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/Java%20NIO%E4%B9%8BSelector(%E9%80%89%E6%8B%A9%E5%99%A8" target="_blank" rel="noopener">ava NIO之Selector(选择器)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA/Java%20NIO%E4%B9%8B%E6%8B%A5%E6%8A%B1Path%E5%92%8CFiles.md" target="_blank" rel="noopener">Java NIO之拥抱Path和Files</a></li></ul><h3 id="三、Java并发容器"><a href="#三、Java并发容器" class="headerlink" title="三、Java并发容器"></a>三、Java并发容器</h3><ul><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%B9%B6%E5%8F%91%E5%AE%B9%E5%99%A8/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(%E5%B9%B6%E5%8F%91%E5%AE%B9%E5%99%A8%E5%A4%A7%E7%BA%B2" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(并发容器大纲)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%B9%B6%E5%8F%91%E5%AE%B9%E5%99%A8/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(LinkedBlockingQueue" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(LinkedBlockingQueue)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%B9%B6%E5%8F%91%E5%AE%B9%E5%99%A8/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(LinkedBlockingDeque" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(LinkedBlockingDeque)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%B9%B6%E5%8F%91%E5%AE%B9%E5%99%A8/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(CopyOnWriteArraySet" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(CopyOnWriteArraySet)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%B9%B6%E5%8F%91%E5%AE%B9%E5%99%A8/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(CopyOnWriteArrayList" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(CopyOnWriteArrayList)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%B9%B6%E5%8F%91%E5%AE%B9%E5%99%A8/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(ConcurrentSkipListSet" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(ConcurrentSkipListSet)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%B9%B6%E5%8F%91%E5%AE%B9%E5%99%A8/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(ConcurrentSkipListMap" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(ConcurrentSkipListMap)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%B9%B6%E5%8F%91%E5%AE%B9%E5%99%A8/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(ConcurrentLinkedQueue" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(ConcurrentLinkedQueue)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%B9%B6%E5%8F%91%E5%AE%B9%E5%99%A8/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(ConcurrentHashMap" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(ConcurrentHashMap)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%B9%B6%E5%8F%91%E5%AE%B9%E5%99%A8/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%88%90%E7%A5%9E%E4%B9%8B%E8%B7%AF-Java%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%A2%9E%E5%BC%BA(ArrayBlockingQueue" target="_blank" rel="noopener">大数据成神之路-Java高级特性增强(ArrayBlockingQueue)</a>.md)</li></ul><h3 id="四、JVM深度解析和面试点"><a href="#四、JVM深度解析和面试点" class="headerlink" title="四、JVM深度解析和面试点"></a>四、JVM深度解析和面试点</h3><h5 id="先来10篇基础热身"><a href="#先来10篇基础热身" class="headerlink" title="先来10篇基础热身"></a>先来10篇基础热身</h5><ul><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/JVM%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.md" target="_blank" rel="noopener">JVM内存结构</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/HotSpot%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%AF%B9%E8%B1%A1%E6%8E%A2%E7%A7%98.md" target="_blank" rel="noopener">HotSpot虚拟机对象探秘</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E7%AD%96%E7%95%A5%E4%B8%8E%E7%AE%97%E6%B3%95.md" target="_blank" rel="noopener">垃圾收集策略与算法</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/HotSpot%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8.md" target="_blank" rel="noopener">HotSpot垃圾收集器</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E4%B8%8E%E5%9B%9E%E6%94%B6%E7%AD%96%E7%95%A5.md" target="_blank" rel="noopener">内存分配与回收策略</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/JVM%20%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98.md" target="_blank" rel="noopener">JVM性能调优</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/%E7%B1%BB%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84.md" target="_blank" rel="noopener">类文件结构</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E7%9A%84%E6%97%B6%E6%9C%BA.md" target="_blank" rel="noopener">类加载的时机</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E7%9A%84%E8%BF%87%E7%A8%8B.md" target="_blank" rel="noopener">类加载的过程</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8.md" target="_blank" rel="noopener">类加载器</a></li></ul><h5 id="再来5篇详细解说"><a href="#再来5篇详细解说" class="headerlink" title="再来5篇详细解说"></a>再来5篇详细解说</h5><ul><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/jvm%E7%B3%BB%E5%88%97(%E4%B8%80" target="_blank" rel="noopener">java类的加载机制</a>java%E7%B1%BB%E7%9A%84%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/jvm%E7%B3%BB%E5%88%97(%E4%BA%8C" target="_blank" rel="noopener">JVM内存结构</a>JVM%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/jvm%E7%B3%BB%E5%88%97(%E4%B8%89" target="_blank" rel="noopener">GC算法 垃圾收集器</a>GC%E7%AE%97%E6%B3%95%20%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/jvm%E7%B3%BB%E5%88%97(%E5%9B%9B" target="_blank" rel="noopener">jvm调优-命令大全</a>jvm%E8%B0%83%E4%BC%98-%E5%91%BD%E4%BB%A4%E5%A4%A7%E5%85%A8%EF%BC%88jps%20jstat%20jmap%20jhat%20jstack%20jinfo%EF%BC%89.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/JVM/jvm%E7%B3%BB%E5%88%97(%E4%BA%94" target="_blank" rel="noopener">Java GC 分析</a>Java%20GC%20%E5%88%86%E6%9E%90.md)</li></ul><h3 id="五、分布式理论基础和原理"><a href="#五、分布式理论基础和原理" class="headerlink" title="五、分布式理论基础和原理"></a>五、分布式理论基础和原理</h3><ul><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E7%9A%84%E4%B8%80%E4%BA%9B%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5.md" target="_blank" rel="noopener">分布式系统的一些基本概念</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80%E4%B8%80%EF%BC%9A%20%E4%B8%80%E8%87%B4%E6%80%A7%E3%80%812PC%E5%92%8C3PC.md" target="_blank" rel="noopener">分布式系统理论基础一： 一致性、2PC和3PC</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80%E4%BA%8C-CAP.md" target="_blank" rel="noopener">分布式系统理论基础二-CAP</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80%E4%B8%89-%E6%97%B6%E9%97%B4%E3%80%81%E6%97%B6%E9%92%9F%E5%92%8C%E4%BA%8B%E4%BB%B6%E9%A1%BA%E5%BA%8F.md" target="_blank" rel="noopener">分布式系统理论基础三-时间、时钟和事件顺序</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E7%90%86%E8%AE%BA%E8%BF%9B%E9%98%B6%20-%20Paxos.md" target="_blank" rel="noopener">分布式系统理论进阶 - Paxos</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E7%90%86%E8%AE%BA%E8%BF%9B%E9%98%B6%20-%20Raft%E3%80%81Zab.md" target="_blank" rel="noopener">分布式系统理论进阶 - Raft、Zab</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E7%90%86%E8%AE%BA%E8%BF%9B%E9%98%B6%EF%BC%9A%E9%80%89%E4%B8%BE%E3%80%81%E5%A4%9A%E6%95%B0%E6%B4%BE%E5%92%8C%E7%A7%9F%E7%BA%A6.md" target="_blank" rel="noopener">分布式系统理论进阶：选举、多数派和租约</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88.md" target="_blank" rel="noopener">分布式锁的解决方案</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88(%E4%BA%8C" target="_blank" rel="noopener">分布式锁的解决方案(二)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88.md" target="_blank" rel="noopener">分布式事务的解决方案</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA/%E5%88%86%E5%B8%83%E5%BC%8FID%E7%94%9F%E6%88%90%E5%99%A8%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88.md" target="_blank" rel="noopener">分布式ID生成器解决方案</a></li></ul><h3 id="六、大数据框架开发基础-Zookeeper"><a href="#六、大数据框架开发基础-Zookeeper" class="headerlink" title="六、大数据框架开发基础-Zookeeper"></a>六、大数据框架开发基础-Zookeeper</h3><ul><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/zookeeeper/zk%E5%AE%89%E8%A3%85%E5%92%8C%E8%BF%90%E8%A1%8C.md" target="_blank" rel="noopener">安装和运行</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/zookeeeper/zk%E6%9C%8D%E5%8A%A1.md" target="_blank" rel="noopener">zookeeper服务</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/zookeeeper/ZooKeeper%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F.md" target="_blank" rel="noopener">zookeeper应用程序</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/zookeeeper/zk%E5%BC%80%E5%8F%91%E5%AE%9E%E4%BE%8B.md" target="_blank" rel="noopener">zookeeper开发实例</a></li><li><a href="http://www.importnew.com/23237.html" target="_blank" rel="noopener">zookeeper集群构建</a></li></ul><h3 id="七、大数据框架开发基础-RPC"><a href="#七、大数据框架开发基础-RPC" class="headerlink" title="七、大数据框架开发基础-RPC"></a>七、大数据框架开发基础-RPC</h3><ul><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/RPC/RPC%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D.md" target="_blank" rel="noopener">RPC简单介绍</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/RPC/RPC%E7%9A%84%E5%8E%9F%E7%90%86%E5%92%8C%E6%A1%86%E6%9E%B6.md" target="_blank" rel="noopener">RPC的原理和框架</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/RPC/%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84RPC.md" target="_blank" rel="noopener">手把手教你实现一个简单的RPC</a></li></ul><h3 id="八、大数据框架基石之网路通信-Netty"><a href="#八、大数据框架基石之网路通信-Netty" class="headerlink" title="八、大数据框架基石之网路通信-Netty"></a>八、大数据框架基石之网路通信-Netty</h3><ul><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Netty/%E5%85%B3%E4%BA%8ENetty%E6%88%91%E4%BB%AC%E9%83%BD%E9%9C%80%E8%A6%81%E7%9F%A5%E9%81%93%E4%BB%80%E4%B9%88.md" target="_blank" rel="noopener">关于Netty我们都需要知道什么</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Netty/Netty%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-%E6%A6%82%E8%BF%B0%E7%AF%87.md" target="_blank" rel="noopener">Netty源码解析-概述篇</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Netty/Netty%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%901-Buffer.md" target="_blank" rel="noopener">Netty源码解析1-Buffer</a> </li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Netty/Netty%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%902-Reactor.md" target="_blank" rel="noopener">Netty源码解析2-Reactor</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Netty/Netty%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%903-Pipeline.md" target="_blank" rel="noopener">Netty源码解析3-Pipeline</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Netty/Netty%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%904-Handler%E7%BB%BC%E8%BF%B0.md" target="_blank" rel="noopener">Netty源码解析4-Handler综述</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Netty/Netty%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%905-ChannelHandler.md" target="_blank" rel="noopener">Netty源码解析5-ChannelHandler</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Netty/Netty%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%906-ChannelHandler%E5%AE%9E%E4%BE%8B%E4%B9%8BLoggingHandler.md" target="_blank" rel="noopener">Netty源码解析6-ChannelHandler实例之LoggingHandler</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Netty/Netty%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%907-ChannelHandler%E5%AE%9E%E4%BE%8B%E4%B9%8BTimeoutHandler.md" target="_blank" rel="noopener">Netty源码解析7-ChannelHandler实例之TimeoutHandler</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Netty/Netty%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%908-ChannelHandler%E5%AE%9E%E4%BE%8B%E4%B9%8BCodecHandler.md" target="_blank" rel="noopener">Netty源码解析8-ChannelHandler实例之CodecHandler</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Netty/Netty%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%909-ChannelHandler%E5%AE%9E%E4%BE%8B%E4%B9%8BMessageToByteEncoder.md" target="_blank" rel="noopener">Netty源码解析9-ChannelHandler实例之MessageToByteEncoder</a></li></ul><h2 id="第二部分-大数据框架学习篇"><a href="#第二部分-大数据框架学习篇" class="headerlink" title="第二部分:大数据框架学习篇"></a>第二部分:大数据框架学习篇</h2><p>本部分引用了Bigdata-Notes的文章，作者是heibaiying，大佬写的文章非常好，欢迎大家关注他的博客。<br>我个人会持续补充更有深度和实战性的文章~</p><h3 id="一、Hadoop"><a href="#一、Hadoop" class="headerlink" title="一、Hadoop"></a>一、Hadoop</h3><ol><li><a href="大数据框架学习/Hadoop-HDFS.md">分布式文件存储系统 —— HDFS</a></li><li><a href="大数据框架学习/Hadoop-MapReduce.md">分布式计算框架 —— MapReduce</a></li><li><a href="大数据框架学习/Hadoop-YARN.md">集群资源管理器 —— YARN</a></li><li><a href="大数据框架学习/installation/Hadoop单机环境搭建.md">Hadoop 单机伪集群环境搭建</a></li><li><a href="大数据框架学习/installation/Hadoop集群环境搭建.md">Hadoop 集群环境搭建</a></li><li><a href="大数据框架学习/HDFS常用Shell命令.md">HDFS 常用 Shell 命令</a></li><li><a href="大数据框架学习/HDFS-Java-API.md">HDFS Java API 的使用</a></li><li><a href="大数据框架学习/installation/基于Zookeeper搭建Hadoop高可用集群.md">基于 Zookeeper 搭建 Hadoop 高可用集群</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Hadoop/Hadoop%E6%9E%81%E7%AE%80%E5%85%A5%E9%97%A8.md" target="_blank" rel="noopener">Hadoop级简入门</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Hadoop/MapReduce%E7%BC%96%E7%A8%8B%E6%A8%A1%E5%9E%8B%E5%92%8C%E8%AE%A1%E7%AE%97%E6%A1%86%E6%9E%B6%E6%9E%B6%E6%9E%84%E5%8E%9F%E7%90%86.md" target="_blank" rel="noopener">MapReduce编程模型和计算框架架构原理</a></li></ol><h3 id="二、Hive"><a href="#二、Hive" class="headerlink" title="二、Hive"></a>二、Hive</h3><ol><li><a href="大数据框架学习/Hive简介及核心概念.md">Hive 简介及核心概念</a></li><li><a href="大数据框架学习/installation/Linux环境下Hive的安装部署.md">Linux 环境下 Hive 的安装部署</a></li><li><a href="大数据框架学习/HiveCLI和Beeline命令行的基本使用.md">Hive CLI 和 Beeline 命令行的基本使用</a></li><li><a href="大数据框架学习/Hive常用DDL操作.md">Hive 常用 DDL 操作</a></li><li><a href="大数据框架学习/Hive分区表和分桶表.md">Hive 分区表和分桶表</a></li><li><a href="大数据框架学习/Hive视图和索引.md">Hive 视图和索引</a></li><li><a href="大数据框架学习/Hive常用DML操作.md">Hive常用 DML 操作</a></li><li><a href="大数据框架学习/Hive数据查询详解.md">Hive 数据查询详解</a></li></ol><h3 id="三、Spark"><a href="#三、Spark" class="headerlink" title="三、Spark"></a>三、Spark</h3><p><strong>Spark Core :</strong></p><ol><li><a href="大数据框架学习/Spark简介.md">Spark 简介</a></li><li><a href="大数据框架学习/installation/Spark开发环境搭建.md">Spark 开发环境搭建</a></li><li><a href="大数据框架学习/Spark_RDD.md">弹性式数据集 RDD</a></li><li><a href="大数据框架学习/Spark_Transformation和Action算子.md">RDD 常用算子详解</a></li><li><a href="大数据框架学习/Spark部署模式与作业提交.md">Spark 运行模式与作业提交</a></li><li><a href="大数据框架学习/Spark累加器与广播变量.md">Spark 累加器与广播变量</a></li><li><a href="大数据框架学习/installation/Spark集群环境搭建.md">基于 Zookeeper 搭建 Spark 高可用集群</a></li></ol><p><strong>Spark SQL :</strong></p><ol><li><a href="大数据框架学习/SparkSQL_Dataset和DataFrame简介.md">DateFrame 和 DataSet </a></li><li><a href="大数据框架学习/Spark_Structured_API的基本使用.md">Structured API 的基本使用</a></li><li><a href="大数据框架学习/SparkSQL外部数据源.md">Spark SQL 外部数据源</a></li><li><a href="大数据框架学习/SparkSQL常用聚合函数.md">Spark SQL 常用聚合函数</a></li><li><a href="大数据框架学习/SparkSQL联结操作.md">Spark SQL JOIN 操作</a></li></ol><p><strong>Spark Streaming ：</strong></p><ol><li><a href="大数据框架学习/Spark_Streaming与流处理.md">Spark Streaming 简介</a></li><li><a href="大数据框架学习/Spark_Streaming基本操作.md">Spark Streaming 基本操作</a></li><li><a href="大数据框架学习/Spark_Streaming整合Flume.md">Spark Streaming 整合 Flume</a></li><li><a href="大数据框架学习/Spark_Streaming整合Kafka.md">Spark Streaming 整合 Kafka</a></li></ol><h2 id="四、Flink"><a href="#四、Flink" class="headerlink" title="四、Flink"></a>四、Flink</h2><ol><li><a href="大数据框架学习/Flink核心概念综述.md">Flink 核心概念综述</a></li><li><a href="大数据框架学习/Flink开发环境搭建.md">Flink 开发环境搭建</a></li><li><a href="大数据框架学习/Flink_Data_Source.md">Flink Data Source</a></li><li><a href="大数据框架学习/Flink_Data_Transformation.md">Flink Data Transformation</a></li><li><a href="大数据框架学习/Flink_Data_Sink.md">Flink Data Sink</a></li><li><a href="大数据框架学习/Flink_Windows.md">Flink 窗口模型</a></li><li><a href="大数据框架学习/Flink状态管理与检查点机制.md">Flink 状态管理与检查点机制</a></li><li><a href="大数据框架学习/installation/Flink_Standalone_Cluster.md">Flink Standalone 集群部署</a></li></ol><h4 id="Flink当前最火的实时计算引擎-入门篇"><a href="#Flink当前最火的实时计算引擎-入门篇" class="headerlink" title="Flink当前最火的实时计算引擎-入门篇"></a>Flink当前最火的实时计算引擎-入门篇</h4><ul><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/Flink%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E6%94%BE%E5%BC%83(%E5%85%A5%E9%97%A8%E7%AF%871" target="_blank" rel="noopener">Flink从入门到放弃(入门篇1)-Flink是什么</a>-Flink%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/Flink%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E6%94%BE%E5%BC%83(%E5%85%A5%E9%97%A8%E7%AF%872" target="_blank" rel="noopener">Flink从入门到放弃(入门篇2)-本地环境搭建&amp;构建第一个Flink应用</a>-%E6%9C%AC%E5%9C%B0%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%26%E6%9E%84%E5%BB%BA%E7%AC%AC%E4%B8%80%E4%B8%AAFlink%E5%BA%94%E7%94%A8.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/Flink%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E6%94%BE%E5%BC%83(%E5%85%A5%E9%97%A8%E7%AF%873" target="_blank" rel="noopener">Flink从入门到放弃(入门篇3)-DataSetAPI</a>-DataSetAPI.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/Flink%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E6%94%BE%E5%BC%83(%E5%85%A5%E9%97%A8%E7%AF%874" target="_blank" rel="noopener">Flink从入门到放弃(入门篇4)-DataStreamAPI</a>-DataStreamAPI.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/Flink%E9%9B%86%E7%BE%A4%E9%83%A8%E7%BD%B2.md" target="_blank" rel="noopener">Flink集群部署</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/6-Flink%E9%87%8D%E5%90%AF%E7%AD%96%E7%95%A5.md" target="_blank" rel="noopener">Flink重启策略</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/7-Flink%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98.md" target="_blank" rel="noopener">Flink的分布式缓存</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/8-Flink%E4%B8%AD%E7%9A%84%E7%AA%97%E5%8F%A3.md" target="_blank" rel="noopener">Flink中的窗口</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/9-Flink%E4%B8%AD%E7%9A%84Time.md" target="_blank" rel="noopener">Flink中的Time</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/10-Flink%E9%9B%86%E7%BE%A4%E7%9A%84%E9%AB%98%E5%8F%AF%E7%94%A8(%E6%90%AD%E5%BB%BA%E7%AF%87%E8%A1%A5%E5%85%85" target="_blank" rel="noopener">Flink集群搭建的HA</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/11-%E6%97%B6%E9%97%B4%E6%88%B3%E5%92%8C%E6%B0%B4%E5%8D%B0.md" target="_blank" rel="noopener">Flink中的时间戳和水印</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/12-Broadcast%E5%B9%BF%E6%92%AD%E5%8F%98%E9%87%8F.md" target="_blank" rel="noopener">Flink广播变量</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/13-Flink-Kafka-Connector.md" target="_blank" rel="noopener">Flink-Kafka-Connector</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/14-Flink-Table-%26-SQL.md" target="_blank" rel="noopener">Flink-Table-&amp;-SQL实战</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/15-Flink%E5%AE%9E%E6%88%98%E9%A1%B9%E7%9B%AE%E4%B9%8B%E5%AE%9E%E6%97%B6%E7%83%AD%E9%94%80%E6%8E%92%E8%A1%8C.md" target="_blank" rel="noopener">15-Flink实战项目之实时热销排行</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/16-Flink-Redis-Sink.md" target="_blank" rel="noopener">16-Flink-Redis-Sink</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink/17-Flink%E6%B6%88%E8%B4%B9Kafka%E5%86%99%E5%85%A5Mysql.md" target="_blank" rel="noopener">17-Flink消费Kafka写入Mysql</a></li></ul><h4 id="Flink当前最火的实时计算引擎-放弃篇"><a href="#Flink当前最火的实时计算引擎-放弃篇" class="headerlink" title="Flink当前最火的实时计算引擎-放弃篇"></a>Flink当前最火的实时计算引擎-放弃篇</h4><ul><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink%E6%BC%AB%E8%B0%88%E7%B3%BB%E5%88%97/Apache-Flink%E6%BC%AB%E8%B0%88%E7%B3%BB%E5%88%97(1" target="_blank" rel="noopener">Flink漫谈系列1-概述</a>-%E6%A6%82%E8%BF%B0.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink%E6%BC%AB%E8%B0%88%E7%B3%BB%E5%88%97/Apache-Flink-%E6%BC%AB%E8%B0%88%E7%B3%BB%E5%88%97(02" target="_blank" rel="noopener">Flink漫谈系列2-watermark</a>-Watermark.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Flink%E6%BC%AB%E8%B0%88%E7%B3%BB%E5%88%97/Apache-Flink-%E6%BC%AB%E8%B0%88%E7%B3%BB%E5%88%97(03" target="_blank" rel="noopener">Flink漫谈系列3-state</a>-State.md)</li></ul><h2 id="五、HBase"><a href="#五、HBase" class="headerlink" title="五、HBase"></a>五、HBase</h2><ol><li><a href="大数据框架学习/Hbase简介.md">Hbase 简介</a></li><li><a href="大数据框架学习/Hbase系统架构及数据结构.md">HBase 系统架构及数据结构</a></li><li><a href="大数据框架学习/installation/HBase单机环境搭建.md">HBase 基本环境搭建 (Standalone /pseudo-distributed mode)</a></li><li><a href="大数据框架学习/installation/HBase集群环境搭建.md">HBase 集群环境搭建</a></li><li><a href="大数据框架学习/Hbase_Shell.md">HBase 常用 Shell 命令</a></li><li><a href="大数据框架学习/Hbase_Java_API.md">HBase Java API</a></li><li><a href="大数据框架学习/Hbase过滤器详解.md">Hbase 过滤器详解</a></li><li><a href="大数据框架学习/Hbase协处理器详解.md">HBase 协处理器详解</a></li><li><a href="大数据框架学习/Hbase容灾与备份.md">HBase 容灾与备份</a></li><li><a href="大数据框架学习/Hbase的SQL中间层_Phoenix.md">HBase的 SQL 中间层 —— Phoenix</a></li><li><a href="大数据框架学习/Spring+Mybtais+Phoenix整合.md">Spring/Spring Boot 整合 Mybatis + Phoenix</a></li></ol><h2 id="六、Kafka"><a href="#六、Kafka" class="headerlink" title="六、Kafka"></a>六、Kafka</h2><p><strong>Kafka基本原理 ：</strong></p><ol><li><a href="大数据框架学习/Kafka简介.md">Kafka 简介</a></li><li><a href="大数据框架学习/installation/基于Zookeeper搭建Kafka高可用集群.md">基于 Zookeeper 搭建 Kafka 高可用集群</a></li><li><a href="大数据框架学习/Kafka生产者详解.md">Kafka 生产者详解</a></li><li><a href="大数据框架学习/Kafka消费者详解.md">Kafka 消费者详解</a></li><li><a href="大数据框架学习/Kafka深入理解分区副本机制.md">深入理解 Kafka 副本机制</a></li></ol><p><strong>分布式消息队列Kafka原理及与流式计算的集成 ：</strong></p><ol><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Kafka/Apache-Kafka%E7%AE%80%E4%BB%8B.md" target="_blank" rel="noopener">Apache-Kafka简介</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Kafka/Apache-Kafka%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5.md" target="_blank" rel="noopener">Apache-Kafka核心概念</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Kafka/Apache-Kafka%E5%AE%89%E8%A3%85%E5%92%8C%E4%BD%BF%E7%94%A8.md" target="_blank" rel="noopener">Apache-Kafka安装和使用</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Kafka/Apache-Kafka%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98.md" target="_blank" rel="noopener">Apache-Kafka编程实战</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Kafka/Apache-Kafka%E6%A0%B8%E5%BF%83%E7%BB%84%E4%BB%B6%E5%92%8C%E6%B5%81%E7%A8%8B(%E5%89%AF%E6%9C%AC%E7%AE%A1%E7%90%86%E5%99%A8" target="_blank" rel="noopener">Apache-Kafka核心组件和流程(副本管理器)</a>.md)</li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Kafka/Apache-Kafka%E6%A0%B8%E5%BF%83%E7%BB%84%E4%BB%B6%E5%92%8C%E6%B5%81%E7%A8%8B-%E5%8D%8F%E8%B0%83%E5%99%A8.md" target="_blank" rel="noopener">Apache-Kafka核心组件和流程-协调器</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Kafka/Apache-Kafka%E6%A0%B8%E5%BF%83%E7%BB%84%E4%BB%B6%E5%92%8C%E6%B5%81%E7%A8%8B-%E6%8E%A7%E5%88%B6%E5%99%A8.md" target="_blank" rel="noopener">Apache-Kafka核心组件和流程-控制器</a></li><li><a href="https://github.com/wangzhiwubigdata/God-Of-BigData/blob/master/Kafka/Apache-Kafka%E6%A0%B8%E5%BF%83%E7%BB%84%E4%BB%B6%E5%92%8C%E6%B5%81%E7%A8%8B-%E6%97%A5%E5%BF%97%E7%AE%A1%E7%90%86%E5%99%A8.md" target="_blank" rel="noopener">Apache-Kafka核心组件和流程-日志管理器</a></li></ol><h2 id="七、Zookeeper"><a href="#七、Zookeeper" class="headerlink" title="七、Zookeeper"></a>七、Zookeeper</h2><ol><li><a href="大数据框架学习/Zookeeper简介及核心概念.md">Zookeeper 简介及核心概念</a></li><li><a href="大数据框架学习/installation/Zookeeper单机环境和集群环境搭建.md">Zookeeper 单机环境和集群环境搭建</a> </li><li><a href="大数据框架学习/Zookeeper常用Shell命令.md">Zookeeper 常用 Shell 命令</a></li><li><a href="大数据框架学习/Zookeeper_Java客户端Curator.md">Zookeeper Java 客户端 —— Apache Curator</a></li><li><a href="大数据框架学习/Zookeeper_ACL权限控制.md">Zookeeper  ACL 权限控制</a></li></ol><h2 id="八、Flume"><a href="#八、Flume" class="headerlink" title="八、Flume"></a>八、Flume</h2><ol><li><a href="大数据框架学习/Flume简介及基本使用.md">Flume 简介及基本使用</a></li><li><a href="大数据框架学习/installation/Linux下Flume的安装.md">Linux 环境下 Flume 的安装部署</a></li><li><a href="大数据框架学习/Flume整合Kafka.md">Flume 整合 Kafka</a></li></ol><h2 id="九、Sqoop"><a href="#九、Sqoop" class="headerlink" title="九、Sqoop"></a>九、Sqoop</h2><ol><li><a href="大数据框架学习/Sqoop简介与安装.md">Sqoop 简介与安装</a></li><li><a href="大数据框架学习/Sqoop基本使用.md">Sqoop 的基本使用</a></li></ol><h2 id="十、Azkaban"><a href="#十、Azkaban" class="headerlink" title="十、Azkaban"></a>十、Azkaban</h2><ol><li><a href="大数据框架学习/Azkaban简介.md">Azkaban 简介</a></li><li><a href="大数据框架学习/installation/Azkaban_3.x_编译及部署.md">Azkaban3.x 编译及部署</a></li><li><a href="大数据框架学习/Azkaban_Flow_1.0_的使用.md">Azkaban Flow 1.0 的使用</a></li><li><a href="大数据框架学习/Azkaban_Flow_2.0_的使用.md">Azkaban Flow 2.0 的使用</a></li></ol><h2 id="第三部分-大数据开发实战进阶篇"><a href="#第三部分-大数据开发实战进阶篇" class="headerlink" title="第三部分:大数据开发实战进阶篇"></a>第三部分:大数据开发实战进阶篇</h2><h3 id="Flink实战进阶文章合集"><a href="#Flink实战进阶文章合集" class="headerlink" title="Flink实战进阶文章合集"></a>Flink实战进阶文章合集</h3><ol><li><a href="https://mp.weixin.qq.com/s/fnx2GnbCWNcaptVPsSp7dw" target="_blank" rel="noopener">菜鸟供应链实时技术架构演进</a></li><li><a href="https://mp.weixin.qq.com/s/s6YFOINMw9TKg-QVOkZT9A" target="_blank" rel="noopener">趣头条实战-基于Flink+ClickHouse构建实时数据平台</a></li><li><a href="https://mp.weixin.qq.com/s/NLwYpjzNgkR8O5zRg7RCoQ" target="_blank" rel="noopener">ApacheFlink新场景-OLAP引擎</a></li><li><a href="https://mp.weixin.qq.com/s/d_jzHb-b7LEGNz1CN34zMg" target="_blank" rel="noopener">说说Flink DataStream的八种物理分区逻辑</a></li><li><a href="https://mp.weixin.qq.com/s/eHPQx3kGKnhXeZpLhUNvng" target="_blank" rel="noopener">State Processor API：如何读取，写入和修改 Flink 应用程序的状态</a></li><li><a href="https://mp.weixin.qq.com/s/Q4k0xgPCOUQ-A2DQ-XaJgw" target="_blank" rel="noopener">Flink滑动窗口原理与细粒度滑动窗口的性能问题</a></li><li><a href="https://mp.weixin.qq.com/s/Ppz5740WTB7lTTLHNL72Tg" target="_blank" rel="noopener">基于Flink快速开发实时TopN</a></li><li><a href="https://mp.weixin.qq.com/s/kLjEceslHQxDDi3mRrjR-g" target="_blank" rel="noopener">使用 Apache Flink 开发实时 ETL</a></li><li><a href="https://mp.weixin.qq.com/s/kZQRQBVjYiXKfMhKM7SSqQ" target="_blank" rel="noopener">Flink Source/Sink探究与实践：RocketMQ数据写入HBase</a></li><li><a href="https://mp.weixin.qq.com/s/GDylIWCDnjpX9_X6T9NmMA" target="_blank" rel="noopener">Spark/Flink广播实现作业配置动态更新</a></li><li><a href="https://mp.weixin.qq.com/s/A6CIPsGf-aCWXkB7O-toVw" target="_blank" rel="noopener">Flink全链路延迟的测量方式</a></li><li><a href="https://mp.weixin.qq.com/s/5BlCzguYiEP1h48jwkos2w" target="_blank" rel="noopener">Flink原理-Flink中的数据抽象及数据交换过程</a></li><li><a href="https://mp.weixin.qq.com/s/UkpkS_JiRGR0ibZKYechbg" target="_blank" rel="noopener">Flink SQL Window源码全解析</a></li><li><a href="https://mp.weixin.qq.com/s/e-lyViKV4NPmOVwA5Jn6Qw" target="_blank" rel="noopener">Flink DataStream维度表Join的简单方案</a></li><li><a href="https://mp.weixin.qq.com/s/cBMrF814jGtEFdve0Lrr6g" target="_blank" rel="noopener">Apache Flink的内存管理</a></li><li><a href="https://mp.weixin.qq.com/s/e0BQoY5Y79NHhcQ9MqltFQ" target="_blank" rel="noopener">Flink1.9整合Kafka实战</a></li><li><a href="https://mp.weixin.qq.com/s/KbhmJCW80UmeFwRxM3jerg" target="_blank" rel="noopener">Apache Flink在小米的发展和应用</a></li><li><a href="https://mp.weixin.qq.com/s/BPzOBz7oTfn2_yW8tevEEw" target="_blank" rel="noopener">基于Kafka+Flink+Redis的电商大屏实时计算案例</a></li><li><a href="https://mp.weixin.qq.com/s/TsU_5N0Csfw-afN9AdAihw" target="_blank" rel="noopener">Flink实战-壳找房基于Flink的实时平台建设</a></li><li><a href="https://mp.weixin.qq.com/s/0M8XLTgpj6jWNcokNhyxAw" target="_blank" rel="noopener">用Flink取代Spark Streaming！知乎实时数仓架构演进</a></li><li><a href="https://mp.weixin.qq.com/s/Oom-TaEsT6GKGs95dJil5Q" target="_blank" rel="noopener">Flink实时数仓-美团点评实战</a></li><li><a href="https://mp.weixin.qq.com/s/13w43iYT3-riIj757HPGxw" target="_blank" rel="noopener">来将可留姓名？Flink最强学习资源合集!</a></li><li><a href="https://mp.weixin.qq.com/s/0VXqbzLBj5rZjjf4jAc3UQ" target="_blank" rel="noopener">数据不撒谎，Flink-Kafka性能压测全记录!</a></li><li><a href="https://mp.weixin.qq.com/s/2_8uOdDJwzYxUP-NLh6VhA" target="_blank" rel="noopener">菜鸟在物流场景中基于Flink的流计算实践</a></li><li><a href="https://mp.weixin.qq.com/s/Rhgt33y102WzR9-Zq15iVQ" target="_blank" rel="noopener">基于Flink构建实时数据仓库</a></li><li><a href="https://mp.weixin.qq.com/s/sjRV_F9tXEfqKL_00rJc7w" target="_blank" rel="noopener">Flink/Spark 如何实现动态更新作业配置</a></li></ol><h3 id="Spark实战进阶文章合集"><a href="#Spark实战进阶文章合集" class="headerlink" title="Spark实战进阶文章合集"></a>Spark实战进阶文章合集</h3><ol><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486986&amp;idx=1&amp;sn=422d1a3c11c72ff97b32cc01142839f4&amp;chksm=fd3d489fca4ac1895242ab94b932b12c65dc57b5f3a16acc7084dc8a189e9026290245a64c4f&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">如果你在准备面试，好好看看这130道题</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486981&amp;idx=1&amp;sn=9c8fc4c127d7e6108ac4e171e750d490&amp;chksm=fd3d4890ca4ac186614f0dda8ffb2d35693a925b03861a01769c898652b53d0d436bca05ea12&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">ORC文件存储格式的深入探究</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486938&amp;idx=1&amp;sn=83347b444fc721442d2b4e1a58eca0e8&amp;chksm=fd3d4b4fca4ac2597abd4a39a21dea83220858ae5efd1ae287b471bb9970f165b4854426f4c2&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">基于SparkStreaming+Kafka+HBase实时点击流案例</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486931&amp;idx=1&amp;sn=9c3f3d6a677ed2aa6cc508046ca6da78&amp;chksm=fd3d4b46ca4ac25044d6033458d3b43e7d1f50f447bded4eed8b246991cf620c91919c51e35b&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">HyperLogLog函数在Spark中的高级应用</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486931&amp;idx=2&amp;sn=1c5987f3bad7a805895484ebfd683e11&amp;chksm=fd3d4b46ca4ac250d31502a76f0cfd02ebea29d1161b9e9faec23ea6aa8947d0dd7d4268427f&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">我们常说的海量小文件的根源是什么？</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486904&amp;idx=1&amp;sn=5f4b673a87497a9c1dc9d7ce253994f3&amp;chksm=fd3d4b2dca4ac23b9850c54d62ebe8be5920cbd4a491a46a0325e1fbbd91f6b1aef9adf2f638&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Structured Streaming | Apache Spark中处理实时数据的声明式API</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486765&amp;idx=1&amp;sn=516a32e8c1e9842606a7670862ec7e97&amp;chksm=fd3d4bb8ca4ac2ae24e315083cdb195fdf897e1c3cce69d2c94183d5c20c1af1bfbe0e5480fd&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark面对OOM问题的解决方法及优化总结</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486761&amp;idx=1&amp;sn=959aaa5266307a64181631ba1ae46e86&amp;chksm=fd3d4bbcca4ac2aad8a3958da40c22e565997180c635bb6c32a9b4aa7e82a74a56423320c65a&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark 动态资源分配(Dynamic Resource Allocation) 解析</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486668&amp;idx=1&amp;sn=981028fdcf937ba45b3914e2d48b94db&amp;chksm=fd3d4a59ca4ac34f089ff1dce9542a0f481a73439acec9223fa2d776ff3f2eaec498cc543d06&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Apache Spark在海致大数据平台中的优化实践</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486644&amp;idx=1&amp;sn=d2637a1e918c2b1be4c9fe3d74f75a92&amp;chksm=fd3d4a21ca4ac3377cc8836939cc041cf934bb57f73b6b618fd1de608495d86e278c1c7e4cdc&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark/Flink广播实现作业配置动态更新</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486629&amp;idx=2&amp;sn=54d05858d659756a46b9be36b7b63ee5&amp;chksm=fd3d4a30ca4ac326f69ba9b7cf6a82418afd31193e96aa28346779264a7d8a1df4c7a3a31fba&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark SQL读数据库时不支持某些数据类型的问题</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486586&amp;idx=1&amp;sn=e35def6429adaada0910b91eed8d7b1f&amp;chksm=fd3d4aefca4ac3f9918d57b947d4bc8f71afd9d7666ffe28e3660e5efab332c714bef0879a90&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">这个面试问题很难么 | 如何处理大数据中的数据倾斜</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486582&amp;idx=1&amp;sn=7b6291dedb2e6892342e1ed705bdfb2e&amp;chksm=fd3d4ae3ca4ac3f591f297635c0ff8e63e3deb6d6aec72c8fb18e19f8f6fcdb5cd2abcd37d09&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark难点 | Join的实现原理</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486577&amp;idx=1&amp;sn=49fd0138ad9837192b6eb78cbdc478e6&amp;chksm=fd3d4ae4ca4ac3f2aec91f40435d1eb55a9e497fe7d8ab9ad52396838f467879ac56cb83c9c2&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">面试注意点 | Spark&amp;Flink的区别拾遗</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486514&amp;idx=1&amp;sn=a3c3084b8a797a0889d7583fb10d70c9&amp;chksm=fd3d4aa7ca4ac3b12416ba8269041032a515b7a4e71bda1709e359a15a04df4f638738c6e01a&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark Checkpoint的运行原理和源码实现</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486505&amp;idx=1&amp;sn=91316d3aa5a99945ccd992c0a98e500d&amp;chksm=fd3d4abcca4ac3aa26f051504244239ff1ab48eb71cf01c14f689eb4767c121b1477d93b60ea&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">阿里云Spark Shuffle的优化</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486485&amp;idx=1&amp;sn=999b84e0ac87f2aa1be4870921279a21&amp;chksm=fd3d4a80ca4ac39695d9307582ff58938180d0c5b12f6db84ddae933edb345b0cbe46f6284ed&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">使用Kafka+Spark+Cassandra构建实时处理引擎</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486477&amp;idx=1&amp;sn=21394cca8fe279bbc2032d48f65672f6&amp;chksm=fd3d4a98ca4ac38e3bf9700cfe65131fffadbbf9493ccc658b3e7be97ddb6665ad41a70f67f9&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">基于HBase和Spark构建企业级数据处理平台</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486419&amp;idx=1&amp;sn=3bc8af144370a602817ca87415fe525d&amp;chksm=fd3d4d46ca4ac450ec6f1ac84b2f3162071eb85221464bdb1976178ca2363cd2acb3d63d401c&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">SparkSQL在字节跳动的应用实践和优化实战</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486369&amp;idx=1&amp;sn=9a760114ed2c7509e191ad29370eecce&amp;chksm=fd3d4d34ca4ac4222d310c37fb67bcaafd5425e1a16c43434c4d1fea403756e266de21b83181&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">SparkRDD转DataSet/DataFrame的一个深坑</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486332&amp;idx=1&amp;sn=ecbe21981c5c36f6c420755e8d63fb8f&amp;chksm=fd3d4de9ca4ac4ff1b11093432f444c9e4f66c45a6e01155d69cface70456fb418a98163757d&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark和Flink的状态管理State的区别和应用</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486328&amp;idx=1&amp;sn=b5d53e0007032114fb277e440e5ce4bf&amp;chksm=fd3d4dedca4ac4fbb5026338af7dbbfe1d845fdc11e7e2279035dddc8ff0b6dd24c32be129ce&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Kafka+Spark Streaming管理offset的几种方法</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247486322&amp;idx=2&amp;sn=00ddcd16109249e45a70233d5ef959ba&amp;chksm=fd3d4de7ca4ac4f15f85d9a2873c5d1070af3bb929479bd66f7a0dc89ac0777b6960cbce5970&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">从 PageRank Example谈Spark应用程序调优</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247485574&amp;idx=1&amp;sn=723c02562ee1f44e88c389d6ac8a2c87&amp;chksm=fd3d4e13ca4ac705ad892ada2c68792c906946f271bb1e6afd15a93749f8f87d0ea967ba2d37&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark调优|SparkSQL参数调优</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247485507&amp;idx=1&amp;sn=2cab7a3714ce4e16351b8edcde95e777&amp;chksm=fd3d4ed6ca4ac7c058d820f36b2d03c3ccddc293e1035b6897e99544e55931f6cd6a2f343647&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Flink/Spark 如何实现动态更新作业配置</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247485361&amp;idx=1&amp;sn=8348203b6f17662a64fa5d412de97296&amp;chksm=fd3d4124ca4ac8329bac00fae705f37c08236d583e9900f268796d463be9323d82ba4b8b1554&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Stream SQL的执行原理与Flink的实现</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247485321&amp;idx=1&amp;sn=13e73673fb29bd134ab79e03a369288c&amp;chksm=fd3d411cca4ac80ac68204d18aee55b95cadde7bf18cc2f8a549fc0b6ff4ec4db570190566c3&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark将Dataframe数据写入Hive分区表的方案</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247485316&amp;idx=1&amp;sn=8a7a02023f15324885de7a5c93d4dd94&amp;chksm=fd3d4111ca4ac807f34e1fa03023494d46f3770c035290ddefe1333419453a9258f504584aee&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark中几种ShuffleWriter的区别你都知道吗？</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247485217&amp;idx=1&amp;sn=3ce9fa8ad179c008754873129e51fbe7&amp;chksm=fd3d41b4ca4ac8a25957fc9541437e6546fc2926df2908bad5adbd30a6cecf9f797fef74f894&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">SparkSQL的3种Join实现</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247485138&amp;idx=1&amp;sn=8f71070470c8963e7c973b5f10bf3c03&amp;chksm=fd3d4047ca4ac951981f7f0fa08f9f6a1821270441b5008da28115b67020b01ef2936b5f1e88&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">周期性清除Spark Streaming流状态的方法</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247485119&amp;idx=1&amp;sn=fd172b1f9c9ef99eac2ed976a7d4459f&amp;chksm=fd3d402aca4ac93ced929dfb9b3d785fa5e00d82939b4ea7ed31e68096b87f31a0d11c5f7414&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Structured Streaming之状态存储解析</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247485105&amp;idx=1&amp;sn=0bea228e6845d04739937b75bd2f8d9a&amp;chksm=fd3d4024ca4ac9322cae55569b7bdc6d8546dc9c4583f25801d3820ab3b30094c50816c7c1c8&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark SQL重点知识总结</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247485058&amp;idx=1&amp;sn=4d3b5c25ca1fdf1f0fb0cd99959d2371&amp;chksm=fd3d4017ca4ac90140442dfc6032346d5841d6705bd92441ad3d0a0366db346752a6b154b976&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">SparkSQL极简入门</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247485051&amp;idx=1&amp;sn=a1a70cad450634ceae44d4e14a4fc3ef&amp;chksm=fd3d40eeca4ac9f8901a93271683811da05c62e7a6f1d2825378c32542ef2edf9947273a601a&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark Shuffle在网易的优化</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247484999&amp;idx=1&amp;sn=f9cf6eae39bc1d54faaa144357731d2f&amp;chksm=fd3d40d2ca4ac9c4e886bd0208521c45dbab98e9cfd34826e6808d414bcd66f203f0359382e1&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">广告点击数实时统计：Spark StructuredStreaming + Redis Streams</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247484972&amp;idx=1&amp;sn=ff9a2925c31e07b558504be17937872b&amp;chksm=fd3d40b9ca4ac9afe2cce8ff4a6c50724a146b87d2d05d87d6dcdfb34a177f3be4d5ba79b492&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark内存调优</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247484959&amp;idx=1&amp;sn=2173f71a32e16b510f047fa716549bc2&amp;chksm=fd3d408aca4ac99cef4ec3079d9c7376c14f457e80f814e1494f5324612b553fb5487783a486&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Structured Streaming 实现思路与实现概述</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247484956&amp;idx=1&amp;sn=9182a40fcf1fced04acee81aa9261bfe&amp;chksm=fd3d4089ca4ac99f23952f0d627db4600a81808d98b1a635ae40e06c939e4a229a47d666de47&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark之数据倾斜调优</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247484766&amp;idx=1&amp;sn=8d0aeaa1166a9338df9f28bb47959f4a&amp;chksm=fd3d43cbca4acadd0dfc9e753ca4fe1cc2ca49060d886b359bdf3537809015b04f82b4be27ac&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">你不得不知道的知识-零拷贝</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU3MzgwNTU2Mg==&amp;mid=2247484751&amp;idx=1&amp;sn=11315f599b39eac96c17a78da2fa1258&amp;chksm=fd3d43daca4acaccc624947f5fa84f650e7f61d8638feda67db07cacf51bc985faae74cc94c3&amp;token=1999457569&amp;lang=zh_CN#rd" target="_blank" rel="noopener">Spark Streaming消费Kafka数据的两种方案</a></li></ol>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="大数据" scheme="https://icocos.github.io/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="学习目录" scheme="https://icocos.github.io/tags/%E5%AD%A6%E4%B9%A0%E7%9B%AE%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>大数据之Storm集成Redis详解</title>
    <link href="https://icocos.github.io/2019/11/03/%E5%A4%A7%E6%95%B0%E6%8D%AE%E4%B9%8BStorm%E9%9B%86%E6%88%90Redis%E8%AF%A6%E8%A7%A3/"/>
    <id>https://icocos.github.io/2019/11/03/大数据之Storm集成Redis详解/</id>
    <published>2019-11-03T13:19:01.000Z</published>
    <updated>2020-04-09T14:45:07.958Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h1 id="Storm-集成-Redis-详解"><a href="#Storm-集成-Redis-详解" class="headerlink" title="Storm 集成 Redis 详解"></a>Storm 集成 Redis 详解</h1><nav><br><a href="#一简介">一、简介</a><br><br><a href="#二集成案例">二、集成案例</a><br><br><a href="#三storm-redis-实现原理">三、storm-redis 实现原理</a><br><br><a href="#四自定义RedisBolt实现词频统计">四、自定义RedisBolt实现词频统计</a><br><br></nav><h2 id="一、简介"><a href="#一、简介" class="headerlink" title="一、简介"></a>一、简介</h2><p>Storm-Redis 提供了 Storm 与 Redis 的集成支持，你只需要引入对应的依赖即可使用：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.storm<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>storm-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;storm.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">type</span>&gt;</span>jar<span class="tag">&lt;/<span class="name">type</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>Storm-Redis 使用 Jedis 为 Redis 客户端，并提供了如下三个基本的 Bolt 实现：</p><ul><li><strong>RedisLookupBolt</strong>：从 Redis 中查询数据；</li><li><strong>RedisStoreBolt</strong>：存储数据到 Redis；</li><li><strong>RedisFilterBolt</strong> : 查询符合条件的数据；</li></ul><p><code>RedisLookupBolt</code>、<code>RedisStoreBolt</code>、<code>RedisFilterBolt</code> 均继承自 <code>AbstractRedisBolt</code> 抽象类。我们可以通过继承该抽象类，实现自定义 RedisBolt，进行功能的拓展。</p><h2 id="二、集成案例"><a href="#二、集成案例" class="headerlink" title="二、集成案例"></a>二、集成案例</h2><h3 id="2-1-项目结构"><a href="#2-1-项目结构" class="headerlink" title="2.1 项目结构"></a>2.1 项目结构</h3><p>这里首先给出一个集成案例：进行词频统计并将最后的结果存储到 Redis。项目结构如下：</p><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/storm-wordcounttoredis.png"> </div><blockquote><p>用例源码下载地址：<a href="https://github.com/heibaiying/BigData-Notes/tree/master/code/Storm/storm-redis-integration" target="_blank" rel="noopener">storm-redis-integration</a></p></blockquote><h3 id="2-2-项目依赖"><a href="#2-2-项目依赖" class="headerlink" title="2.2 项目依赖"></a>2.2 项目依赖</h3><p>项目主要依赖如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">storm.version</span>&gt;</span>1.2.2<span class="tag">&lt;/<span class="name">storm.version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.storm<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>storm-core<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;storm.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.storm<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>storm-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;storm.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="2-3-DataSourceSpout"><a href="#2-3-DataSourceSpout" class="headerlink" title="2.3 DataSourceSpout"></a>2.3 DataSourceSpout</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 产生词频样本的数据源</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DataSourceSpout</span> <span class="keyword">extends</span> <span class="title">BaseRichSpout</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; list = Arrays.asList(<span class="string">"Spark"</span>, <span class="string">"Hadoop"</span>, <span class="string">"HBase"</span>, <span class="string">"Storm"</span>, <span class="string">"Flink"</span>, <span class="string">"Hive"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> SpoutOutputCollector spoutOutputCollector;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">open</span><span class="params">(Map map, TopologyContext topologyContext, SpoutOutputCollector spoutOutputCollector)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.spoutOutputCollector = spoutOutputCollector;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">nextTuple</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 模拟产生数据</span></span><br><span class="line">        String lineData = productData();</span><br><span class="line">        spoutOutputCollector.emit(<span class="keyword">new</span> Values(lineData));</span><br><span class="line">        Utils.sleep(<span class="number">1000</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer outputFieldsDeclarer)</span> </span>&#123;</span><br><span class="line">        outputFieldsDeclarer.declare(<span class="keyword">new</span> Fields(<span class="string">"line"</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 模拟数据</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> String <span class="title">productData</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Collections.shuffle(list);</span><br><span class="line">        Random random = <span class="keyword">new</span> Random();</span><br><span class="line">        <span class="keyword">int</span> endIndex = random.nextInt(list.size()) % (list.size()) + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">return</span> StringUtils.join(list.toArray(), <span class="string">"\t"</span>, <span class="number">0</span>, endIndex);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>产生的模拟数据格式如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">SparkHBase</span><br><span class="line">HiveFlinkStormHadoopHBaseSpark</span><br><span class="line">Flink</span><br><span class="line">HBaseStorm</span><br><span class="line">HBaseHadoopHiveFlink</span><br><span class="line">HBaseFlinkHiveStorm</span><br><span class="line">HiveFlinkHadoop</span><br><span class="line">HBaseHive</span><br><span class="line">HadoopSparkHBaseStorm</span><br></pre></td></tr></table></figure><h3 id="2-4-SplitBolt"><a href="#2-4-SplitBolt" class="headerlink" title="2.4 SplitBolt"></a>2.4 SplitBolt</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将每行数据按照指定分隔符进行拆分</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SplitBolt</span> <span class="keyword">extends</span> <span class="title">BaseRichBolt</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> OutputCollector collector;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">(Map stormConf, TopologyContext context, OutputCollector collector)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.collector = collector;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Tuple input)</span> </span>&#123;</span><br><span class="line">        String line = input.getStringByField(<span class="string">"line"</span>);</span><br><span class="line">        String[] words = line.split(<span class="string">"\t"</span>);</span><br><span class="line">        <span class="keyword">for</span> (String word : words) &#123;</span><br><span class="line">            collector.emit(<span class="keyword">new</span> Values(word, String.valueOf(<span class="number">1</span>)));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer declarer)</span> </span>&#123;</span><br><span class="line">        declarer.declare(<span class="keyword">new</span> Fields(<span class="string">"word"</span>, <span class="string">"count"</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-5-CountBolt"><a href="#2-5-CountBolt" class="headerlink" title="2.5 CountBolt"></a>2.5 CountBolt</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 进行词频统计</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CountBolt</span> <span class="keyword">extends</span> <span class="title">BaseRichBolt</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, Integer&gt; counts = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> OutputCollector collector;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">(Map stormConf, TopologyContext context, OutputCollector collector)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.collector=collector;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Tuple input)</span> </span>&#123;</span><br><span class="line">        String word = input.getStringByField(<span class="string">"word"</span>);</span><br><span class="line">        Integer count = counts.get(word);</span><br><span class="line">        <span class="keyword">if</span> (count == <span class="keyword">null</span>) &#123;</span><br><span class="line">            count = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        count++;</span><br><span class="line">        counts.put(word, count);</span><br><span class="line">        <span class="comment">// 输出</span></span><br><span class="line">        collector.emit(<span class="keyword">new</span> Values(word, String.valueOf(count)));</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer declarer)</span> </span>&#123;</span><br><span class="line">        declarer.declare(<span class="keyword">new</span> Fields(<span class="string">"word"</span>, <span class="string">"count"</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-6-WordCountStoreMapper"><a href="#2-6-WordCountStoreMapper" class="headerlink" title="2.6 WordCountStoreMapper"></a>2.6 WordCountStoreMapper</h3><p>实现 RedisStoreMapper 接口，定义 tuple 与 Redis 中数据的映射关系：即需要指定 tuple 中的哪个字段为 key，哪个字段为 value，并且存储到 Redis 的何种数据结构中。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 定义 tuple 与 Redis 中数据的映射关系</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span>  <span class="title">WordCountStoreMapper</span> <span class="keyword">implements</span> <span class="title">RedisStoreMapper</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> RedisDataTypeDescription description;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String hashKey = <span class="string">"wordCount"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">WordCountStoreMapper</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        description = <span class="keyword">new</span> RedisDataTypeDescription(</span><br><span class="line">                RedisDataTypeDescription.RedisDataType.HASH, hashKey);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> RedisDataTypeDescription <span class="title">getDataTypeDescription</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> description;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getKeyFromTuple</span><span class="params">(ITuple tuple)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> tuple.getStringByField(<span class="string">"word"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getValueFromTuple</span><span class="params">(ITuple tuple)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> tuple.getStringByField(<span class="string">"count"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-7-WordCountToRedisApp"><a href="#2-7-WordCountToRedisApp" class="headerlink" title="2.7 WordCountToRedisApp"></a>2.7 WordCountToRedisApp</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 进行词频统计 并将统计结果存储到 Redis 中</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WordCountToRedisApp</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String DATA_SOURCE_SPOUT = <span class="string">"dataSourceSpout"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String SPLIT_BOLT = <span class="string">"splitBolt"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String COUNT_BOLT = <span class="string">"countBolt"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String STORE_BOLT = <span class="string">"storeBolt"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//在实际开发中这些参数可以将通过外部传入 使得程序更加灵活</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String REDIS_HOST = <span class="string">"192.168.200.226"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> REDIS_PORT = <span class="number">6379</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        TopologyBuilder builder = <span class="keyword">new</span> TopologyBuilder();</span><br><span class="line">        builder.setSpout(DATA_SOURCE_SPOUT, <span class="keyword">new</span> DataSourceSpout());</span><br><span class="line">        <span class="comment">// split</span></span><br><span class="line">        builder.setBolt(SPLIT_BOLT, <span class="keyword">new</span> SplitBolt()).shuffleGrouping(DATA_SOURCE_SPOUT);</span><br><span class="line">        <span class="comment">// count</span></span><br><span class="line">        builder.setBolt(COUNT_BOLT, <span class="keyword">new</span> CountBolt()).shuffleGrouping(SPLIT_BOLT);</span><br><span class="line">        <span class="comment">// save to redis</span></span><br><span class="line">        JedisPoolConfig poolConfig = <span class="keyword">new</span> JedisPoolConfig.Builder()</span><br><span class="line">                .setHost(REDIS_HOST).setPort(REDIS_PORT).build();</span><br><span class="line">        RedisStoreMapper storeMapper = <span class="keyword">new</span> WordCountStoreMapper();</span><br><span class="line">        RedisStoreBolt storeBolt = <span class="keyword">new</span> RedisStoreBolt(poolConfig, storeMapper);</span><br><span class="line">        builder.setBolt(STORE_BOLT, storeBolt).shuffleGrouping(COUNT_BOLT);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果外部传参 cluster 则代表线上环境启动否则代表本地启动</span></span><br><span class="line">        <span class="keyword">if</span> (args.length &gt; <span class="number">0</span> &amp;&amp; args[<span class="number">0</span>].equals(<span class="string">"cluster"</span>)) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                StormSubmitter.submitTopology(<span class="string">"ClusterWordCountToRedisApp"</span>, <span class="keyword">new</span> Config(), builder.createTopology());</span><br><span class="line">            &#125; <span class="keyword">catch</span> (AlreadyAliveException | InvalidTopologyException | AuthorizationException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            LocalCluster cluster = <span class="keyword">new</span> LocalCluster();</span><br><span class="line">            cluster.submitTopology(<span class="string">"LocalWordCountToRedisApp"</span>,</span><br><span class="line">                    <span class="keyword">new</span> Config(), builder.createTopology());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-8-启动测试"><a href="#2-8-启动测试" class="headerlink" title="2.8 启动测试"></a>2.8 启动测试</h3><p>可以用直接使用本地模式运行，也可以打包后提交到服务器集群运行。本仓库提供的源码默认采用 <code>maven-shade-plugin</code> 进行打包，打包命令如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> mvn clean package -D maven.test.skip=<span class="literal">true</span></span></span><br></pre></td></tr></table></figure><p>启动后，查看 Redis 中的数据：</p><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/store-redis-manager.png"> </div><h2 id="三、storm-redis-实现原理"><a href="#三、storm-redis-实现原理" class="headerlink" title="三、storm-redis 实现原理"></a>三、storm-redis 实现原理</h2><h3 id="3-1-AbstractRedisBolt"><a href="#3-1-AbstractRedisBolt" class="headerlink" title="3.1 AbstractRedisBolt"></a>3.1 AbstractRedisBolt</h3><p><code>RedisLookupBolt</code>、<code>RedisStoreBolt</code>、<code>RedisFilterBolt</code> 均继承自 <code>AbstractRedisBolt</code> 抽象类，和我们自定义实现 Bolt 一样，<code>AbstractRedisBolt</code> 间接继承自 <code>BaseRichBolt</code>。</p><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/storm-abstractRedisBolt.png"> </div><p><code>AbstractRedisBolt</code> 中比较重要的是 prepare 方法，在该方法中通过外部传入的 jedis 连接池配置 ( jedisPoolConfig/jedisClusterConfig) 创建用于管理 Jedis 实例的容器 <code>JedisCommandsInstanceContainer</code>。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractRedisBolt</span> <span class="keyword">extends</span> <span class="title">BaseTickTupleAwareRichBolt</span> </span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> OutputCollector collector;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">transient</span> JedisCommandsInstanceContainer container;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> JedisPoolConfig jedisPoolConfig;</span><br><span class="line">    <span class="keyword">private</span> JedisClusterConfig jedisClusterConfig;</span><br><span class="line"></span><br><span class="line">   ......</span><br><span class="line">   </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">(Map map, TopologyContext topologyContext, OutputCollector collector)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// <span class="doctag">FIXME:</span> stores map (stormConf), topologyContext and expose these to derived classes</span></span><br><span class="line">        <span class="keyword">this</span>.collector = collector;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (jedisPoolConfig != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">this</span>.container = JedisCommandsContainerBuilder.build(jedisPoolConfig);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (jedisClusterConfig != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">this</span>.container = JedisCommandsContainerBuilder.build(jedisClusterConfig);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Jedis configuration not found"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">  .......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>JedisCommandsInstanceContainer</code> 的 <code>build()</code> 方法如下，实际上就是创建 JedisPool 或 JedisCluster 并传入容器中。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> JedisCommandsInstanceContainer <span class="title">build</span><span class="params">(JedisPoolConfig config)</span> </span>&#123;</span><br><span class="line">        JedisPool jedisPool = <span class="keyword">new</span> JedisPool(DEFAULT_POOL_CONFIG, config.getHost(), config.getPort(), config.getTimeout(), config.getPassword(), config.getDatabase());</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> JedisContainer(jedisPool);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> JedisCommandsInstanceContainer <span class="title">build</span><span class="params">(JedisClusterConfig config)</span> </span>&#123;</span><br><span class="line">        JedisCluster jedisCluster = <span class="keyword">new</span> JedisCluster(config.getNodes(), config.getTimeout(), config.getTimeout(), config.getMaxRedirections(), config.getPassword(), DEFAULT_POOL_CONFIG);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> JedisClusterContainer(jedisCluster);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h3 id="3-2-RedisStoreBolt和RedisLookupBolt"><a href="#3-2-RedisStoreBolt和RedisLookupBolt" class="headerlink" title="3.2 RedisStoreBolt和RedisLookupBolt"></a>3.2 RedisStoreBolt和RedisLookupBolt</h3><p><code>RedisStoreBolt</code> 中比较重要的是 process 方法，该方法主要从 storeMapper 中获取传入 key/value 的值，并按照其存储类型 <code>dataType</code> 调用 jedisCommand 的对应方法进行存储。</p><p>RedisLookupBolt 的实现基本类似，从 lookupMapper 中获取传入的 key 值，并进行查询操作。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RedisStoreBolt</span> <span class="keyword">extends</span> <span class="title">AbstractRedisBolt</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> RedisStoreMapper storeMapper;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> RedisDataTypeDescription.RedisDataType dataType;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String additionalKey;</span><br><span class="line"></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="title">RedisStoreBolt</span><span class="params">(JedisPoolConfig config, RedisStoreMapper storeMapper)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(config);</span><br><span class="line">        <span class="keyword">this</span>.storeMapper = storeMapper;</span><br><span class="line"></span><br><span class="line">        RedisDataTypeDescription dataTypeDescription = storeMapper.getDataTypeDescription();</span><br><span class="line">        <span class="keyword">this</span>.dataType = dataTypeDescription.getDataType();</span><br><span class="line">        <span class="keyword">this</span>.additionalKey = dataTypeDescription.getAdditionalKey();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">RedisStoreBolt</span><span class="params">(JedisClusterConfig config, RedisStoreMapper storeMapper)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(config);</span><br><span class="line">        <span class="keyword">this</span>.storeMapper = storeMapper;</span><br><span class="line"></span><br><span class="line">        RedisDataTypeDescription dataTypeDescription = storeMapper.getDataTypeDescription();</span><br><span class="line">        <span class="keyword">this</span>.dataType = dataTypeDescription.getDataType();</span><br><span class="line">        <span class="keyword">this</span>.additionalKey = dataTypeDescription.getAdditionalKey();</span><br><span class="line">    &#125;</span><br><span class="line">       </span><br><span class="line">  </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">process</span><span class="params">(Tuple input)</span> </span>&#123;</span><br><span class="line">        String key = storeMapper.getKeyFromTuple(input);</span><br><span class="line">        String value = storeMapper.getValueFromTuple(input);</span><br><span class="line"></span><br><span class="line">        JedisCommands jedisCommand = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            jedisCommand = getInstance();</span><br><span class="line"></span><br><span class="line">            <span class="keyword">switch</span> (dataType) &#123;</span><br><span class="line">                <span class="keyword">case</span> STRING:</span><br><span class="line">                    jedisCommand.set(key, value);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">case</span> LIST:</span><br><span class="line">                    jedisCommand.rpush(key, value);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">case</span> HASH:</span><br><span class="line">                    jedisCommand.hset(additionalKey, key, value);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">case</span> SET:</span><br><span class="line">                    jedisCommand.sadd(key, value);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">case</span> SORTED_SET:</span><br><span class="line">                    jedisCommand.zadd(additionalKey, Double.valueOf(value), key);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">case</span> HYPER_LOG_LOG:</span><br><span class="line">                    jedisCommand.pfadd(key, value);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">case</span> GEO:</span><br><span class="line">                    String[] array = value.split(<span class="string">":"</span>);</span><br><span class="line">                    <span class="keyword">if</span> (array.length != <span class="number">2</span>) &#123;</span><br><span class="line">                        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"value structure should be longitude:latitude"</span>);</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">double</span> longitude = Double.valueOf(array[<span class="number">0</span>]);</span><br><span class="line">                    <span class="keyword">double</span> latitude = Double.valueOf(array[<span class="number">1</span>]);</span><br><span class="line">                    jedisCommand.geoadd(additionalKey, longitude, latitude, key);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">default</span>:</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Cannot process such data type: "</span> + dataType);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            collector.ack(input);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">this</span>.collector.reportError(e);</span><br><span class="line">            <span class="keyword">this</span>.collector.fail(input);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            returnInstance(jedisCommand);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">     .........</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-3-JedisCommands"><a href="#3-3-JedisCommands" class="headerlink" title="3.3 JedisCommands"></a>3.3 JedisCommands</h3><p>JedisCommands 接口中定义了所有的 Redis 客户端命令，它有以下三个实现类，分别是 Jedis、JedisCluster、ShardedJedis。Strom 中主要使用前两种实现类，具体调用哪一个实现类来执行命令，由传入的是 jedisPoolConfig 还是 jedisClusterConfig 来决定。</p><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/storm-jedicCommands.png"> </div><h3 id="3-4-RedisMapper-和-TupleMapper"><a href="#3-4-RedisMapper-和-TupleMapper" class="headerlink" title="3.4 RedisMapper 和 TupleMapper"></a>3.4 RedisMapper 和 TupleMapper</h3><p>RedisMapper 和 TupleMapper 定义了 tuple 和 Redis 中的数据如何进行映射转换。</p><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/storm-Redis-Mapper.png"> </div><h4 id="1-TupleMapper"><a href="#1-TupleMapper" class="headerlink" title="1. TupleMapper"></a>1. TupleMapper</h4><p>TupleMapper 主要定义了两个方法：</p><ul><li><p>getKeyFromTuple(ITuple tuple)： 从 tuple 中获取那个字段作为 Key；</p></li><li><p>getValueFromTuple(ITuple tuple)：从 tuple 中获取那个字段作为 Value；</p></li></ul><h4 id="2-RedisMapper"><a href="#2-RedisMapper" class="headerlink" title="2. RedisMapper"></a>2. RedisMapper</h4><p>定义了获取数据类型的方法 <code>getDataTypeDescription()</code>,RedisDataTypeDescription 中 RedisDataType 枚举类定义了所有可用的 Redis 数据类型：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RedisDataTypeDescription</span> <span class="keyword">implements</span> <span class="title">Serializable</span> </span>&#123; </span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">enum</span> RedisDataType &#123; STRING, HASH, LIST, SET, SORTED_SET, HYPER_LOG_LOG, GEO &#125;</span><br><span class="line">     ......</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h4 id="3-RedisStoreMapper"><a href="#3-RedisStoreMapper" class="headerlink" title="3. RedisStoreMapper"></a>3. RedisStoreMapper</h4><p>RedisStoreMapper 继承 TupleMapper 和 RedisMapper 接口，用于数据存储时，没有定义额外方法。</p><h4 id="4-RedisLookupMapper"><a href="#4-RedisLookupMapper" class="headerlink" title="4. RedisLookupMapper"></a>4. RedisLookupMapper</h4><p>RedisLookupMapper 继承 TupleMapper 和 RedisMapper 接口：</p><ul><li>定义了 declareOutputFields 方法，声明输出的字段。</li><li>定义了 toTuple 方法，将查询结果组装为 Storm 的 Values 的集合，并用于发送。</li></ul><p>下面的例子表示从输入 <code>Tuple</code> 的获取 <code>word</code> 字段作为 key，使用 <code>RedisLookupBolt</code> 进行查询后，将 key 和查询结果 value 组装为 values 并发送到下一个处理单元。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">WordCountRedisLookupMapper</span> <span class="keyword">implements</span> <span class="title">RedisLookupMapper</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> RedisDataTypeDescription description;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String hashKey = <span class="string">"wordCount"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">WordCountRedisLookupMapper</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        description = <span class="keyword">new</span> RedisDataTypeDescription(</span><br><span class="line">                RedisDataTypeDescription.RedisDataType.HASH, hashKey);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;Values&gt; <span class="title">toTuple</span><span class="params">(ITuple input, Object value)</span> </span>&#123;</span><br><span class="line">        String member = getKeyFromTuple(input);</span><br><span class="line">        List&lt;Values&gt; values = Lists.newArrayList();</span><br><span class="line">        values.add(<span class="keyword">new</span> Values(member, value));</span><br><span class="line">        <span class="keyword">return</span> values;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer declarer)</span> </span>&#123;</span><br><span class="line">        declarer.declare(<span class="keyword">new</span> Fields(<span class="string">"wordName"</span>, <span class="string">"count"</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> RedisDataTypeDescription <span class="title">getDataTypeDescription</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> description;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getKeyFromTuple</span><span class="params">(ITuple tuple)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> tuple.getStringByField(<span class="string">"word"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getValueFromTuple</span><span class="params">(ITuple tuple)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="5-RedisFilterMapper"><a href="#5-RedisFilterMapper" class="headerlink" title="5. RedisFilterMapper"></a>5. RedisFilterMapper</h4><p>RedisFilterMapper 继承 TupleMapper 和 RedisMapper 接口，用于查询数据时，定义了 declareOutputFields 方法，声明输出的字段。如下面的实现：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer declarer)</span> </span>&#123;</span><br><span class="line">    declarer.declare(<span class="keyword">new</span> Fields(<span class="string">"wordName"</span>, <span class="string">"count"</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="四、自定义RedisBolt实现词频统计"><a href="#四、自定义RedisBolt实现词频统计" class="headerlink" title="四、自定义RedisBolt实现词频统计"></a>四、自定义RedisBolt实现词频统计</h2><h3 id="4-1-实现原理"><a href="#4-1-实现原理" class="headerlink" title="4.1 实现原理"></a>4.1 实现原理</h3><p>自定义 RedisBolt：主要利用 Redis 中哈希结构的 <code>hincrby key field</code> 命令进行词频统计。在 Redis 中 <code>hincrby</code> 的执行效果如下。hincrby 可以将字段按照指定的值进行递增，如果该字段不存在的话，还会新建该字段，并赋值为 0。通过这个命令可以非常轻松的实现词频统计功能。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">redis&gt;</span><span class="bash">  HSET myhash field 5</span></span><br><span class="line">(integer) 1</span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash">  HINCRBY myhash field 1</span></span><br><span class="line">(integer) 6</span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash">  HINCRBY myhash field -1</span></span><br><span class="line">(integer) 5</span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash">  HINCRBY myhash field -10</span></span><br><span class="line">(integer) -5</span><br><span class="line"><span class="meta">redis&gt;</span><span class="bash"></span></span><br></pre></td></tr></table></figure><h3 id="4-2-项目结构"><a href="#4-2-项目结构" class="headerlink" title="4.2 项目结构"></a>4.2 项目结构</h3><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/CustomRedisCountApp.png"> </div><h3 id="4-3-自定义RedisBolt的代码实现"><a href="#4-3-自定义RedisBolt的代码实现" class="headerlink" title="4.3 自定义RedisBolt的代码实现"></a>4.3 自定义RedisBolt的代码实现</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义 RedisBolt 利用 Redis 的哈希数据结构的 hincrby key field 命令进行词频统计</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RedisCountStoreBolt</span> <span class="keyword">extends</span> <span class="title">AbstractRedisBolt</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> RedisStoreMapper storeMapper;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> RedisDataTypeDescription.RedisDataType dataType;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String additionalKey;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">RedisCountStoreBolt</span><span class="params">(JedisPoolConfig config, RedisStoreMapper storeMapper)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(config);</span><br><span class="line">        <span class="keyword">this</span>.storeMapper = storeMapper;</span><br><span class="line">        RedisDataTypeDescription dataTypeDescription = storeMapper.getDataTypeDescription();</span><br><span class="line">        <span class="keyword">this</span>.dataType = dataTypeDescription.getDataType();</span><br><span class="line">        <span class="keyword">this</span>.additionalKey = dataTypeDescription.getAdditionalKey();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">process</span><span class="params">(Tuple tuple)</span> </span>&#123;</span><br><span class="line">        String key = storeMapper.getKeyFromTuple(tuple);</span><br><span class="line">        String value = storeMapper.getValueFromTuple(tuple);</span><br><span class="line"></span><br><span class="line">        JedisCommands jedisCommand = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            jedisCommand = getInstance();</span><br><span class="line">            <span class="keyword">if</span> (dataType == RedisDataTypeDescription.RedisDataType.HASH) &#123;</span><br><span class="line">                jedisCommand.hincrBy(additionalKey, key, Long.valueOf(value));</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Cannot process such data type for Count: "</span> + dataType);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            collector.ack(tuple);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">this</span>.collector.reportError(e);</span><br><span class="line">            <span class="keyword">this</span>.collector.fail(tuple);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            returnInstance(jedisCommand);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer declarer)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-4-CustomRedisCountApp"><a href="#4-4-CustomRedisCountApp" class="headerlink" title="4.4 CustomRedisCountApp"></a>4.4 CustomRedisCountApp</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 利用自定义的 RedisBolt 实现词频统计</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CustomRedisCountApp</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String DATA_SOURCE_SPOUT = <span class="string">"dataSourceSpout"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String SPLIT_BOLT = <span class="string">"splitBolt"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String STORE_BOLT = <span class="string">"storeBolt"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String REDIS_HOST = <span class="string">"192.168.200.226"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> REDIS_PORT = <span class="number">6379</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        TopologyBuilder builder = <span class="keyword">new</span> TopologyBuilder();</span><br><span class="line">        builder.setSpout(DATA_SOURCE_SPOUT, <span class="keyword">new</span> DataSourceSpout());</span><br><span class="line">        <span class="comment">// split</span></span><br><span class="line">        builder.setBolt(SPLIT_BOLT, <span class="keyword">new</span> SplitBolt()).shuffleGrouping(DATA_SOURCE_SPOUT);</span><br><span class="line">        <span class="comment">// save to redis and count</span></span><br><span class="line">        JedisPoolConfig poolConfig = <span class="keyword">new</span> JedisPoolConfig.Builder()</span><br><span class="line">                .setHost(REDIS_HOST).setPort(REDIS_PORT).build();</span><br><span class="line">        RedisStoreMapper storeMapper = <span class="keyword">new</span> WordCountStoreMapper();</span><br><span class="line">        RedisCountStoreBolt countStoreBolt = <span class="keyword">new</span> RedisCountStoreBolt(poolConfig, storeMapper);</span><br><span class="line">        builder.setBolt(STORE_BOLT, countStoreBolt).shuffleGrouping(SPLIT_BOLT);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果外部传参 cluster 则代表线上环境启动,否则代表本地启动</span></span><br><span class="line">        <span class="keyword">if</span> (args.length &gt; <span class="number">0</span> &amp;&amp; args[<span class="number">0</span>].equals(<span class="string">"cluster"</span>)) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                StormSubmitter.submitTopology(<span class="string">"ClusterCustomRedisCountApp"</span>, <span class="keyword">new</span> Config(), builder.createTopology());</span><br><span class="line">            &#125; <span class="keyword">catch</span> (AlreadyAliveException | InvalidTopologyException | AuthorizationException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            LocalCluster cluster = <span class="keyword">new</span> LocalCluster();</span><br><span class="line">            cluster.submitTopology(<span class="string">"LocalCustomRedisCountApp"</span>,</span><br><span class="line">                    <span class="keyword">new</span> Config(), builder.createTopology());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ol><li><a href="http://storm.apache.org/releases/2.0.0-SNAPSHOT/storm-redis.html" target="_blank" rel="noopener">Storm Redis Integration</a></li></ol>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="Storm" scheme="https://icocos.github.io/categories/Storm/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="Storm" scheme="https://icocos.github.io/tags/Storm/"/>
    
      <category term="Redis" scheme="https://icocos.github.io/tags/Redis/"/>
    
  </entry>
  
  <entry>
    <title>大数据之Storm集成Kakfa</title>
    <link href="https://icocos.github.io/2019/11/03/%E5%A4%A7%E6%95%B0%E6%8D%AE%E4%B9%8BStorm%E9%9B%86%E6%88%90Kakfa/"/>
    <id>https://icocos.github.io/2019/11/03/大数据之Storm集成Kakfa/</id>
    <published>2019-11-03T12:09:01.000Z</published>
    <updated>2020-04-09T14:45:08.845Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h1 id="Storm集成Kafka"><a href="#Storm集成Kafka" class="headerlink" title="Storm集成Kafka"></a>Storm集成Kafka</h1><nav><br><a href="#一整合说明">一、整合说明</a><br><br><a href="#二写入数据到Kafka">二、写入数据到Kafka</a><br><br><a href="#三从Kafka中读取数据">三、从Kafka中读取数据</a><br><br></nav><h2 id="一、整合说明"><a href="#一、整合说明" class="headerlink" title="一、整合说明"></a>一、整合说明</h2><p>Storm 官方对 Kafka 的整合分为两个版本，官方说明文档分别如下：</p><ul><li><a href="http://storm.apache.org/releases/2.0.0-SNAPSHOT/storm-kafka.html" target="_blank" rel="noopener">Storm Kafka Integration</a> : 主要是针对 0.8.x 版本的 Kafka 提供整合支持；</li><li><a href="">Storm Kafka Integration (0.10.x+)</a> : 包含 Kafka 新版本的 consumer API，主要对 Kafka 0.10.x + 提供整合支持。</li></ul><p>这里我服务端安装的 Kafka 版本为 2.2.0(Released Mar 22, 2019) ，按照官方 0.10.x+ 的整合文档进行整合，不适用于 0.8.x 版本的 Kafka。</p><h2 id="二、写入数据到Kafka"><a href="#二、写入数据到Kafka" class="headerlink" title="二、写入数据到Kafka"></a>二、写入数据到Kafka</h2><h3 id="2-1-项目结构"><a href="#2-1-项目结构" class="headerlink" title="2.1 项目结构"></a>2.1 项目结构</h3><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/writetokafka.png"> </div><h3 id="2-2-项目主要依赖"><a href="#2-2-项目主要依赖" class="headerlink" title="2.2 项目主要依赖"></a>2.2 项目主要依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">storm.version</span>&gt;</span>1.2.2<span class="tag">&lt;/<span class="name">storm.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">kafka.version</span>&gt;</span>2.2.0<span class="tag">&lt;/<span class="name">kafka.version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.storm<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>storm-core<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;storm.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.storm<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>storm-kafka-client<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;storm.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.kafka<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>kafka-clients<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;kafka.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="2-3-DataSourceSpout"><a href="#2-3-DataSourceSpout" class="headerlink" title="2.3 DataSourceSpout"></a>2.3 DataSourceSpout</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 产生词频样本的数据源</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DataSourceSpout</span> <span class="keyword">extends</span> <span class="title">BaseRichSpout</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; list = Arrays.asList(<span class="string">"Spark"</span>, <span class="string">"Hadoop"</span>, <span class="string">"HBase"</span>, <span class="string">"Storm"</span>, <span class="string">"Flink"</span>, <span class="string">"Hive"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> SpoutOutputCollector spoutOutputCollector;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">open</span><span class="params">(Map map, TopologyContext topologyContext, SpoutOutputCollector spoutOutputCollector)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.spoutOutputCollector = spoutOutputCollector;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">nextTuple</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 模拟产生数据</span></span><br><span class="line">        String lineData = productData();</span><br><span class="line">        spoutOutputCollector.emit(<span class="keyword">new</span> Values(lineData));</span><br><span class="line">        Utils.sleep(<span class="number">1000</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer outputFieldsDeclarer)</span> </span>&#123;</span><br><span class="line">        outputFieldsDeclarer.declare(<span class="keyword">new</span> Fields(<span class="string">"line"</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 模拟数据</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> String <span class="title">productData</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Collections.shuffle(list);</span><br><span class="line">        Random random = <span class="keyword">new</span> Random();</span><br><span class="line">        <span class="keyword">int</span> endIndex = random.nextInt(list.size()) % (list.size()) + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">return</span> StringUtils.join(list.toArray(), <span class="string">"\t"</span>, <span class="number">0</span>, endIndex);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>产生的模拟数据格式如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">SparkHBase</span><br><span class="line">HiveFlinkStormHadoopHBaseSpark</span><br><span class="line">Flink</span><br><span class="line">HBaseStorm</span><br><span class="line">HBaseHadoopHiveFlink</span><br><span class="line">HBaseFlinkHiveStorm</span><br><span class="line">HiveFlinkHadoop</span><br><span class="line">HBaseHive</span><br><span class="line">HadoopSparkHBaseStorm</span><br></pre></td></tr></table></figure><h3 id="2-4-WritingToKafkaApp"><a href="#2-4-WritingToKafkaApp" class="headerlink" title="2.4 WritingToKafkaApp"></a>2.4 WritingToKafkaApp</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 写入数据到 Kafka 中</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WritingToKafkaApp</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String BOOTSTRAP_SERVERS = <span class="string">"hadoop001:9092"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TOPIC_NAME = <span class="string">"storm-topic"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        TopologyBuilder builder = <span class="keyword">new</span> TopologyBuilder();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 定义 Kafka 生产者属性</span></span><br><span class="line">        Properties props = <span class="keyword">new</span> Properties();</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * 指定 broker 的地址清单，清单里不需要包含所有的 broker 地址，生产者会从给定的 broker 里查找其他 broker 的信息。</span></span><br><span class="line"><span class="comment">         * 不过建议至少要提供两个 broker 的信息作为容错。</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        props.put(<span class="string">"bootstrap.servers"</span>, BOOTSTRAP_SERVERS);</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * acks 参数指定了必须要有多少个分区副本收到消息，生产者才会认为消息写入是成功的。</span></span><br><span class="line"><span class="comment">         * acks=0 : 生产者在成功写入消息之前不会等待任何来自服务器的响应。</span></span><br><span class="line"><span class="comment">         * acks=1 : 只要集群的首领节点收到消息，生产者就会收到一个来自服务器成功响应。</span></span><br><span class="line"><span class="comment">         * acks=all : 只有当所有参与复制的节点全部收到消息时，生产者才会收到一个来自服务器的成功响应。</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        props.put(<span class="string">"acks"</span>, <span class="string">"1"</span>);</span><br><span class="line">        props.put(<span class="string">"key.serializer"</span>, <span class="string">"org.apache.kafka.common.serialization.StringSerializer"</span>);</span><br><span class="line">        props.put(<span class="string">"value.serializer"</span>, <span class="string">"org.apache.kafka.common.serialization.StringSerializer"</span>);</span><br><span class="line"></span><br><span class="line">        KafkaBolt bolt = <span class="keyword">new</span> KafkaBolt&lt;String, String&gt;()</span><br><span class="line">                .withProducerProperties(props)</span><br><span class="line">                .withTopicSelector(<span class="keyword">new</span> DefaultTopicSelector(TOPIC_NAME))</span><br><span class="line">                .withTupleToKafkaMapper(<span class="keyword">new</span> FieldNameBasedTupleToKafkaMapper&lt;&gt;());</span><br><span class="line"></span><br><span class="line">        builder.setSpout(<span class="string">"sourceSpout"</span>, <span class="keyword">new</span> DataSourceSpout(), <span class="number">1</span>);</span><br><span class="line">        builder.setBolt(<span class="string">"kafkaBolt"</span>, bolt, <span class="number">1</span>).shuffleGrouping(<span class="string">"sourceSpout"</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (args.length &gt; <span class="number">0</span> &amp;&amp; args[<span class="number">0</span>].equals(<span class="string">"cluster"</span>)) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                StormSubmitter.submitTopology(<span class="string">"ClusterWritingToKafkaApp"</span>, <span class="keyword">new</span> Config(), builder.createTopology());</span><br><span class="line">            &#125; <span class="keyword">catch</span> (AlreadyAliveException | InvalidTopologyException | AuthorizationException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            LocalCluster cluster = <span class="keyword">new</span> LocalCluster();</span><br><span class="line">            cluster.submitTopology(<span class="string">"LocalWritingToKafkaApp"</span>,</span><br><span class="line">                    <span class="keyword">new</span> Config(), builder.createTopology());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-5-测试准备工作"><a href="#2-5-测试准备工作" class="headerlink" title="2.5 测试准备工作"></a>2.5 测试准备工作</h3><p>进行测试前需要启动 Kakfa：</p><h4 id="1-启动Kakfa"><a href="#1-启动Kakfa" class="headerlink" title="1. 启动Kakfa"></a>1. 启动Kakfa</h4><p>Kafka 的运行依赖于 zookeeper，需要预先启动，可以启动 Kafka 内置的 zookeeper,也可以启动自己安装的：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> zookeeper启动命令</span></span><br><span class="line">bin/zkServer.sh start</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 内置zookeeper启动命令</span></span><br><span class="line">bin/zookeeper-server-start.sh config/zookeeper.properties</span><br></pre></td></tr></table></figure><p>启动单节点 kafka 用于测试：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> bin/kafka-server-start.sh config/server.properties</span></span><br></pre></td></tr></table></figure><h4 id="2-创建topic"><a href="#2-创建topic" class="headerlink" title="2. 创建topic"></a>2. 创建topic</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 创建用于测试主题</span></span><br><span class="line">bin/kafka-topics.sh --create --bootstrap-server hadoop001:9092 --replication-factor 1 --partitions 1 --topic storm-topic</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 查看所有主题</span></span><br><span class="line"> bin/kafka-topics.sh --list --bootstrap-server hadoop001:9092</span><br></pre></td></tr></table></figure><h4 id="3-启动消费者"><a href="#3-启动消费者" class="headerlink" title="3. 启动消费者"></a>3. 启动消费者</h4><p> 启动一个消费者用于观察写入情况，启动命令如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> bin/kafka-console-consumer.sh --bootstrap-server hadoop001:9092 --topic storm-topic --from-beginning</span></span><br></pre></td></tr></table></figure><h3 id="2-6-测试"><a href="#2-6-测试" class="headerlink" title="2.6 测试"></a>2.6 测试</h3><p>可以用直接使用本地模式运行，也可以打包后提交到服务器集群运行。本仓库提供的源码默认采用 <code>maven-shade-plugin</code> 进行打包，打包命令如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> mvn clean package -D maven.test.skip=<span class="literal">true</span></span></span><br></pre></td></tr></table></figure><p>启动后，消费者监听情况如下：</p><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/strom-kafka-consumer.png"> </div><h2 id="三、从Kafka中读取数据"><a href="#三、从Kafka中读取数据" class="headerlink" title="三、从Kafka中读取数据"></a>三、从Kafka中读取数据</h2><h3 id="3-1-项目结构"><a href="#3-1-项目结构" class="headerlink" title="3.1 项目结构"></a>3.1 项目结构</h3><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/readfromkafka.png"> </div><h3 id="3-2-ReadingFromKafkaApp"><a href="#3-2-ReadingFromKafkaApp" class="headerlink" title="3.2  ReadingFromKafkaApp"></a>3.2  ReadingFromKafkaApp</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 从 Kafka 中读取数据</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ReadingFromKafkaApp</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String BOOTSTRAP_SERVERS = <span class="string">"hadoop001:9092"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TOPIC_NAME = <span class="string">"storm-topic"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">final</span> TopologyBuilder builder = <span class="keyword">new</span> TopologyBuilder();</span><br><span class="line">        builder.setSpout(<span class="string">"kafka_spout"</span>, <span class="keyword">new</span> KafkaSpout&lt;&gt;(getKafkaSpoutConfig(BOOTSTRAP_SERVERS, TOPIC_NAME)), <span class="number">1</span>);</span><br><span class="line">        builder.setBolt(<span class="string">"bolt"</span>, <span class="keyword">new</span> LogConsoleBolt()).shuffleGrouping(<span class="string">"kafka_spout"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果外部传参 cluster 则代表线上环境启动,否则代表本地启动</span></span><br><span class="line">        <span class="keyword">if</span> (args.length &gt; <span class="number">0</span> &amp;&amp; args[<span class="number">0</span>].equals(<span class="string">"cluster"</span>)) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                StormSubmitter.submitTopology(<span class="string">"ClusterReadingFromKafkaApp"</span>, <span class="keyword">new</span> Config(), builder.createTopology());</span><br><span class="line">            &#125; <span class="keyword">catch</span> (AlreadyAliveException | InvalidTopologyException | AuthorizationException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            LocalCluster cluster = <span class="keyword">new</span> LocalCluster();</span><br><span class="line">            cluster.submitTopology(<span class="string">"LocalReadingFromKafkaApp"</span>,</span><br><span class="line">                    <span class="keyword">new</span> Config(), builder.createTopology());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> KafkaSpoutConfig&lt;String, String&gt; <span class="title">getKafkaSpoutConfig</span><span class="params">(String bootstrapServers, String topic)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> KafkaSpoutConfig.builder(bootstrapServers, topic)</span><br><span class="line">                <span class="comment">// 除了分组 ID,以下配置都是可选的。分组 ID 必须指定,否则会抛出 InvalidGroupIdException 异常</span></span><br><span class="line">                .setProp(ConsumerConfig.GROUP_ID_CONFIG, <span class="string">"kafkaSpoutTestGroup"</span>)</span><br><span class="line">                <span class="comment">// 定义重试策略</span></span><br><span class="line">                .setRetry(getRetryService())</span><br><span class="line">                <span class="comment">// 定时提交偏移量的时间间隔,默认是 15s</span></span><br><span class="line">                .setOffsetCommitPeriodMs(<span class="number">10_000</span>)</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义重试策略</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> KafkaSpoutRetryService <span class="title">getRetryService</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> KafkaSpoutRetryExponentialBackoff(TimeInterval.microSeconds(<span class="number">500</span>),</span><br><span class="line">                TimeInterval.milliSeconds(<span class="number">2</span>), Integer.MAX_VALUE, TimeInterval.seconds(<span class="number">10</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-3-LogConsoleBolt"><a href="#3-3-LogConsoleBolt" class="headerlink" title="3.3 LogConsoleBolt"></a>3.3 LogConsoleBolt</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 打印从 Kafka 中获取的数据</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LogConsoleBolt</span> <span class="keyword">extends</span> <span class="title">BaseRichBolt</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> OutputCollector collector;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">(Map stormConf, TopologyContext context, OutputCollector collector)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.collector=collector;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Tuple input)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            String value = input.getStringByField(<span class="string">"value"</span>);</span><br><span class="line">            System.out.println(<span class="string">"received from kafka : "</span>+ value);</span><br><span class="line">            <span class="comment">// 必须 ack,否则会重复消费 kafka 中的消息</span></span><br><span class="line">            collector.ack(input);</span><br><span class="line">        &#125;<span class="keyword">catch</span> (Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            collector.fail(input);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer declarer)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里从 <code>value</code> 字段中获取 kafka 输出的值数据。</p><p>在开发中，我们可以通过继承 <code>RecordTranslator</code> 接口定义了 Kafka 中 Record 与输出流之间的映射关系，可以在构建 <code>KafkaSpoutConfig</code> 的时候通过构造器或者 <code>setRecordTranslator()</code> 方法传入，并最后传递给具体的 <code>KafkaSpout</code>。</p><p>默认情况下使用内置的 <code>DefaultRecordTranslator</code>，其源码如下，<code>FIELDS</code> 中 定义了 tuple 中所有可用的字段：主题，分区，偏移量，消息键，值。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultRecordTranslator</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; <span class="keyword">implements</span> <span class="title">RecordTranslator</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = -<span class="number">5782462870112305750L</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Fields FIELDS = <span class="keyword">new</span> Fields(<span class="string">"topic"</span>, <span class="string">"partition"</span>, <span class="string">"offset"</span>, <span class="string">"key"</span>, <span class="string">"value"</span>);</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;Object&gt; <span class="title">apply</span><span class="params">(ConsumerRecord&lt;K, V&gt; record)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Values(record.topic(),</span><br><span class="line">                record.partition(),</span><br><span class="line">                record.offset(),</span><br><span class="line">                record.key(),</span><br><span class="line">                record.value());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Fields <span class="title">getFieldsFor</span><span class="params">(String stream)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> FIELDS;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;String&gt; <span class="title">streams</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> DEFAULT_STREAM;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-4-启动测试"><a href="#3-4-启动测试" class="headerlink" title="3.4 启动测试"></a>3.4 启动测试</h3><p>这里启动一个生产者用于发送测试数据，启动命令如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> bin/kafka-console-producer.sh --broker-list hadoop001:9092 --topic storm-topic</span></span><br></pre></td></tr></table></figure><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/storm-kafka-producer.png"> </div><p>本地运行的项目接收到从 Kafka 发送过来的数据：</p><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/storm-kafka-receiver.png"> </div><p><br></p><blockquote><p>用例源码下载地址：<a href="https://github.com/heibaiying/BigData-Notes/tree/master/code/Storm/storm-kafka-integration" target="_blank" rel="noopener">storm-kafka-integration</a></p></blockquote><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ol><li><a href="http://storm.apache.org/releases/2.0.0-SNAPSHOT/storm-kafka-client.html" target="_blank" rel="noopener">Storm Kafka Integration (0.10.x+)</a></li></ol>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="Storm" scheme="https://icocos.github.io/categories/Storm/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="Kafka" scheme="https://icocos.github.io/tags/Kafka/"/>
    
      <category term="Storm" scheme="https://icocos.github.io/tags/Storm/"/>
    
  </entry>
  
  <entry>
    <title>大数据之Storm和流处理简介</title>
    <link href="https://icocos.github.io/2019/10/09/%E5%A4%A7%E6%95%B0%E6%8D%AE%E4%B9%8BStorm%E5%92%8C%E6%B5%81%E5%A4%84%E7%90%86%E7%AE%80%E4%BB%8B/"/>
    <id>https://icocos.github.io/2019/10/09/大数据之Storm和流处理简介/</id>
    <published>2019-10-09T14:51:39.000Z</published>
    <updated>2020-04-09T14:45:08.846Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h1 id="Storm和流处理简介"><a href="#Storm和流处理简介" class="headerlink" title="Storm和流处理简介"></a>Storm和流处理简介</h1><nav><br><a href="#一Storm">一、Storm</a><br><br><a href="#11-简介">1.1 简介</a><br><br><a href="#12-Storm-与-Hadoop对比">1.2 Storm 与 Hadoop对比</a><br><br><a href="#13-Storm-与Spark-Streaming对比">1.3 Storm 与  Spark Streaming对比</a><br><br><a href="#14-Storm-与-Flink对比">1.4 Storm 与 Flink对比</a><br><br><a href="#二流处理">二、流处理</a><br><br><a href="#21-静态数据处理">2.1 静态数据处理</a><br><br><a href="#22-流处理">2.2 流处理</a><br><br></nav><h2 id="一、Storm"><a href="#一、Storm" class="headerlink" title="一、Storm"></a>一、Storm</h2><h4 id="1-1-简介"><a href="#1-1-简介" class="headerlink" title="1.1 简介"></a>1.1 简介</h4><p>Storm 是一个开源的分布式实时计算框架，可以以简单、可靠的方式进行大数据流的处理。通常用于实时分析，在线机器学习、持续计算、分布式 RPC、ETL 等场景。Storm 具有以下特点：</p><ul><li>支持水平横向扩展；</li><li>具有高容错性，通过 ACK 机制每个消息都不丢失；</li><li>处理速度非常快，每个节点每秒能处理超过一百万个 tuples ；</li><li>易于设置和操作，并可以与任何编程语言一起使用；</li><li>支持本地模式运行，对于开发人员来说非常友好；</li><li>支持图形化管理界面。</li></ul><h4 id="1-2-Storm-与-Hadoop对比"><a href="#1-2-Storm-与-Hadoop对比" class="headerlink" title="1.2 Storm 与 Hadoop对比"></a>1.2 Storm 与 Hadoop对比</h4><p>Hadoop 采用 MapReduce 处理数据，而 MapReduce 主要是对数据进行批处理，这使得 Hadoop 更适合于海量数据离线处理的场景。而 Strom 的设计目标是对数据进行实时计算，这使得其更适合实时数据分析的场景。</p><h4 id="1-3-Storm-与-Spark-Streaming对比"><a href="#1-3-Storm-与-Spark-Streaming对比" class="headerlink" title="1.3 Storm 与 Spark Streaming对比"></a>1.3 Storm 与 Spark Streaming对比</h4><p>Spark Streaming 并不是真正意义上的流处理框架。 Spark Streaming 接收实时输入的数据流，并将数据拆分为一系列批次，然后进行微批处理。只不过 Spark Streaming 能够将数据流进行极小粒度的拆分，使得其能够得到接近于流处理的效果，但其本质上还是批处理（或微批处理）。</p><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/streaming-flow.png"> </div><h4 id="1-4-Strom-与-Flink对比"><a href="#1-4-Strom-与-Flink对比" class="headerlink" title="1.4 Strom 与 Flink对比"></a>1.4 Strom 与 Flink对比</h4><p>storm 和 Flink 都是真正意义上的实时计算框架。其对比如下：</p><table><thead><tr><th></th><th>storm</th><th>flink</th></tr></thead><tbody><tr><td>状态管理</td><td>无状态</td><td>有状态</td></tr><tr><td>窗口支持</td><td>对事件窗口支持较弱，缓存整个窗口的所有数据，窗口结束时一起计算</td><td>窗口支持较为完善，自带一些窗口聚合方法，<br>并且会自动管理窗口状态</td></tr><tr><td>消息投递</td><td>At Most  Once<br>At Least Once</td><td>At Most  Once<br>At Least Once<br><strong>Exactly Once</strong></td></tr><tr><td>容错方式</td><td>ACK 机制：对每个消息进行全链路跟踪，失败或者超时时候进行重发</td><td>检查点机制：通过分布式一致性快照机制，<br>对数据流和算子状态进行保存。在发生错误时，使系统能够进行回滚。</td></tr></tbody></table><blockquote><p>注  :  对于消息投递，一般有以下三种方案：</p><ul><li>At Most Once : 保证每个消息会被投递 0 次或者 1 次，在这种机制下消息很有可能会丢失；</li><li>At Least Once : 保证了每个消息会被默认投递多次，至少保证有一次被成功接收，信息可能有重复，但是不会丢失；</li><li>Exactly Once  :  每个消息对于接收者而言正好被接收一次，保证即不会丢失也不会重复。</li></ul></blockquote><h2 id="二、流处理"><a href="#二、流处理" class="headerlink" title="二、流处理"></a>二、流处理</h2><h4 id="2-1-静态数据处理"><a href="#2-1-静态数据处理" class="headerlink" title="2.1 静态数据处理"></a>2.1 静态数据处理</h4><p>在流处理之前，数据通常存储在数据库或文件系统中，应用程序根据需要查询或计算数据，这就是传统的静态数据处理架构。Hadoop 采用 HDFS 进行数据存储，采用 MapReduce 进行数据查询或分析，这就是典型的静态数据处理架构。</p><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/01_data_at_rest_infrastructure.png"> </div><h4 id="2-2-流处理"><a href="#2-2-流处理" class="headerlink" title="2.2 流处理"></a>2.2 流处理</h4><p>而流处理则是直接对运动中数据的处理，在接收数据的同时直接计算数据。实际上，在真实世界中的大多数数据都是连续的流，如传感器数据，网站用户活动数据，金融交易数据等等 ，所有这些数据都是随着时间的推移而源源不断地产生。</p><p>接收和发送数据流并执行应用程序或分析逻辑的系统称为<strong>流处理器</strong>。流处理器的基本职责是确保数据有效流动，同时具备可扩展性和容错能力，Storm 和 Flink 就是其代表性的实现。</p><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/02_stream_processing_infrastructure.png"> </div><p>流处理带来了很多优点：</p><ul><li><p><strong>可以立即对数据做出反应</strong>：降低了数据的滞后性，使得数据更具有时效性，更能反映对未来的预期；</p></li><li><p><strong>可以处理更大的数据量</strong>：直接处理数据流，并且只保留数据中有意义的子集，然后将其传送到下一个处理单元，通过逐级过滤数据，从而降低实际需要处理的数据量；</p></li><li><p><strong>更贴近现实的数据模型</strong>：在实际的环境中，一切数据都是持续变化的，想要通过历史数据推断未来的趋势，必须保证数据的不断输入和模型的持续修正，典型的就是金融市场、股票市场，流处理能更好地处理这些场景下对数据连续性和及时性的需求；</p></li><li><p><strong>分散和分离基础设施</strong>：流式处理减少了对大型数据库的需求。每个流处理程序通过流处理框架维护了自己的数据和状态，这使其更适合于当下最流行的微服务架构。</p></li></ul><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ol><li><a href="https://www.ververica.com/what-is-stream-processing" target="_blank" rel="noopener">What is stream processing?</a></li><li><a href="http://bigdata.51cto.com/art/201711/558416.htm" target="_blank" rel="noopener">流计算框架 Flink 与 Storm 的性能对比</a></li></ol>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="Storm" scheme="https://icocos.github.io/categories/Storm/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="Storm" scheme="https://icocos.github.io/tags/Storm/"/>
    
      <category term="流处理" scheme="https://icocos.github.io/tags/%E6%B5%81%E5%A4%84%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>大数据之Storm集成HBase和HDFS</title>
    <link href="https://icocos.github.io/2019/10/05/%E5%A4%A7%E6%95%B0%E6%8D%AE%E4%B9%8BStorm%E9%9B%86%E6%88%90HBase%E5%92%8CHDFS/"/>
    <id>https://icocos.github.io/2019/10/05/大数据之Storm集成HBase和HDFS/</id>
    <published>2019-10-05T14:29:37.000Z</published>
    <updated>2020-04-09T14:45:08.833Z</updated>
    
    <content type="html"><![CDATA[<hr><a id="more"></a><h1 id="Storm集成HDFS和HBase"><a href="#Storm集成HDFS和HBase" class="headerlink" title="Storm集成HDFS和HBase"></a>Storm集成HDFS和HBase</h1><nav><br><a href="#一Storm集成HDFS">一、Storm集成HDFS</a><br><br><a href="#二Storm集成HBase">二、Storm集成HBase</a><br><br></nav><h2 id="一、Storm集成HDFS"><a href="#一、Storm集成HDFS" class="headerlink" title="一、Storm集成HDFS"></a>一、Storm集成HDFS</h2><h3 id="1-1-项目结构"><a href="#1-1-项目结构" class="headerlink" title="1.1 项目结构"></a>1.1 项目结构</h3><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/datasourcetohdfs.png"> </div><blockquote><p>本用例源码下载地址：<a href="https://github.com/heibaiying/BigData-Notes/tree/master/code/Storm/storm-hdfs-integration" target="_blank" rel="noopener">storm-hdfs-integration</a></p></blockquote><h3 id="1-2-项目主要依赖"><a href="#1-2-项目主要依赖" class="headerlink" title="1.2 项目主要依赖"></a>1.2 项目主要依赖</h3><p>项目主要依赖如下，有两个地方需要注意：</p><ul><li>这里由于我服务器上安装的是 CDH 版本的 Hadoop，在导入依赖时引入的也是 CDH 版本的依赖，需要使用 <code>&lt;repository&gt;</code> 标签指定 CDH 的仓库地址；</li><li><code>hadoop-common</code>、<code>hadoop-client</code>、<code>hadoop-hdfs</code> 均需要排除 <code>slf4j-log4j12</code> 依赖，原因是 <code>storm-core</code> 中已经有该依赖，不排除的话有 JAR 包冲突的风险；</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">storm.version</span>&gt;</span>1.2.2<span class="tag">&lt;/<span class="name">storm.version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">repositories</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">repository</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span>&gt;</span>cloudera<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://repository.cloudera.com/artifactory/cloudera-repos/<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">repository</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">repositories</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.storm<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>storm-core<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;storm.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--Storm 整合 HDFS 依赖--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.storm<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>storm-hdfs<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;storm.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.hadoop<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>hadoop-common<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.6.0-cdh5.15.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">exclusions</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">exclusion</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>slf4j-log4j12<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">exclusion</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">exclusions</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.hadoop<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>hadoop-client<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.6.0-cdh5.15.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">exclusions</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">exclusion</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>slf4j-log4j12<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">exclusion</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">exclusions</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.hadoop<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>hadoop-hdfs<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.6.0-cdh5.15.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">exclusions</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">exclusion</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>slf4j-log4j12<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">exclusion</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">exclusions</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="1-3-DataSourceSpout"><a href="#1-3-DataSourceSpout" class="headerlink" title="1.3 DataSourceSpout"></a>1.3 DataSourceSpout</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 产生词频样本的数据源</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DataSourceSpout</span> <span class="keyword">extends</span> <span class="title">BaseRichSpout</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; list = Arrays.asList(<span class="string">"Spark"</span>, <span class="string">"Hadoop"</span>, <span class="string">"HBase"</span>, <span class="string">"Storm"</span>, <span class="string">"Flink"</span>, <span class="string">"Hive"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> SpoutOutputCollector spoutOutputCollector;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">open</span><span class="params">(Map map, TopologyContext topologyContext, SpoutOutputCollector spoutOutputCollector)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.spoutOutputCollector = spoutOutputCollector;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">nextTuple</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 模拟产生数据</span></span><br><span class="line">        String lineData = productData();</span><br><span class="line">        spoutOutputCollector.emit(<span class="keyword">new</span> Values(lineData));</span><br><span class="line">        Utils.sleep(<span class="number">1000</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer outputFieldsDeclarer)</span> </span>&#123;</span><br><span class="line">        outputFieldsDeclarer.declare(<span class="keyword">new</span> Fields(<span class="string">"line"</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 模拟数据</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> String <span class="title">productData</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Collections.shuffle(list);</span><br><span class="line">        Random random = <span class="keyword">new</span> Random();</span><br><span class="line">        <span class="keyword">int</span> endIndex = random.nextInt(list.size()) % (list.size()) + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">return</span> StringUtils.join(list.toArray(), <span class="string">"\t"</span>, <span class="number">0</span>, endIndex);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>产生的模拟数据格式如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">SparkHBase</span><br><span class="line">HiveFlinkStormHadoopHBaseSpark</span><br><span class="line">Flink</span><br><span class="line">HBaseStorm</span><br><span class="line">HBaseHadoopHiveFlink</span><br><span class="line">HBaseFlinkHiveStorm</span><br><span class="line">HiveFlinkHadoop</span><br><span class="line">HBaseHive</span><br><span class="line">HadoopSparkHBaseStorm</span><br></pre></td></tr></table></figure><h3 id="1-4-将数据存储到HDFS"><a href="#1-4-将数据存储到HDFS" class="headerlink" title="1.4 将数据存储到HDFS"></a>1.4 将数据存储到HDFS</h3><p>这里 HDFS 的地址和数据存储路径均使用了硬编码，在实际开发中可以通过外部传参指定，这样程序更为灵活。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DataToHdfsApp</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String DATA_SOURCE_SPOUT = <span class="string">"dataSourceSpout"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String HDFS_BOLT = <span class="string">"hdfsBolt"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 指定 Hadoop 的用户名 如果不指定,则在 HDFS 创建目录时候有可能抛出无权限的异常 (RemoteException: Permission denied)</span></span><br><span class="line">        System.setProperty(<span class="string">"HADOOP_USER_NAME"</span>, <span class="string">"root"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 定义输出字段 (Field) 之间的分隔符</span></span><br><span class="line">        RecordFormat format = <span class="keyword">new</span> DelimitedRecordFormat()</span><br><span class="line">                .withFieldDelimiter(<span class="string">"|"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 同步策略: 每 100 个 tuples 之后就会把数据从缓存刷新到 HDFS 中</span></span><br><span class="line">        SyncPolicy syncPolicy = <span class="keyword">new</span> CountSyncPolicy(<span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 文件策略: 每个文件大小上限 1M,超过限定时,创建新文件并继续写入</span></span><br><span class="line">        FileRotationPolicy rotationPolicy = <span class="keyword">new</span> FileSizeRotationPolicy(<span class="number">1.0f</span>, Units.MB);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 定义存储路径</span></span><br><span class="line">        FileNameFormat fileNameFormat = <span class="keyword">new</span> DefaultFileNameFormat()</span><br><span class="line">                .withPath(<span class="string">"/storm-hdfs/"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 定义 HdfsBolt</span></span><br><span class="line">        HdfsBolt hdfsBolt = <span class="keyword">new</span> HdfsBolt()</span><br><span class="line">                .withFsUrl(<span class="string">"hdfs://hadoop001:8020"</span>)</span><br><span class="line">                .withFileNameFormat(fileNameFormat)</span><br><span class="line">                .withRecordFormat(format)</span><br><span class="line">                .withRotationPolicy(rotationPolicy)</span><br><span class="line">                .withSyncPolicy(syncPolicy);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 构建 Topology</span></span><br><span class="line">        TopologyBuilder builder = <span class="keyword">new</span> TopologyBuilder();</span><br><span class="line">        builder.setSpout(DATA_SOURCE_SPOUT, <span class="keyword">new</span> DataSourceSpout());</span><br><span class="line">        <span class="comment">// save to HDFS</span></span><br><span class="line">        builder.setBolt(HDFS_BOLT, hdfsBolt, <span class="number">1</span>).shuffleGrouping(DATA_SOURCE_SPOUT);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果外部传参 cluster 则代表线上环境启动,否则代表本地启动</span></span><br><span class="line">        <span class="keyword">if</span> (args.length &gt; <span class="number">0</span> &amp;&amp; args[<span class="number">0</span>].equals(<span class="string">"cluster"</span>)) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                StormSubmitter.submitTopology(<span class="string">"ClusterDataToHdfsApp"</span>, <span class="keyword">new</span> Config(), builder.createTopology());</span><br><span class="line">            &#125; <span class="keyword">catch</span> (AlreadyAliveException | InvalidTopologyException | AuthorizationException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            LocalCluster cluster = <span class="keyword">new</span> LocalCluster();</span><br><span class="line">            cluster.submitTopology(<span class="string">"LocalDataToHdfsApp"</span>,</span><br><span class="line">                    <span class="keyword">new</span> Config(), builder.createTopology());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="1-5-启动测试"><a href="#1-5-启动测试" class="headerlink" title="1.5 启动测试"></a>1.5 启动测试</h3><p>可以用直接使用本地模式运行，也可以打包后提交到服务器集群运行。本仓库提供的源码默认采用 <code>maven-shade-plugin</code> 进行打包，打包命令如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> mvn clean package -D maven.test.skip=<span class="literal">true</span></span></span><br></pre></td></tr></table></figure><p>运行后，数据会存储到 HDFS 的 <code>/storm-hdfs</code> 目录下。使用以下命令可以查看目录内容：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 查看目录内容</span></span><br><span class="line">hadoop fs -ls /storm-hdfs</span><br><span class="line"><span class="meta">#</span><span class="bash"> 监听文内容变化</span></span><br><span class="line">hadoop fs -tail -f /strom-hdfs/文件名</span><br></pre></td></tr></table></figure><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/storm-hdfs-result.png"> </div><h2 id="二、Storm集成HBase"><a href="#二、Storm集成HBase" class="headerlink" title="二、Storm集成HBase"></a>二、Storm集成HBase</h2><h3 id="2-1-项目结构"><a href="#2-1-项目结构" class="headerlink" title="2.1 项目结构"></a>2.1 项目结构</h3><p>集成用例： 进行词频统计并将最后的结果存储到 HBase，项目主要结构如下：</p><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/WordCountToHBaseApp.png"> </div><blockquote><p>本用例源码下载地址：<a href="https://github.com/heibaiying/BigData-Notes/tree/master/code/Storm/storm-hbase-integration" target="_blank" rel="noopener">storm-hbase-integration</a></p></blockquote><h3 id="2-2-项目主要依赖"><a href="#2-2-项目主要依赖" class="headerlink" title="2.2 项目主要依赖"></a>2.2 项目主要依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">storm.version</span>&gt;</span>1.2.2<span class="tag">&lt;/<span class="name">storm.version</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">           <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.storm<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">           <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>storm-core<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">           <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;storm.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">       <span class="comment">&lt;!--Storm 整合 HBase 依赖--&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">           <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.storm<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">           <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>storm-hbase<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">           <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;storm.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="2-3-DataSourceSpout"><a href="#2-3-DataSourceSpout" class="headerlink" title="2.3 DataSourceSpout"></a>2.3 DataSourceSpout</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 产生词频样本的数据源</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DataSourceSpout</span> <span class="keyword">extends</span> <span class="title">BaseRichSpout</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; list = Arrays.asList(<span class="string">"Spark"</span>, <span class="string">"Hadoop"</span>, <span class="string">"HBase"</span>, <span class="string">"Storm"</span>, <span class="string">"Flink"</span>, <span class="string">"Hive"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> SpoutOutputCollector spoutOutputCollector;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">open</span><span class="params">(Map map, TopologyContext topologyContext, SpoutOutputCollector spoutOutputCollector)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.spoutOutputCollector = spoutOutputCollector;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">nextTuple</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 模拟产生数据</span></span><br><span class="line">        String lineData = productData();</span><br><span class="line">        spoutOutputCollector.emit(<span class="keyword">new</span> Values(lineData));</span><br><span class="line">        Utils.sleep(<span class="number">1000</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer outputFieldsDeclarer)</span> </span>&#123;</span><br><span class="line">        outputFieldsDeclarer.declare(<span class="keyword">new</span> Fields(<span class="string">"line"</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 模拟数据</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> String <span class="title">productData</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Collections.shuffle(list);</span><br><span class="line">        Random random = <span class="keyword">new</span> Random();</span><br><span class="line">        <span class="keyword">int</span> endIndex = random.nextInt(list.size()) % (list.size()) + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">return</span> StringUtils.join(list.toArray(), <span class="string">"\t"</span>, <span class="number">0</span>, endIndex);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>产生的模拟数据格式如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">SparkHBase</span><br><span class="line">HiveFlinkStormHadoopHBaseSpark</span><br><span class="line">Flink</span><br><span class="line">HBaseStorm</span><br><span class="line">HBaseHadoopHiveFlink</span><br><span class="line">HBaseFlinkHiveStorm</span><br><span class="line">HiveFlinkHadoop</span><br><span class="line">HBaseHive</span><br><span class="line">HadoopSparkHBaseStorm</span><br></pre></td></tr></table></figure><h3 id="2-4-SplitBolt"><a href="#2-4-SplitBolt" class="headerlink" title="2.4 SplitBolt"></a>2.4 SplitBolt</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将每行数据按照指定分隔符进行拆分</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SplitBolt</span> <span class="keyword">extends</span> <span class="title">BaseRichBolt</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> OutputCollector collector;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">(Map stormConf, TopologyContext context, OutputCollector collector)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.collector = collector;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Tuple input)</span> </span>&#123;</span><br><span class="line">        String line = input.getStringByField(<span class="string">"line"</span>);</span><br><span class="line">        String[] words = line.split(<span class="string">"\t"</span>);</span><br><span class="line">        <span class="keyword">for</span> (String word : words) &#123;</span><br><span class="line">            collector.emit(tuple(word, <span class="number">1</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer declarer)</span> </span>&#123;</span><br><span class="line">        declarer.declare(<span class="keyword">new</span> Fields(<span class="string">"word"</span>, <span class="string">"count"</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-5-CountBolt"><a href="#2-5-CountBolt" class="headerlink" title="2.5 CountBolt"></a>2.5 CountBolt</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 进行词频统计</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CountBolt</span> <span class="keyword">extends</span> <span class="title">BaseRichBolt</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, Integer&gt; counts = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> OutputCollector collector;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">(Map stormConf, TopologyContext context, OutputCollector collector)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.collector=collector;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Tuple input)</span> </span>&#123;</span><br><span class="line">        String word = input.getStringByField(<span class="string">"word"</span>);</span><br><span class="line">        Integer count = counts.get(word);</span><br><span class="line">        <span class="keyword">if</span> (count == <span class="keyword">null</span>) &#123;</span><br><span class="line">            count = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        count++;</span><br><span class="line">        counts.put(word, count);</span><br><span class="line">        <span class="comment">// 输出</span></span><br><span class="line">        collector.emit(<span class="keyword">new</span> Values(word, String.valueOf(count)));</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">declareOutputFields</span><span class="params">(OutputFieldsDeclarer declarer)</span> </span>&#123;</span><br><span class="line">        declarer.declare(<span class="keyword">new</span> Fields(<span class="string">"word"</span>, <span class="string">"count"</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-6-WordCountToHBaseApp"><a href="#2-6-WordCountToHBaseApp" class="headerlink" title="2.6 WordCountToHBaseApp"></a>2.6 WordCountToHBaseApp</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 进行词频统计 并将统计结果存储到 HBase 中</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WordCountToHBaseApp</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String DATA_SOURCE_SPOUT = <span class="string">"dataSourceSpout"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String SPLIT_BOLT = <span class="string">"splitBolt"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String COUNT_BOLT = <span class="string">"countBolt"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String HBASE_BOLT = <span class="string">"hbaseBolt"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// storm 的配置</span></span><br><span class="line">        Config config = <span class="keyword">new</span> Config();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// HBase 的配置</span></span><br><span class="line">        Map&lt;String, Object&gt; hbConf = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line">        hbConf.put(<span class="string">"hbase.rootdir"</span>, <span class="string">"hdfs://hadoop001:8020/hbase"</span>);</span><br><span class="line">        hbConf.put(<span class="string">"hbase.zookeeper.quorum"</span>, <span class="string">"hadoop001:2181"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 将 HBase 的配置传入 Storm 的配置中</span></span><br><span class="line">        config.put(<span class="string">"hbase.conf"</span>, hbConf);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 定义流数据与 HBase 中数据的映射</span></span><br><span class="line">        SimpleHBaseMapper mapper = <span class="keyword">new</span> SimpleHBaseMapper()</span><br><span class="line">                .withRowKeyField(<span class="string">"word"</span>)</span><br><span class="line">                .withColumnFields(<span class="keyword">new</span> Fields(<span class="string">"word"</span>,<span class="string">"count"</span>))</span><br><span class="line">                .withColumnFamily(<span class="string">"info"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * 给 HBaseBolt 传入表名、数据映射关系、和 HBase 的配置信息</span></span><br><span class="line"><span class="comment">         * 表需要预先创建: create 'WordCount','info'</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        HBaseBolt hbase = <span class="keyword">new</span> HBaseBolt(<span class="string">"WordCount"</span>, mapper)</span><br><span class="line">                .withConfigKey(<span class="string">"hbase.conf"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 构建 Topology</span></span><br><span class="line">        TopologyBuilder builder = <span class="keyword">new</span> TopologyBuilder();</span><br><span class="line">        builder.setSpout(DATA_SOURCE_SPOUT, <span class="keyword">new</span> DataSourceSpout(),<span class="number">1</span>);</span><br><span class="line">        <span class="comment">// split</span></span><br><span class="line">        builder.setBolt(SPLIT_BOLT, <span class="keyword">new</span> SplitBolt(), <span class="number">1</span>).shuffleGrouping(DATA_SOURCE_SPOUT);</span><br><span class="line">        <span class="comment">// count</span></span><br><span class="line">        builder.setBolt(COUNT_BOLT, <span class="keyword">new</span> CountBolt(),<span class="number">1</span>).shuffleGrouping(SPLIT_BOLT);</span><br><span class="line">        <span class="comment">// save to HBase</span></span><br><span class="line">        builder.setBolt(HBASE_BOLT, hbase, <span class="number">1</span>).shuffleGrouping(COUNT_BOLT);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果外部传参 cluster 则代表线上环境启动,否则代表本地启动</span></span><br><span class="line">        <span class="keyword">if</span> (args.length &gt; <span class="number">0</span> &amp;&amp; args[<span class="number">0</span>].equals(<span class="string">"cluster"</span>)) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                StormSubmitter.submitTopology(<span class="string">"ClusterWordCountToRedisApp"</span>, config, builder.createTopology());</span><br><span class="line">            &#125; <span class="keyword">catch</span> (AlreadyAliveException | InvalidTopologyException | AuthorizationException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            LocalCluster cluster = <span class="keyword">new</span> LocalCluster();</span><br><span class="line">            cluster.submitTopology(<span class="string">"LocalWordCountToRedisApp"</span>,</span><br><span class="line">                    config, builder.createTopology());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-7-启动测试"><a href="#2-7-启动测试" class="headerlink" title="2.7 启动测试"></a>2.7 启动测试</h3><p>可以用直接使用本地模式运行，也可以打包后提交到服务器集群运行。本仓库提供的源码默认采用 <code>maven-shade-plugin</code> 进行打包，打包命令如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> mvn clean package -D maven.test.skip=<span class="literal">true</span></span></span><br></pre></td></tr></table></figure><p>运行后，数据会存储到 HBase 的 <code>WordCount</code> 表中。使用以下命令查看表的内容：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hbase &gt;  scan 'WordCount'</span><br></pre></td></tr></table></figure><div align="center"> <img src="https://raw.githubusercontent.com/iCocos/icocos_hexo_images/master/2020/god_of_bd/pictures/storm-hbase-result.png"> </div><h3 id="2-8-withCounterFields"><a href="#2-8-withCounterFields" class="headerlink" title="2.8 withCounterFields"></a>2.8 withCounterFields</h3><p>在上面的用例中我们是手动编码来实现词频统计，并将最后的结果存储到 HBase 中。其实也可以在构建 <code>SimpleHBaseMapper</code> 的时候通过 <code>withCounterFields</code> 指定 count 字段，被指定的字段会自动进行累加操作，这样也可以实现词频统计。需要注意的是 withCounterFields 指定的字段必须是 Long 类型，不能是 String 类型。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">SimpleHBaseMapper mapper = <span class="keyword">new</span> SimpleHBaseMapper() </span><br><span class="line">        .withRowKeyField(<span class="string">"word"</span>)</span><br><span class="line">        .withColumnFields(<span class="keyword">new</span> Fields(<span class="string">"word"</span>))</span><br><span class="line">        .withCounterFields(<span class="keyword">new</span> Fields(<span class="string">"count"</span>))</span><br><span class="line">        .withColumnFamily(<span class="string">"cf"</span>);</span><br></pre></td></tr></table></figure><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ol><li><a href="http://storm.apache.org/releases/2.0.0-SNAPSHOT/storm-hdfs.html" target="_blank" rel="noopener">Apache HDFS Integration</a></li><li><a href="http://storm.apache.org/releases/2.0.0-SNAPSHOT/storm-hbase.html" target="_blank" rel="noopener">Apache HBase Integration</a></li></ol>]]></content>
    
    <summary type="html">
    
      &lt;hr&gt;
    
    </summary>
    
      <category term="Storm" scheme="https://icocos.github.io/categories/Storm/"/>
    
    
      <category term="大数据" scheme="https://icocos.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="HBase" scheme="https://icocos.github.io/tags/HBase/"/>
    
      <category term="HDFS" scheme="https://icocos.github.io/tags/HDFS/"/>
    
      <category term="Storm" scheme="https://icocos.github.io/tags/Storm/"/>
    
  </entry>
  
</feed>
