Hadoop-和-Flume-分布式日志收集-全-
Hadoop 和 Flume 分布式日志收集(全)
零、前言
Hadoop 是一个很棒的开源工具,可以将大量非结构化数据筛选成可管理的数据,以便您的企业能够更好地洞察您的客户和需求。 它很便宜(大部分可以是免费的),只要你的数据中心有空间和电力,它就可以水平扩展,并且可以处理你的传统数据仓库可能会被压垮的问题。 也就是说,一个鲜为人知的秘密是,您的 Hadoop 集群需要您向其提供数据;否则,您只会拥有一个非常昂贵的发热器。 一旦您度过了使用 Hadoop 的“玩耍”阶段,您很快就会发现,您将需要一个工具来自动将数据馈送到您的集群中。 在过去,你必须为这个问题想出一个解决方案,但仅此而已! Flume 一开始是 Cloudera 的一个项目,当时他们的集成工程师不得不一遍又一遍地编写工具,让客户自动导入数据。 如今,该项目与 Apache Foundation 一起工作,正在积极开发中,并拥有多年来一直在其生产环境中使用它的用户。
在这本书中,我希望通过 Flume 的架构概述和快速入门指南来帮助您快速上手。 之后,我们将深入研究许多更有用的 Flume 组件的细节,包括用于持久化动态数据记录的非常重要的 File Channel,以及用于缓冲数据并将数据写入HDFS(Hadoop 分布式文件系统)的 HDFS Sink。 由于 Flume 附带了各种各样的模块,因此您需要开始使用的唯一工具很可能是配置文件的文本编辑器。
到本书结束时,您应该已经掌握了足够的知识来构建一个为 Hadoop 集群提供支持的高可用性、容错、流数据管道。
这本书涵盖了哪些内容
第 1 章,概述和体系结构向读者介绍了 Flume 以及它试图解决的问题空间(特别是关于 Hadoop)。 我们将在后面的章节中给出各种组件的体系结构概述。
第 2 章,Flume Quick Start帮助您快速启动和运行,包括下载 Flume、创建“Hello World”配置并运行它。
第三章通道涵盖了大多数人将使用的两个主要通道以及每个通道的配置选项。
第 4 章,接收器和接收器处理器详细介绍了如何使用 HDFS Flume 输出,包括压缩选项和格式化数据的选项。 还介绍了故障转移选项,以创建更强大的数据管道。
第 5 章,源和通道选择器将介绍几种 Flume 输入机制及其配置选项。 涵盖了基于数据内容的不同通道之间的切换,允许创建复杂的数据流。
第 6 章,拦截器、ETL 和路由解释了如何转换飞行中的数据,以及如何从有效负载中提取信息以与通道选择器一起使用来做出路由决策。 通过使用 Avro 序列化以及使用 Flume 命令行作为独立的 Avro 客户端手动测试和导入数据,介绍了对 Flume 代理进行分层。
第 7 章,监视 Flume讨论了可用于内部和外部监视 Flume 的各种选项,包括 Monit、Nagios、Ganglia 和自定义挂钩。
第 8 章,没有勺子-实时分布式数据收集的实际情况,是一组需要考虑的杂物,超出了仅配置和使用 Flume 的范围。
这本书你需要什么
您需要一台安装了 Java 虚拟机的计算机,因为 Flume 是用 Java 编写的。 如果您的计算机上没有 JAVA,可以从http://java.com/下载。
您还需要 Internet 连接,才能下载 Flume 来运行快速入门示例。
本书介绍了 Apache Flume 1.3.0,包括一些反向移植到 Cloudera 的 Flume CDH4 发行版中的项目。
这本书是给谁看的
本书面向负责实现将数据从各种系统自动移动到 Hadoop 集群的人员。 如果您的工作是定期将数据加载到 Hadoop 中,那么这本书应该会帮助您将自己的代码编写得不再繁琐,也不再需要编写一个只要您在公司工作就会支持的自定义工具。
只需要具备 HDFS 的基本 Hadoop 知识。 如果您的需要需要,本文还介绍了一些自定义实现。 对于这种级别的实现,您需要知道如何用 Java 编程。
最后,您需要您最喜欢的文本编辑器,因为本书的大部分内容介绍了如何通过代理的文本配置文件配置各种 Flume 组件。
公约
在这本书中,你会发现许多区分不同信息的文本样式。 以下是这些风格的一些示例,并解释了它们的含义。
文本中的代码词如下所示:“我们可以通过使用include
指令包括其他上下文。”
代码块设置如下:
agent.sinks.k1.hdfs.path=/logs/apache/access
agent.sinks.k1.hdfs.filePrefix=access
agent.sinks.k1.hdfs.fileSuffix=.log
当我们希望您注意代码块的特定部分时,相关行或项将以粗体显示:
agent.sources.s1.command=uptime
agent.sources.s1.restart=true
agent.sources.s1.restartThrottle=60000
任何命令行输入或输出都如下所示:
$ tar -zxf apache-flume-1.3.1.tar.gz
$ cd apache-flume-1.3.1
新术语和重要单词以粗体显示。
备注
警告或重要说明会出现在这样的框中。
提示
提示和技巧如下所示。
读者反馈
欢迎读者的反馈。 让我们知道你对这本书的看法-你喜欢什么或不喜欢什么。 读者反馈对于我们开发真正能让您获得最大收益的图书非常重要。
要向我们发送一般反馈,只需发送电子邮件至<[feedback@packtpub.com](mailto:feedback@packtpub.com)>
,并通过消息主题提及书名。 如果有一个您擅长的主题,并且您有兴趣撰写或投稿一本书,请参阅我们位于www.Packtpub.com/Authors上的作者指南。
客户支持
现在您已经成为 Packt 图书的拥有者,我们有很多东西可以帮助您从购买中获得最大价值。
勘误表
虽然我们已经竭尽全力确保内容的准确性,但错误还是会发生。 如果您在我们的一本书中发现错误--可能是文本或代码中的错误--如果您能向我们报告,我们将不胜感激。 通过这样做,您可以将其他读者从挫折中解救出来,并帮助我们改进本书的后续版本。 如果您发现任何勘误表,请访问http://www.packtpub.com/submit-errata,选择您的图书,单击勘误表提交表链接,然后输入勘误表的详细信息。 一旦您的勘误表被核实,您提交的勘误表将被接受,勘误表将被上传到我们的网站上,或添加到该标题勘误表部分下的任何现有勘误表列表中。 通过从http://www.packtpub.com/support中选择您的书目,可以查看任何现有勘误表。
盗版
在互联网上盗版版权材料是所有媒体持续存在的问题。 在 Packt,我们非常重视版权和许可证的保护。 如果您在互联网上发现任何形式的非法复制我们的作品,请立即提供我们的位置、地址或网站名称,以便我们采取补救措施。
请拨打<[copyright@packtpub.com](mailto:copyright@packtpub.com)>
与我们联系,并提供疑似盗版材料的链接。
我们感谢您在保护我们的作者方面的帮助,以及我们为您提供有价值内容的能力。
问题
如果您对本书的任何方面有任何问题,可以拨打<[questions@packtpub.com](mailto:questions@packtpub.com)>
与我们联系,我们将尽最大努力解决。
一、概述和架构
如果你正在读这本书,你很有可能会沉浸在堆积如山的数据中。 多亏了 Facebook、Twitter、亚马逊、数码相机和照相手机、YouTube、谷歌,以及你能想到的任何连接到互联网的东西,创建海量数据变得非常容易。 作为网站提供商,10 年前,您的应用日志仅用于帮助您排除网站故障。 今天,如果您知道如何从您的数据洪流中淘金,同样的数据可以为您的业务和客户提供宝贵的洞察力。
此外,由于您正在阅读这本书,您也知道创建 Hadoop 的目的是(部分)解决筛选海量数据的问题。 当然,这只有在您能够可靠地将数据加载到 Hadoop 集群以供数据科学家分析的情况下才有效。
将数据传入和传出 Hadoop(在本例中为Hadoop File System(HDFS))并不困难-它只是一个简单的命令,如下所示:
% hadoop fs --put data.csv .
当您将所有数据整齐打包并准备上传时,这一功能非常有用。
但你的网站一直在创建数据。 您应该多久将数据批量加载到 HDFS? 每天都有吗? 每小时? 不管你选择什么样的处理周期,最终总会有人问,“你能早点给我拿到数据吗?” 您真正需要的是一个能够处理流日志/数据的解决方案。
事实证明,你并不是唯一一个有这种需要的人。 Cloudera 是一家为 Hadoop 提供专业服务的提供商,也是他们自己的 Hadoop 发行版,在与他们的客户合作时,Cloudera 一次又一次地看到了这种需求。 Flume 就是为了满足这一需求而创建的,它创建了一个标准的、简单的、健壮的、灵活的和可扩展的工具,用于将数据摄取到 Hadoop 中。
Flume 0.9
Flume 于 2011 年首次引入 Cloudera 的 CDH3 发行版。 它由通过 ZooKeeper(一个联合配置和协调系统)从一个集中的主进程(或多个主进程)配置的工作者守护进程(代理)组成。 在主服务器上,您可以在 Web UI 中检查代理状态,也可以从 UI 或通过命令行 shell 集中推送配置(两者实际上都是通过 ZooKeeper 与辅助代理通信)。
数据可以以三种模式之一发送,即尽力而为(BE)、磁盘故障转移(DFO)和端到端(E2E)。 主机用于端到端(E2E)模式确认,而多主机配置从未真正成熟,因此通常只有一个主机使其成为 E2E 数据流的中心故障点。 尽力而为就像它听起来那样-代理将尝试发送数据,但如果不能,数据将被丢弃。 此模式适用于度量标准等可以轻松容忍差距的情况,因为新数据只有一秒钟的距离。 磁盘故障转移模式将无法传递的数据存储到本地磁盘(有时是本地数据库),并不断重试,直到可以将数据传递给数据流中的下一个收件人。 只要您有足够的本地磁盘空间来缓冲负载,这对于那些计划内(或计划外)的停机非常方便。
2011 年 6 月,Cloudera 将 Flume 项目的控制权移交给了 Apache 基金会。 一年后,它在 2012 年脱离了孵化器的地位。 在那个孵化年,“星际迷航”主题标签下的 Flume-NG(Flume The Next Generation)的重构工作已经开始。
Flume 1.X(Flume-NG)
Flume 被重构的原因有很多。 如果您对详细信息感兴趣,可以在https://issues.apache.org/jira/browse/FLUME-728上阅读。 最初的重构分支最终成为 Flume 1.X 的开发主线。
Flume 1.X 中最明显的变化是集中配置 Master/Master 和 ZooKeeper 消失了。 Flume 0.9 中的配置过于冗长,很容易出错。 此外,集中式配置确实超出了 Flume 的目标范围。 集中式配置被简单的磁盘配置文件取代(尽管配置提供程序是可插拔的,因此可以替换)。 这些配置文件可以使用 cf-engine、chef 和 plupet 等工具轻松分发。 如果您使用的是 Cloudera 发行版,请查看 Cloudera Manager 来管理您的配置-他们最近更改了许可以提高节点限制,因此对您来说这可能是一个有吸引力的选择。 请确保不要手动管理这些配置,否则您将永远手动编辑这些文件。
Flume 1.X 中的另一个主要区别是,输入数据的读取和输出数据的写入现在由不同的工作线程(称为运行器)处理。 在 Flume 0.9 中,输入线程还执行对输出的写入(故障转移重试除外)。 如果输出写入器速度慢(而不仅仅是彻底失败),它将阻止 Flume 接收数据的能力。 这种新的异步设计让输入线程完全没有意识到任何下游问题。
本书涵盖的 Flume 版本是 1.3.1(撰写本书时的当前版本)。
HDFS 和流数据/日志的问题
HDFS 不是真正的文件系统,至少不是传统意义上的文件系统,而且我们认为普通文件系统理所当然的许多东西在这里都不适用,例如能够挂载它。 这使得将流数据导入 Hadoop 变得更加复杂。
在常规的可移植操作系统接口(POSIX)样式的文件系统中,如果您打开一个文件并写入数据,那么在关闭该文件之前,该文件仍然存在于磁盘上。 也就是说,如果另一个程序打开相同的文件并开始读取,它将获得写入器已经刷新到磁盘的数据。 此外,如果写入过程中断,则写入磁盘的任何部分都是可用的(它可能不完整,但确实存在)。
在 HDFS 中,文件仅作为目录条目存在,在文件关闭之前,它会显示为零长度。 这意味着,如果数据在较长时间内写入文件而没有关闭,则与客户端断开网络连接后,您所做的所有工作只会留下一个空文件。 这可能会导致您得出这样的结论:编写小文件是明智的,这样您就可以尽快关闭它们。
问题是 Hadoop 不喜欢很多小文件。 由于 HDFS 元数据保存在 NameNode 上的内存中,因此创建的文件越多,需要使用的 RAM 就越多。 从 MapReduce 的角度来看,小文件会导致效率低下。 通常,每个映射器都会被指定单个文件块作为输入(除非您使用了某些压缩编解码器)。 如果您有很多小文件,启动工作进程的成本可能会比它正在处理的数据高得不成比例。 这种块碎片还会导致更多的映射器任务,增加总体作业运行时间。
在确定写入 HDFS 时要使用的循环周期时,需要权衡这些因素。 如果计划短期保留数据,那么您可以选择较小的文件大小。 但是,如果您计划将数据保留很长时间,则可以针对较大的文件或执行一些定期清理,将较小的文件压缩到较少的较大文件中,以使它们对 MapReduce 更加友好。 毕竟,您只能摄取一次数据,但是您可能会对该数据运行一个 MapReduce 作业数百次或数千次。
源、通道和汇
在这个简单的图中可以看到 Flume 代理的体系结构。 输入称为源,输出称为宿。 通道提供源和宿之间的粘合剂。 所有这些都在一个称为代理的守护进程中运行。
备注
我们应该牢记以下几点:
源将事件写入一个或多个通道。
当事件从信源传递到信宿时,信道是保持区域。
接收器仅从一个通道接收事件。
代理可以有许多源、通道和汇。
Flume 事件
Flume 传输的数据的基本有效负载称为事件。 一个事件由个或多个标头和一个正文组成。
标头是键/值对,可用于做出路由决策或携带其他结构化信息(如事件的时间戳或事件发起的服务器的主机名)。 您可以将其视为与 HTTP 报头的功能相同-一种传递与正文不同的附加信息的方式。
正文是包含实际有效负载的字节数组。 如果您的输入由尾随的日志文件组成,则数组很可能是包含一行文本的 UTF-8 编码的String
。
Flume 可能会自动添加其他标头(例如,当源添加数据来源的主机名或创建事件的时间戳时),但正文基本保持不变,除非您在使用拦截器进行编辑的过程中对其进行编辑。
拦截器、通道选择器和接收器处理器
拦截器是数据流中的一个点,您可以在这里检查和更改 Flume事件。 您可以在源创建事件之后或在接收器将事件发送到任何目的地之前链接零个或多个拦截器。 如果您熟悉 AOP Spring 框架,它类似于 Java Servlet 中的MethodInterceptor.
,它类似于ServletFilter
。 下面的示例展示了在一个源上使用四个链式拦截器可能会是什么样子:
通道选择器负责数据如何从源移动到一个或多个通道。 Flume 附带了两个通道选择器,它们涵盖了您可能会遇到的大多数用例,不过如果需要,您也可以编写自己的通道选择器。 假设您配置了多个通道,复制通道选择器(默认设置)只会将事件的副本放入每个通道。 相反,多路复用通道选择器可以根据某些报头信息写入不同的通道。 与拦截器逻辑相结合,这两个组合形成了将输入路由到不同通道的基础。
最后,接收器处理器是一种机制,您可以通过该机制为接收器创建故障转移路径,或跨通道中的多个接收器进行负载平衡事件。
分层数据收集(多个流和/或代理)
您可以根据您的特定用例来链接您的 Flume 代理。 例如,您可能希望以分层方式插入代理,以限制尝试直接连接到 Hadoop 群集的客户端数量。 更有可能的情况是,源计算机没有足够的磁盘空间来应对长时间的停机或维护窗口,因此您在源计算机和 Hadoop 集群之间创建了一个具有大量磁盘空间的层。
在下图中,您可以看到有两个创建数据的位置(左侧)和两个数据的最终目的地(右侧的HDFS和ElasticSearch云泡)。 为了使更有趣,假设其中一台机器生成两种数据(我们称它们为正方形和三角形数据)。 您可以看到,在左下角的代理中,我们使用多路复用通道选择器将两种数据分成不同的通道。 然后,矩形通道被路由到右上角的代理(以及来自左上角代理的数据)。 合并后的事件卷一起写入数据中心 1 的 HDFS 中。同时,三角形数据被发送到写入数据中心 2 中的 ElasticSearch 的代理。请记住,数据转换可以发生在任何源之后,也可以发生在任何接收器之前。 随着本书的继续,如何使用所有这些组件来构建复杂的数据工作流将变得清晰起来。
摘要
在本章中,我们讨论了 Flume 试图解决的问题:以一种易于配置和可靠的方式将数据放入 Hadoop 集群进行数据处理。 我们还讨论了 Flume 代理及其逻辑组件,包括:事件、源、通道选择器、通道、接收器处理器和接收器。
下一章将更详细地介绍这些实现,特别是每种实现最常用的实现。 与所有优秀的开源项目一样,如果捆绑的组件不能完成您需要它们做的事情,那么几乎所有这些组件都是可扩展的。
二、Flume 快速入门
正如我们在上一章中介绍的一些基础知识一样,本章将帮助您开始使用 Flume。 那么,让我们从第一步开始,下载并配置 Flume。
正在下载 Flume
让我们从http://flume.apache.org/下载 Flume。 在侧面导航中查找下载链接。 您将看到两个压缩的 tar 归档文件,以及用于验证归档的校验和和 gpg 签名文件。 网站上有验证下载的说明,所以我在这里不再赘述。 对照实际校验和检查校验和文件内容,验证下载是否未损坏。 检查签名文件可以验证您正在下载的所有文件(包括校验和和签名)是否来自 Apache,而不是某个邪恶的地方。 你真的需要验证你的下载吗? 一般来说,这是一个好主意,Apache 建议您这样做。 如果你选择不说,我就不说了。
二进制分发归档文件的名称中有bin
,源归档文件标记为src
。 源代码归档只包含 Flume 源代码。 二进制发行版要大得多,因为它不仅包含 Flume 源代码和已编译的 Flume 组件(JAR、javadoc 等),还包含所有依赖的 Java 库。 二进制包包含与源归档相同的 Maven POM 文件,因此即使您从二进制发行版开始,也始终可以重新编译代码。
继续下载(并验证)二进制发行版,为我们节省一些入门时间。
Hadoop 发行版中的 Flume
Flume 可用于某些 Hadoop 发行版。 据推测,发行版提供了 Hadoop 核心组件和卫星项目(如 Flume)的捆绑包,考虑到了版本兼容性和额外的错误修复。 这些发行版没有好坏之分,只是不同而已。
使用发行版有很多好处。 其他人已经完成了将所有版本兼容组件组合在一起的工作。 现在,自从 Apache bigtop 项目开始(http://bigtop.apache.org/)以来,这不是什么大问题。 尽管如此,拥有预先构建的标准操作系统包(如 RPM 和 Debs)可以简化安装,并提供启动/关闭脚本。 每个发行版都有不同级别的免费到付费选项,包括付费专业服务(如果您真的遇到无法处理的情况)。
当然,这也有不利之处。 捆绑在发行版中的 Flume 版本通常会远远落后于 Apache 发行版。 如果有您感兴趣的新特性或前沿特性,您要么等待发行版的提供商为您提供支持,要么自己修补它。 此外,虽然发行版提供商做了相当多的测试,就像任何通用平台一样,但您很可能会遇到他们的测试没有涵盖的东西。 在这种情况下,您仍然需要想出一个解决办法,或者潜心于代码中,修复它,并希望将补丁提交回开放源码社区(在将来的某个时刻,它将成为您的发行版或下一个版本的更新)。
因此,在 Hadoop 发行版世界中,事情进展较慢。 你可能会认为这是好事,也可能是坏事。 通常,大公司不喜欢尖端技术的不稳定性或经常进行更改,因为更改可能是导致计划外停机的最常见原因。 你很难找到这样一家使用尖端 Linux 内核的公司,而不是像Red Hat Enterprise Linux(RHEL)、CentOS、Ubuntu LTS 或任何其他以稳定和兼容性为目标的发行版。 如果你是一家打造下一个互联网时尚的初创公司,你可能需要这种尖端功能来在现有的竞争中占据一席之地。
如果您正在考虑分发,请进行研究,看看您从每个分发中获得了什么(或没有获得)。 请记住,这些产品中的每一个都希望您最终会想要和/或需要它们的企业产品,这通常不便宜。 做你的功课。
备注
以下是一些较成熟的公司的简短而非确定的名单,以了解更多信息:
- Cloudera:ΔT0→T1®
- Hortonworks:http://hortonworks.com/
- MAPR:http://mapr.com/
Flume 配置文件概述
现在我们已经下载了 Flume,让我们花一些时间来了解如何配置代理。
Flume 代理的缺省配置提供程序使用一个简单的 Java 属性文件,其中包含键/值对,您可以在启动时将其作为参数传递给代理。 由于您可以在单个文件中配置多个代理,因此需要额外传递一个代理标识符(称为名称),以便它知道要使用哪些配置。 在我的示例中,我只指定了一个代理,我将使用名称agent
。
每个代理从三个参数开始配置:
agent.sources=<list of sources>
agent.channels=<list of channels>
agent.sinks=<list of sinks>
每个源、通道和接收器在该代理的上下文中也具有唯一的名称。 例如,如果我要传输我的 Apache 访问日志,我可能会定义一个名为access
的通道。 此信道的配置将全部以前缀agent.channels.access
开始。 每个配置项都有一个类型属性,该属性告诉 Flume 它是哪种源、通道或接收器。 在本例中,我们将使用类型为memory
的内存通道。 名为agent
的代理中名为access
的通道的完整配置如下所示:
agent.channels.access.type=memory
源、通道或接收器的任何参数都使用相同的前缀作为附加属性添加。 memory
通道有一个容量参数来指示它可以容纳的最大 Flume 事件数。 假设我们不想使用默认值 100;我们的配置现在如下所示:
agent.channels.access.type=memory
agent.channels.access.capacity=200
最后,我们需要将access
通道名称添加到agent.channels
属性,以便代理知道加载它:
agent.channels=access
让我们使用规范的“Hello World”示例来看一个完整的示例。
从《Hello World》开始
没有一本技术书籍没有“Hello World”示例,就不完整。 以下是我们将使用的配置文件:
agent.sources=s1
agent.channels=c1
agent.sinks=k1
agent.sources.s1.type=netcat
agent.sources.s1.channels=c1
agent.sources.s1.bind=0.0.0.0
agent.sources.s1.port=12345
agent.channels.c1.type=memory
agent.sinks.k1.type=logger
agent.sinks.k1.channel=c1
在这里,我定义了一个代理(称为agent
),它具有名为s1
的源、名为c1
的通道和名为k1
的接收器。
S1 源的类型是netcat
,它只是打开一个套接字来侦听事件(每个事件一行文本)。 它需要两个参数,一个绑定 IP 和一个端口号。 在本例中,我们使用0.0.0.0
作为bind
地址(指定侦听任何地址的 Java 约定)和port``12345
。 震源配置还有一个名为channels
(复数)的参数,它是震源将向其追加事件的一个或多个通道的名称,在本例中为c1
。 它是复数,因为您可以将一个源配置为写入多个通道;我们只是在这个简单的示例中没有这样做。
名为c1
的通道是默认配置的内存通道。
名为k1
的接收器的类型为logger
。 这是一个主要用于调试和测试的接收器。 它将使用从配置的通道(在本例中为c1
)接收的 log4j 记录所有 INFO 级别的事件。 这里,通道关键字是单数的,因为接收器只能从一个通道馈送数据。
使用此配置,让我们运行代理,并使用 Linux Netcat 实用程序连接到它以发送事件。
首先,解压我们之前下载的二进制发行版的tar
归档文件:
$ tar -zxf apache-flume-1.3.1.tar.gz
$ cd apache-flume-1.3.1
接下来,让我们简要地看一下help
命令。 使用help
命令运行flume-ng
命令:
$ ./bin/flume-ng help
Usage: ./bin/flume-ng<command> [options]...
commands:
help display this help text
agent run a Flume agent
avro-client run an avro Flume client
version show Flume version info
global options:
--conf,-c <conf> use configs in <conf> directory
--classpath,-C <cp> append to the classpath
--dryrun,-d do not actually start Flume, just print the command
-Dproperty=value sets a JDK system property value
agent options:
--conf-file,-f <file> specify a config file (required)
--name,-n <name> the name of this agent (required)
--help,-h display help text
avro-client options:
--dirname<dir> directory to stream to avro source
--host,-H <host> hostname to which events will be sent (required)
--port,-p <port> port of the avro source (required)
--filename,-F <file> text file to stream to avro source [default: std input]
--headerFile,-R <file>headerFile containing headers as key/value pairs on each
new line
--help,-h display help text
备注
请注意,如果指定了<conf>
目录,则它始终首先包含在类路径中。
如您所见,有两种方法可以调用该命令(除了简单的help
和version
命令)。 我们将使用agent
命令。 后面将介绍avro-client
的用法。
agent
命令有个必需参数,即要使用的配置文件和代理名称(如果您的配置包含多个代理)。 让我们使用我们的示例配置并打开一个编辑器(在我的例子中是vi
,但是可以随意使用):
$ vi conf/hw.conf
接下来,将vi
配置的内容放入编辑器,保存,然后退出到 shell。
现在您可以启动代理:
$ ./bin/flume-ng agent -n agent -c conf -f conf/hw.conf -Dflume.root.logger=INFO,console
-Dflume.root.logger
属性覆盖conf/log4j.properties
中的根记录器以使用console
附加器。 如果我们没有覆盖根记录器,一切仍然可以工作,但是输出将转到一个文件log/flume.log
。 当然,您也可以只编辑conf/log4j.properties
文件并更改flume.root.logger
属性(或您喜欢的任何其他内容)。
您可能会问,既然-f
参数包含配置的完整相对路径,为什么需要指定-c
参数。 这是因为 log4j 配置文件将包含在类路径中。 如果不使用该命令的-c
参数,您将看到以下错误:
log4j:WARN No appenders could be found for logger
(org.apache.flume.lifecycle.LifecycleSupervisor).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See
http://logging.apache.org/log4j/1.2/faq.html#noconfig for more
info.
但是您没有这样做,所以您应该看到以下关键日志行:
2013-03-03 12:26:47,437 (main) [INFO -
org.apache.flume.node.FlumeNode.start(FlumeNode.java:54)] Flume
node starting - agent
此行告诉您,您的座席以名称agent
开头。 通常,当您在配置文件中定义了多个数时,您只需查找以下行,以确保您启动了正确的配置:
2013-03-03 12:26:47,448 (conf-file-poller-0) [INFO -
org.apache.flume.conf.file.
AbstractFileConfigurationProvider$FileWatcherRunnable.run
(AbstractFileConfigurationProvider.java:195)] Reloading
configuration file:conf/hw.conf
这是另一个健全性检查,以确保您加载的是正确的文件,在本例中为hw.conf
文件:
2013-03-03 12:26:47,516 (conf-file-poller-0) [INFO -
org.apache.flume.node.nodemanager.DefaultLogicalNodeManager.
startAllComponents(DefaultLogicalNodeManager.java:106)] Starting
new configuration:{ sourceRunners:{s1=EventDrivenSourceRunner: {
source:org.apache.flume.source.NetcatSource{name:s1,state:IDLE}
}} sinkRunners:{k1=SinkRunner: {
policy:org.apache.flume.sink
.DefaultSinkProcessor@42552ccounterGroup:{ name:null counters:{}
} }} channels:{c1=org.apache.flume.channel.MemoryChannel{name:
c1}} }
解析完所有配置后,您会看到此消息,其中显示了所有已配置的内容。 您可以看到s1
、c1
和k1
以及哪些 Java 类实际在执行这项工作。 正如您可能猜到的那样,netcat
对org.apache.flume.source.NetcatSource
来说是一种便利。 如果我们愿意,我们可以使用类名。 事实上,如果我编写了自己的定制源,我会使用它的类名作为源的type
参数。 如果不修补 Flume 分发,则无法定义您自己的简称:
2013-03-03 12:26:48,045 (lifecycleSupervisor-1-1) [INFO -
org.apache.flume.source.NetcatSource.start
(NetcatSource.java:164)] Created
serverSocket:sun.nio.ch.ServerSocketChannelImpl[/0.0.0.0:12345]
在这里我们可以看到,我们的源是,现在正在侦听端口12345
上的输入。 所以让我们给它发送一些数据。
最后,打开第二个航站楼。 我们将使用nc
命令(您可以使用 telnet 或类似的命令)发送String
“Hello World”,然后单击<RETURN>
来标记事件的结束:
% nclocalhost 12345
Hello World<RETURN>
OK
“OK
”是代理在按下 Return 后发出的,表示它接受此文本行作为单个 Flume 事件。 如果查看座席日志,您会看到以下内容:
2013-03-03 12:39:58,582 (SinkRunner-PollingRunner-
DefaultSinkProcessor) [INFO -
org.apache.flume.sink.LoggerSink.process(LoggerSink.java:70)]
Event: { headers:{} body: 48 65 6C6C6F 20 57 6F 72 6C 64
Hello World }
此日志消息显示 Flume 事件不包含任何标头(netcat
源本身不添加任何标头)。 正文以十六进制显示,并带有String
表示(供我们人类阅读,在本例中为Hello World
消息)。
如果我发送另一行,如下所示:
The quick brown fox jumped over the lazy dog.<RETURN>
OK
您将在座席日志中看到以下内容:
2013-03-03 12:45:08,466 (SinkRunner-PollingRunner-
DefaultSinkProcessor) [INFO -
org.apache.flume.sink.LoggerSink.process(LoggerSink.java:70)]
Event: { headers:{} body: 54 68 65 20 71 75 69 63 6B 20 62 72 6F
77 6E 20 The quick brown }
该活动似乎已被截断。 根据设计,记录器接收器将正文内容限制为 16 字节,以避免屏幕中填充的内容超过调试上下文中所需的内容。 如果需要查看调试的完整内容,则应该使用不同的接收器,也许是file_roll
接收器,它将写入本地文件系统。
摘要
在本章中,我们介绍了下载 Flume 二进制发行版。 我们创建了一个简单的配置文件,其中包括一个源写入一个馈送一个接收器的通道。 源监听网络客户端的套接字以连接并向其发送事件数据。 这些事件被写入内存通道,然后馈送到 log4j 接收器以成为输出。 然后,我们使用 Linux Netcat 实用程序连接到侦听代理,并将一些字符串事件发送到 Flume 代理的源代码中。 最后,我们验证了基于 log4j 的接收器是否写出了事件。
在下一章中,我们将详细介绍您可能在数据处理工作流中使用的两种主要通道类型:
- 存储通道
- 文件通道文件通道
对于每种类型,我们将讨论可供您使用的所有配置旋钮,何时以及为什么您可能想要偏离默认值,以及最重要的是,为什么要使用其中一种而不是另一种。
三、通道
在 Flume 中,通道是源和汇之间使用的结构。 它在从源读取动态事件之后为它们提供一个等待区域,直到可以将它们写入数据处理管道中的接收器。
我们将在这里介绍的两种类型是内存支持/非持久通道和本地文件系统支持/持久通道。 持久文件通道在向发送方确认收到事件之前刷新对磁盘的所有更改。 这比使用非持久内存通道慢得多,但在系统或 Flume 代理重新启动时提供可恢复性。 相反,内存通道要快得多,但故障会导致数据丢失,并且与支持文件通道的多 TB 磁盘相比,其存储容量要低得多。 您选择哪个通道取决于您的特定使用案例、故障场景和风险承受能力。
也就是说,无论您选择哪个通道,如果您从源接收到通道的速率大于接收器可以写入数据的速率,您将超出通道的容量,并抛出ChannelException
。 您的源对那个ChannelException
做什么或不做什么是源特定的,但在某些情况下可能会丢失数据,因此您需要通过适当调整大小来避免填充通道。 事实上,您总是希望接收器能够比源输入更快地写入数据。 否则,你可能会陷入一旦你的 Flume 落后了,你就永远赶不上的境地。 如果您的数据量与站点使用情况保持一致,您可能会在白天拥有较高的数据量,而在晚上拥有较低的数据量,从而使您的通道有时间耗尽。 在实践中,您会希望尝试将通道深度(通道中当前的事件数)保持在尽可能低的水平,因为在通道中花费的时间会转化为到达最终目的地之前的时间延迟。
存储通道
正如预期的那样,存储器通道是在存储器中存储动态事件的通道。 由于内存(通常)比磁盘快几个数量级,因此可以更快地接收事件,从而减少硬件需求。 使用此通道的缺点是,代理故障(硬件问题、断电、JVM 崩溃、Flume 重启等)会导致数据丢失。 根据您的用例,这可能完全没有问题。 系统指标通常属于这一类,因为一些丢失的数据点并不是世界末日。 然而,如果你的活动是在你的网站上购物,那么记忆通道将是一个糟糕的选择。
要使用内存通道,请将命名通道上的type
参数设置为memory
。
agent.channels.c1.type=memory
这为名为agent
的代理定义了名为c1
的内存通道。
以下是可以根据默认值调整的配置参数的表格:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| type
| 肯定的回答 / 赞成 / 是 | String
| 记忆力 / 记忆中事物 / 回忆 / 存储器 |
| capacity
| 不 / 否决票 / 同 Noh | int
| 100 个 |
| transactionCapacity
| 不 / 否决票 / 同 Noh | int
| 100 个 |
| byteCapacityBufferPercentage
| 不 / 否决票 / 同 Noh | Колибри(%)Колибри | 20% |
| byteCapacity
| 不 / 否决票 / 同 Noh | long
(字节) | 80%的 JVM 堆 |
| keep-alive
| 不 / 否决票 / 同 Noh | int
| 3(秒) |
此通道的默认容量为 100 个事件。 这可以通过设置capacity
属性来调整,如下所示:
agent.channels.c1.capacity=200
记住如果您增加了这个值,您可能还必须使用-Xmx
和可选的-Xms
参数来增加 Java 堆空间。
您可以设置的另一个与容量相关的设置是transactionCapacity
。 这是源的ChannelProcessor
(负责在单个事务中将数据从源移动到通道的组件)可以写入的最大事件数,也称为 PUT。 这也是负责将数据从通道移动到接收器的组件SinkProcessor
在单个事务中可以读取的事件数,也称为 Take。 您可能希望将此值设置得更高,以减少事务包装器的开销,这可能会加快速度。 如果出现故障,增加这个值的不利之处在于,源将不得不回滚更多数据。
提示
Flume 仅为每个单独代理中的每个通道提供事务保证。 在多代理中,多通道配置重复和无序交付是可能的,但不应被视为标准。 如果您在无故障条件下获得重复项,则意味着您需要继续调整您的 Flume 配置。
如果您使用的接收器写入的位置受益于较大批处理的工作(例如 HDFS),则可能需要将其设置得更高。 与许多事情一样,确保的唯一方法是使用不同的值运行性能测试。 Flume 提交者 Mike Percy 的博客文章http://bit.ly/flumePerfPt1应该会给您一些很好的起点。
https://issues.apache.org/jira/browse/FLUME-1535中引入了byteCapacityBufferPercentage
和byteCapacity
参数,作为一种使用字节而不是事件数量来调整内存通道容量的方法,并试图避免OutOfMemoryErrors
。 如果事件的大小差异很大,您可能会尝试使用这些设置来调整容量,但请注意,计算仅从事件的正文进行估计。 如果您有任何标头(您会这样做),则您的实际内存使用量将高于配置的值。
最后,keep-alive
参数是将数据写入通道的线程在通道满后放弃之前等待的时间。 由于同时从通道中排出数据,因此如果在超时到期之前释放空间,则数据将写入通道,而不是向源抛出异常。 您可能会想要将此值设置得非常高,但请记住,等待对通道的写入会阻止流入源的数据,这可能会导致上游代理中的数据备份。 最终,这可能会导致事件被丢弃。 您需要针对流量的周期性高峰以及临时计划内(和计划外)维护进行规模调整。
帖子主题:Re:Колибри
文件通道是将事件存储到代理的本地文件系统的通道。 虽然它比内存通道慢,但它提供了一个持久的存储路径,可以经受住大多数问题,并且应该在数据流中的间隙不受欢迎的情况下使用。
这种耐用性由预写日志(WAL)和一个或多个文件存储目录的组合提供。 WAL 用于以原子安全的方式跟踪通道的所有输入和输出。 这样,如果代理重新启动,则可以重放 WAL,以确保在可以从本地文件系统清除数据存储之前,所有进入通道(PUT)的事件都已被写出(Take)。
此外,如果您的数据处理策略要求加密磁盘上的所有数据(即使是临时的),则文件通道支持加密写入文件系统的数据。 我不会在这里介绍这一点,但是如果您需要的话,在 Flume 用户指南(http://flume.apache.org/FlumeUserGuide.html)中有一个示例。 请记住,使用加密会降低文件通道的吞吐量。
要使用文件通道,请将命名通道上的type
参数设置为file
:
agent.channels.c1.type=file
这为名为agent
的代理定义了名为c1
的文件通道。
以下是可以根据默认值调整的配置参数的表格:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| type
| 肯定的回答 / 赞成 / 是 | String
| 文件夹 / 卷宗 / 文件 / 锉刀 |
| checkpointDir
| 没有Колибрисистема | String
| ~/.flume/file-channel/checkpoint
|
| dataDirs
| 没有Колибрисистема | String
(逗号分隔列表) | ~/.flume/file-channel/data
|
| capacity
| 没有Колибрисистема | int
| 1000000 |
| keep-alive
| 没有Колибрисистема | int
| 3(秒) |
| transactionCapacity
| 没有Колибрисистема | int
| 1000 |
| checkpointInterval
| 没有Колибрисистема | long
| 300000(毫秒-5 分钟) |
| write-timeout
| 没有Колибрисистема | int
| 10(秒) |
| maxFileSize
| 没有Колибрисистема | long
| 2146435071(字节) |
| minimumRequiredSpace
| 没有Колибрисистема | long
| 524288000(字节) |
要指定 Flume 代理应保存数据的位置,请设置checkpointDir
和dataDirs
属性:
agent.channels.c1.checkpointDir=/flume/c1/checkpoint
agent.channels.c1.dataDirs=/flume/c1/data
从技术上讲,这些属性不是必需的,并且具有合理的开发默认值。 但是,如果您的代理中配置了多个文件通道,则只会启动第一个通道。 对于具有多个文件通道的生产部署和开发工作,您应该为每个文件通道存储区域使用不同的目录路径,并考虑将不同的通道放置在不同的磁盘上,以避免 IO 争用。 此外,如果您要调整大型计算机的大小,请考虑使用某种形式的包含条带化的 RAID(RAID 10、50、60)来实现更高的磁盘性能,而不是购买更昂贵的 10k 或 15k 驱动器或固态硬盘。 如果您没有 RAID 条带化,但有多个磁盘,请将dataDirs
设置为每个存储位置的逗号分隔列表。 使用多个磁盘将几乎与条带式 RAID 一样分散磁盘流量,但不会产生与 RAID 50/60 相关的计算开销以及与 RAID 10 相关的 50%空间浪费。您需要测试您的系统,以了解 RAID 开销是否值得进行速度差异。 由于硬盘故障是事实,您可能更喜欢某些 RAID 配置,而不是单个磁盘,以避免与单个驱动器故障相关的数据丢失。
出于同样的原因,应该避免使用 NFS 存储。 使用 JDBC 通道不是一个好主意,因为它会引入瓶颈和单点故障,而不是应该设计成高度分布式的系统。
提示
请确保在使用文件通道时设置了HADOOP_PREFIX
和JAVA_HOME
环境变量。 虽然我们似乎没有使用任何特定于 Hadoop 的东西(比如写入 HDFS),但文件通道使用 HadoopWriteables
作为磁盘上的序列化格式。 如果 Flume 找不到 Hadoop 库,您可能会在启动时看到这一点,因此请检查您的环境变量:
java.lang.NoClassDefFoundError: org/apache/hadoop/io/Writable
无论事件内容的大小如何,默认文件通道capacity
都是一百万个事件。 如果达到信道容量,源将无法再接收数据。 此缺省值应该适用于低容量情况。 如果您的摄取足够重,以至于无法忍受正常的计划内或计划外停机,那么您会想要更大一些。 例如,您可以在 Hadoop 中进行许多需要重新启动集群的配置更改。 如果您让 Flume 将重要数据写入 Hadoop,那么文件通道的大小应该调整为能够容忍重新启动 Hadoop 所需的时间(或许还可以为意外情况添加一个舒适的缓冲区)。 如果您的群集或其他系统不可靠,您可以将其设置得更高,以处理更长的停机时间。 在某些情况下,您会遇到这样一个事实,即您的磁盘空间是有限的资源,因此您将不得不选择一些上限(或者购买更大的磁盘)。
keep-alive
参数类似于内存通道的参数。它是源在放弃之前尝试写入满通道时等待的最长时间。 如果空间在超时前变得可用,则写入成功;否则将向源抛出ChannelException
。
属性transactionCapacity
是单个事务中允许的最大事件数。 对于将事件批处理在一起并在单个调用中将它们传递给通道的某些源,这可能会变得很重要。 最有可能的情况是,您不需要更改默认设置。 将该值设置得更高会在内部分配额外的资源,因此除非遇到性能问题,否则不应增加该值。
checkpointInterval
属性是执行检查点之间的毫秒数(这还会滚动写入logDirs
的日志文件)。 您不能将其设置为低于 1000 毫秒。
检查点文件也使用maxFileSize
属性根据写入它们的数据量进行滚动。 如果您想尝试节省一些磁盘空间,可以降低低通信量通道的此值。 假设您的最大文件大小为 50,000 字节,但您的通道每天仅写入 500 字节,则需要 100 天才能填满单个日志。 假设您是在第 100 天,所有 2000 个字节都是一次性传入的。 一些数据将被写入旧文件,而新文件将开始溢出。 滚动之后,Flume 尝试删除任何不再需要的日志文件。 由于完整日志中有未处理的记录,因此目前还不能将其删除。 下一次清理旧日志文件的机会可能还要再过 100 天才会到来。 旧的 50,000 字节文件保留更长时间可能并不重要,但是由于默认大小约为 2 GB,因此每个通道使用的磁盘空间可能是该大小的两倍(4 GB)。 这取决于您有多少可用磁盘,以及您的代理中配置的通道数,这可能是问题,也可能不是问题。 如果您的机器有足够的存储空间,默认设置应该可以。
最后,minimumRequiredSpace
属性是您不希望用于写入日志的空间量。 如果您尝试使用与dataDir
路径关联的磁盘的最后 500MB,则默认配置将引发异常。 此限制适用于所有通道,因此如果您配置了三个文件通道,则上限仍为 500 MB,而不是 1.5 GB。 您可以将此值设置为低至 1MB,但一般来说,当您将磁盘利用率推向 100%时,往往会发生不好的事情。
摘要
在本章中,我们介绍了您最有可能在数据处理管道中使用的两种通道类型。
存储器通道在发生故障时以数据丢失为代价提供速度。
或者,文件通道提供更可靠的传输,因为它可以容忍代理故障和重启,但以性能为代价。
您需要决定哪个通道适合您的用例。 在尝试确定内存通道是否合适时,问问自己,如果丢失一些数据,需要付出多少金钱代价。 在决定是否需要持久通道时,要权衡一下增加硬件以弥补性能差异的额外成本。 另一个考虑因素是数据是否可以重新发送。 并不是您可能接收到 Hadoop 的所有数据都来自流式应用日志。 如果您每天都收到数据下载,您可以使用内存通道逃脱惩罚,因为如果您遇到问题,您可以随时重新运行导入。
提示
可能的(或故意的)重复事件是摄取流数据的事实。 有些人会定期运行 MapReduce 作业来清理数据(并在使用数据时删除重复项)。 其他人在运行 MapReduce 作业时将只考虑重复项,这节省了额外的后处理。 在实践中,您可能会两者兼而有之。
在下一章中,我们将研究接收器,特别是用于将事件写入 HDFS 的 HDFS 接收器。 我们还将介绍事件序列化程序,它指定如何将 Flume 事件转换为更适合接收器的输出。 最后,我们将介绍汇聚处理器,以及如何在分层配置中设置负载平衡和故障路径,以实现更健壮的数据传输。
四、Flume 和 Flume 处理器
到目前为止,您应该对 Flume 与 Flume 架构的位置有了很好的了解。 在本章中,我们将了解 Hadoop 中最常用的接收器,即 HDFS 接收器。 Flume 的总体架构支持许多其他 Flume,我们在本书中没有篇幅涵盖所有这些 Flume。 有些与 Flume 捆绑在一起,可以写入 HBase、IRC、ElasticSearch,正如我们在第 2 章、Flume Quick Start、log4j 和文件接收器中看到的那样。 Internet 上还有其他接收器,可用于将数据写入 MongoDB、Cassandra、RabbitMQ、Redis 以及您能想到的任何其他数据存储。 如果您找不到符合您需要的接收器,您可以通过扩展org.apache.flume.sink.Abstractsink
类轻松地编写一个接收器。
HDFS 接收器
HDFS 接收器的任务是在 HDFS 中连续打开一个文件,将数据流到其中,然后在某个时刻关闭该文件并启动一个新文件。 正如我们在第 1 章、概述和体系结构中所讨论的,文件轮换之间的时长必须与 HDFS 中关闭文件的速度相平衡,从而使数据可见以供处理。 正如我们已经讨论过的,输入大量小文件会使 MapReduce 作业效率低下。
要使用 HDFS 接收器,请将您命名的sink
上的type
参数设置为hdfs
:
agent.sinks.k1.type=hdfs
这为名为agent
的代理定义了名为k1
的 HDFS 接收器。 您还需要指定一些其他必需参数,从要写入数据的 HDFS 中的path
开始:
agent.sinks.k1.hdfs.path=/path/in/hdfs
与 Hadoop 中的大多数文件路径一样,可以通过三种不同的方式指定此 HDFS 路径,即绝对路径、使用服务器名称指定的绝对路径和相对路径。 这些都是等效的(假设您的 Flume Agent 以 Flume 用户身份运行):
|类型 / 品种 / 象征 / 印刷文字
|
小径 / 路线 / 途径 / 道路
|
| --- | --- |
| 绝对 | /Users/flume/mydata
|
| 服务器名称为的绝对名称 | hdfs://namenode/Users/flume/mydata
|
| 与ΔT0 抯相关 | mydata
|
我更喜欢通过设置 Hadoop 的core-site.xml
文件中的fs.default.name
属性,使用有效的hadoop
命令行来配置我正在安装 Flume 的任何服务器。 我不在 HDFS 用户目录中保存持久数据,但我更喜欢使用具有一些有意义的路径名(即/logs/apache/access
)的绝对路径。 只有当目标是完全不同的 Hadoop 集群时,我才会专门指定名称节点。 这允许您将已经在一个环境中测试的配置移动到另一个环境中,而不会产生意外的后果,例如生产服务器将数据写入到临时 Hadoop 集群中,因为有人忘记在配置中编辑目标。 外部化环境细节是避免这种情况的最佳实践。
HDFS 接收器(实际上是任何接收器)的最后一个必需参数是它将从中执行 Take 操作的通道。 为此,使用要读取的通道名称设置channel
参数:
agent.sinks.k1.channel=c1
这告诉k1
接收器从c1
通道读取事件。
以下是您可以根据默认值调整的几乎完整的配置参数列表:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| type
| 是 | String
| HDFS |
| channel
| 是 | String
| |
| hdfs.path
| 是 | String
| |
| hdfs.filePrefix
| 没有Колибрисистема | String
| FlumeData |
| hdfs.fileSuffix
| 没有Колибрисистема | String
| |
| hdfs.maxOpenFiles
| 没有Колибрисистема | long
| 5000 |
| hdfs.round
| 没有Колибрисистема | Boolean
| 与事实不符的 / 错的 / 貌似的 / 故意捏造的 |
| hdfs.roundValue
| 没有Колибрисистема | int
| 1. |
| hdfs.roundUnit
| 没有Колибрисистема | String
(second
,minute
,orhour
) | 秒 / 片刻 / 第二名 / 瞬间 |
| hdfs.timeZone
| 没有Колибрисистема | String
| 地方时间,当地时间 |
| hdfs.inUsePrefix
| 没有Колибрисистема | String
| (仅限于 CDH4.2.0 或 Flume 1.4) |
| hdfs.inUseSuffix
| 没有Колибрисистема | String
| .tmp(仅限于 CDH4.2.0 或 Flume 1.4) |
| hdfs.rollInterval
| 没有Колибрисистема | long
(秒) | 30 秒(0=可用) |
| hdfs.rollSize
| 没有Колибрисистема | long
(字节) | 1024 字节(0=禁用) |
| hdfs.rollCount
| 没有Колибрисистема | long
| 10(0=禁用) |
| hdfs.batchSize
| 没有Колибрисистема | long
| 100 个 |
| hdfs.codeC
| 没有Колибрисистема | String
| |
请记住始终检查您在http://flume.apache.org/上使用的版本的 Flume 用户指南,因为本书的发行和您实际使用的版本之间可能会发生变化。
路径和文件名
每次 Flume 在 HDFS 中的hdfs.path
处启动要写入数据的新文件时,文件名由hdfs.filePrefix
、句点字符、文件启动时的纪元时间戳以及由hdfs.fileSuffix
属性(如果设置)指定的文件后缀(可选)组成。 例如,请参见以下代码行:
agent.sinks.k1.hdfs.path=/logs/apache/access
此行将生成一个文件,如/logs/apache/access/FlumeData.1362945258
。
但是,请看一下以下配置:
agent.sinks.k1.hdfs.path=/logs/apache/access
agent.sinks.k1.hdfs.filePrefix=access
agent.sinks.k1.hdfs.fileSuffix=.log
在此配置中,您的文件名更像/logs/apache/access/access.1362945258.log
。
随着时间的推移,hdfs.path
目录将变得非常满,因此您需要在 PATH 中添加某种类型的时间元素,以将文件划分为子目录。 Flume 支持各种基于时间的转义序列,例如指定四位年份的%Y
。 我喜欢使用年/月/日/小时形式的序列(因此它们从最旧到最新排序),因此我经常使用以下形式作为路径:
agent.sinks.k1.hdfs.path=/logs/apache/access/%Y/%m/%D/%H
这说明我需要一条类似/logs/apache/access/2013/03/10/18/
的路径。
有关基于时间的转义序列的完整列表,请参阅《Flume 用户指南》。
另一个方便的转义序列机制是能够在路径中使用 Flume 标头值。 例如,如果有一个标头的键为logType
,我可以通过转义标头的键将 Apache 访问和错误日志分到不同的目录中,同时使用相同的通道,如下所示:
agent.sinks.k1.hdfs.path=/logs/apache/%{logType}/%Y/%m/%D/%H
这将导致访问日志转到/logs/apache/access/2013/03/10/18/
,错误日志转到/logs/apache/error/2013/03/10/18/
。 但是,如果我更喜欢同一目录路径中的两种日志类型,我可以在我的hdfs.filePrefix
中使用logType
,如下所示:
agent.sinks.k1.hdfs.path=/logs/apache/%Y/%m/%D/%H
agent.sinks.k1.hdfs.filePrefix=%{logType}
显然,Flume 可以一次写入多个文件。 属性hdfs.maxOpenFiles
设置一次可以打开的数量上限,默认值为 5000。 如果超过此限制,则仍处于打开状态的最旧文件将被关闭。 请记住,每个打开的文件都会在操作系统级别和 HDFS(NameNode 和 DataNode 连接)上产生开销。
您可能会发现另一组有用的属性允许以小时、分钟或秒的粒度向下舍入事件时间,同时仍保留文件路径中的这些元素。 假设您有一个路径规范,如下所示:
agent.sinks.k1.hdfs.path=/logs/apache/%Y/%m/%D/%H%M
但在这种情况下,您每天只需要四个子目录(在每小时的 00、15、30 和 45 点,每个目录包含 15 分钟的数据)。 您可以通过设置以下值来完成此操作:
agent.sinks.k1.hdfs.round=true
agent.sinks.k1.hdfs.roundValue=15
agent.sinks.k1.hdfs.roundUnit=minute
这将导致 2013-03-10 的 01:15:00 到 01:29:59 之间的日志被写入/logs/apache/2013/03/10/0115/
中包含的文件。 01:30:00 至 01:44:59 的日志将写入/logs/apache/2013/03/10/0130/
中包含的文件中。
hdfs.timeZone
属性用于指定要为转义序列解释时间的时区。 默认值为您的计算机的本地时间。 如果您的当地时间受到夏令时调整的影响,则在%H == 02
时(秋季)会有两倍的数据,而在%H == 02
时(春季)则没有数据。 我认为将时区引入计算机可以读取的内容是一个坏主意。 我相信时区只是人类关心的问题,计算机应该只在世界时进行交流。 出于这个原因,我在我的 Flume 代理上设置了此属性,以使时区问题不复存在:
-Duser.timezone=UTC
如果您不同意,您可以自由使用默认值(当地时间),或者将hdfs.timeZone
设置为您喜欢的任何值。 您传递的值用于对java.util.Timezone.getTimeZone(…)
的调用,因此请检查 Javadoc 是否可以接受此处使用的值。
最后,在将文件写入 HDFS 的同时,添加了.tmp
扩展名。 当文件关闭时,扩展名将被删除。 这允许您在 Flume 主动写入的目录上运行 MapReduce 作业时,轻松排除这些文件作为输入。 它还允许您通过查看 HDFS 中的目录列表来查看正在写入哪些文件。 由于您通常会为 MapReduce 作业中的输入指定一个目录(或者因为您使用的是配置单元),因此临时文件通常会被错误地作为空输入或乱码输入。 Flume-1702 就是为了解决这个问题而创建的,并将在 Flume 1.4 中发布,但是如果您碰巧使用的是 Cloudera 的 CDH4.2.0 版本,那么更改会被反向移植到 Flume 1.3 中。 这引入了两个新属性来更改“in use”前缀和后缀。 为避免临时文件在关闭前被拾取,请将后缀设置为空白(而不是默认的.tmp
),并将前缀设置为圆点或下划线字符,如下所示:
agent.sinks.k1.hdfs.inUsePrefix=_
agent.sinks.k1.hdfs.inUseSuffix=
文件轮换
默认情况下,Flume 将每 30 秒、10 个事件或 1024 字节主动轮换写入文件。 这是通过分别设置hdfs.rollInterval
、hdfs.rollCount
和hdfs.rollSize
属性来实现的。 可以将其中一个或多个设置为零以禁用该特定滚动机构。 例如,如果您只需要 1 分钟的基于时间的滚动,则可以按如下方式设置这些参数:
agent.sinks.k1.hdfs.rollInterval=60
agent.sinks.k1.hdfs.rollCount=0
agent.sinks.k1.hdfs.rollSize=0
如果您的输出包含任意数量的标题信息,则每个文件的 HDFS 大小可能会大于您的预期,因为hdfs.rollSize
循环方案只计算事件正文长度。 显然,您可能不想同时禁用所有三种轮换机制,否则 HDFS 中的一个目录会出现文件溢出。
最后,一个相关参数是hdfs.batchSize
。 这是接收器将从通道读取的每个事务的事件数。 如果您的通道中有大量数据,则可以通过将此值设置为高于默认值 100 来提高性能,从而降低每个事件的事务开销。
既然我们已经讨论了文件在 HDFS 中的管理和滚动方式,让我们来看看事件内容是如何写入的。
压缩编解码器
编解码器(编码器/解码器)用于使用各种压缩算法压缩和解压缩数据。 gzip
、bzip2
、lzo
和snappy
受 Flume 支持,不过您可能需要自己安装 LZO,尤其是在由于许可问题而使用 CDH 等发行版的情况下。
如果要为数据指定压缩,如果希望 HDFS 接收器写入压缩文件,请设置hdfs.codeC
属性。 该属性还用作写入 HDFS 的文件的后缀。 对于示例,如果按如下方式指定编解码器,则所有写入的文件都将具有.gzip
扩展名,因此在这种情况下不需要指定hdfs.fileSuffix
属性:
agent.sinks.k1.hdfs.codeC=gzip
您选择使用哪种编解码器需要您进行一些研究。 有观点认为,使用gzip
或bzip2
可以获得更高的压缩比,但代价是压缩时间更长,特别是如果您的数据只写入一次,但将被读取数百次或数千次。 另一方面,使用snappy
或lzo
会导致较快的压缩性能,但会导致较低的压缩比。 请记住,文件的可拆分性,特别是在使用纯文本文件时,将极大地影响 MapReduce 作业的性能。 如果您不确定我在说什么,可以去拿一本Hadoop 初学者指南(http://amzn.to/14Dh6TA)或Hadoop:权威指南(http://amzn.to/16OsfIf)。
事件序列化程序
事件序列化程序是将 Flume 事件转换为另一种输出格式的机制。 它的功能类似于 log4j 中的Layout
类。 默认情况下,text
序列化程序,它只输出 Flume 事件正文。 还有另一个函数header_and_text
,它同时输出头和正文。 最后,还有一个avro_event
序列化程序,可用于创建事件的 Avro 表示。 如果您自己编写,则可以使用实现的完全限定类名作为serializer
属性值。
文本输出
如前所述,默认序列化程序是text
序列化程序。 这将仅输出 Flume 事件正文,并丢弃标头。 每个事件都有一个新行字符附加器,除非您通过将serializer.appendNewLine
属性设置为false
来覆盖此默认行为。
钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| serializer
| 不 / 否决票 / 同 Noh | String
| 文本 / 文本信息 / 课文 / 主题 |
| serializer.appendNewLine
| 不 / 否决票 / 同 Noh | boolean
| 真实的 / 符合实际情况的 / 精确的 / 忠实的 |
带标题的文本
text_with_headers
序列化程序允许您保存而不是丢弃 Flume 事件标头。 输出格式由标题组成,后跟一个空格,然后是正文有效负载,最后以一个可选禁用的换行符结束。 以下是该序列化程序生成的一些示例输出:
{key1=value1, key2=value2} body text here
|
钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| serializer
| 不 / 否决票 / 同 Noh | String
| 带标题的文本(_W) |
| serializer.appendNewLine
| 不 / 否决票 / 同 Noh | boolean
| 真实的 / 符合实际情况的 / 精确的 / 忠实的 |
_>Apache Avro
Apache Avro 项目(Hadoop)提供了一种在功能上类似于 Google 协议缓冲区的序列化格式,但由于容器基于HadoopSequenceFiles
并具有一些 http://avro.apache.org/集成,因此对 Hadoop 更加友好。 格式还使用 JSON 进行自我描述,这是一种很好的长期数据存储格式,因为您的数据格式可能会随着时间的推移而演变。 如果你的数据有很多你想避免变成字符串的结构,只是为了在你的 MapReduce 作业中解析这些字符串,你应该去阅读更多关于 Avro 的内容,看看你是否想把它用作 HDFS 中的一种存储格式。
avro_event
序列化程序基于 Flume 事件模式创建 Avro 数据。 它没有格式化参数,因为 avro 规定了数据的格式,而 Flume 事件的结构规定了使用的模式:
钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| serializer
| 不 / 否决票 / 同 Noh | String
| 我会有 _ 个活动 |
| serializer.compressionCodec
| 不 / 否决票 / 同 Noh | String
(gzip、bzip2、lzo 或 snappy) | |
| serializer.syncIntervalBytes
| 不 / 否决票 / 同 Noh | int
(字节) | 2048000(字节) |
如果您希望使用 Avro,但又希望使用与 Flume 事件模式不同的模式,则必须编写您自己的事件序列化程序。
如果希望在将数据写入 avro 容器之前对其进行压缩,则应将serializer.compressionCodec
属性设置为已安装编解码器的文件扩展名。 serializer.syncIntervalBytes
属性确定在将数据刷新到 HDFS 之前使用的数据缓冲区的大小,因此,此设置可能会影响使用编解码器时的压缩比。 下面是一个使用 4 MB 缓冲区对 Avro 数据进行快速压缩的示例:
agent.sinks.k1.serializer=avro_event
agent.sinks.k1.serializer.compressionCodec=snappy
agent.sinks.k1.serializer.syncIntervalBytes=4194304
agent.sinks.k1.hdfs.fileSuffix=.avro
要使 Avro 文件在 Avro MapReduce 作业中工作,它们必须以.avro
结尾,否则将被忽略为输入。 因此,您需要显式设置hdfs.fileSuffix
属性。 此外,您不会在 avro 文件上设置hdfs.codeC
属性。
文件类型
默认情况下,HDFS 接收器将数据作为 Hadoop SequenceFiles 写入 HDFS。 这是一个常见的 Hadoop 包装器,由一个键和值字段组成,由二进制字段和记录分隔符分隔。 通常,计算机上的文本文件会假设每条记录都是换行符结束的。 那么,如果您的数据包含换行符,比如一些 XML,该怎么办呢? 使用序列文件可以解决此问题,因为它使用不可打印的字符作为分隔符。 SequenceFiles 也是可拆分的,这使得在对数据(尤其是大文件)运行 MapReduce 作业时具有更好的局部性和并行性。
_ 文件
使用 SequenceFile 文件类型时,需要指定希望如何在 SequenceFile 中的记录上写入键和值。 每条记录上的关键字将始终是包含当前时间戳的LongWritable
,或者如果设置了时间戳事件头,则将改为使用。 默认情况下,该值的格式为与byte[]
Flume Body 对应的org.apache.hadoop.io.BytesWritable
:
钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| hdfs.fileType
| 不 / 否决票 / 同 Noh | String
| SequenceFile |
| hdfs.writeType
| 不 / 否决票 / 同 Noh | String
| 可写入的 |
但是,如果希望将有效负载解释为String
,则可以覆盖hdfs.writeType
属性,以便将org.apache.hadoop.io.Text
用作值字段:
钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| hdfs.fileType
| 不 / 否决票 / 同 Noh | String
| SequenceFile |
| hdfs.writeType
| 不 / 否决票 / 同 Noh | String
| 文本 / 文本信息 / 课文 / 主题 |
数据流
如果因为数据没有自然键而不想输出 SequenceFile,则可以使用数据流仅输出未压缩的值。 只需覆盖hdfs.fileType
属性:
agent.sinks.k1.hdfs.fileType=DataStream
这是您将在 Avro 序列化中使用的文件类型,因为任何压缩都应该在事件序列化程序中完成。 要序列化gzip
压缩 Avro 文件,您需要设置以下属性:
agent.sinks.k1.serializer=avro_event
agent.sinks.k1.serializer.compressionCodec=gzip
agent.sinks.k1.hdfs.fileType=DataStream
agent.sinks.k1.hdfs.fileSuffix=.avro
压缩流
除了数据在写入时被压缩之外,CompressedStream
类似于DataStream
。 您可以将其视为对未压缩文件运行gzip
实用程序,但只需一个步骤。 这与压缩的 avro 文件不同,压缩的 avro 文件的内容被压缩,然后写入未压缩的 avro 包装。
agent.sinks.k1.hdfs.fileType=CompressedStream
请记住,如果您决定使用CompressedStream
,则只有某些压缩格式在 MapReduce 中是可拆分的。 压缩算法选择没有 Flume 配置,而是由核心 Hadoop 中的zlib.compress.strategy
和zlib.compress.level
属性指定的。
超时和工作人员
最后,有两个与超时相关的杂项属性和两个可以更改的工作池的其他属性:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| hdfs.callTimeout
| 不 / 否决票 / 同 Noh | long
(毫秒) | 10000 |
| hdfs.idleTimeout
| 不 / 否决票 / 同 Noh | int
(秒) | 0(0=禁用) |
| hdfs.threadsPoolSize
| 不 / 否决票 / 同 Noh | int
| 10 个 |
| hdfs.rollTimerPoolSize
| 不 / 否决票 / 同 Noh | int
| 1. |
hdfs.callTimeout
是 HDFS 接收器在放弃之前等待 HDFS 操作返回成功(或失败)的时间量。 如果您的 Hadoop 集群特别慢(例如,开发或虚拟集群),您可能需要将此值设置得更高,以避免错误。 请记住,如果您不能保持高于通道输入速率的写入吞吐量,通道将会溢出。
hdfs.idleTimeout
属性如果设置为非零值,则是 Flume 等待自动关闭空闲文件的时间。 我从未使用过它,因为hdfs.fileRollInterval
在每个滚动周期处理文件的关闭,如果通道空闲,它将不会打开新文件。 此设置似乎是作为已讨论的大小、时间和事件计数机制的替代滚动机制而创建的。 您可能希望将尽可能多的数据写入文件,并仅在确实没有更多数据时才将其关闭。 在这种情况下,如果您还将hdfs.rollInterval
、hdfs.rollSize
和hdfs.rollCount
全部设置为零,则可以使用hdfs.idleTimeout
来完成此循环方案。
您可以设置的第一个调整工作进程数的属性是hdfs.threadsPoolSize
,默认为 10。这是可以同时写入的最大文件数。 如果您使用事件头来确定文件路径和名称,您可能会一次打开 10 个以上的文件,但在过度增加此值时要小心,以免使 HDFS 不堪重负。
与工作池相关的最后一个属性是hdfs.rollTimerPoolSize
。 这是处理由hdfs.idleTimeout
属性设置的超时的工作进程数。 关闭文件的工作量非常小,因此不太可能从一个工作进程的默认值增加此值。 如果不使用基于hdfs.idleTimeout
的旋转,则可以忽略hdfs.rollTimerPoolSize
属性,因为它没有被使用。
汇流组
为了消除数据处理管道中的单点故障,Flume 能够使用负载平衡或故障转移将事件发送到不同的接收器。 为了做到这一点,我们需要引入一个新的概念,称为下沉群。 接收器组用于创建接收器的逻辑分组。 此分组的行为由称为接收器处理器的东西决定,它确定如何路由事件。
有一个默认接收器处理器,它包含一个接收器,只要您的接收器不属于任何接收器组,就会使用该接收器。 我们在第 2 章,Flume Quick Start中的 Hello World 示例使用了默认的接收器处理器。 单个接收器不需要特殊配置。
为了让 Flume 了解接收组,有一个名为sinkgroups
的新顶级代理属性。 与源、通道和接收器类似,您可以使用代理名称作为该属性的前缀,如下所示:
agent.sinkgroups=sg1
这里,我们定义了对于名为agent
的代理,有一个名为sg1
的接收组。
对于每个命名接收器组,需要使用由空格分隔的接收器名称列表组成的sinks
属性指定其包含的接收器:
agent.sinkgroups.sg1.sinks=k1,k2
这定义了接收器k1
和k2
是名为agent
的代理的接收器组sg1
的一部分。
接收器组通常与数据的分层移动结合使用,以绕过故障进行路由。 但是,它们也可以用于写入不同的 Hadoop 集群,因为即使是维护良好的集群也有定期维护。
负载均衡
继续前面的示例,假设您希望均匀地将流量负载平衡到k1
和k2
。 下表中列出了一些需要指定的其他属性:
钥匙 / 键 / 关键 / 主调
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- |
| processor.type
| String
| 负载均衡 |
| processor.selector
| String
(ROUND_ROBIN,随机) | 轮询 |
| processor.backoff
| boolean
| 与事实不符的 / 错的 / 貌似的 / 故意捏造的 |
将processor.type
设置为load_balance
时,除非processor.selector
属性另有指定,否则将使用循环选择。 可以将其设置为round_robin
或random
。 您还可以指定您自己的负载平衡选择器机制,我们在这里不会讨论这一点。 如果需要此自定义控件,请参考 Flume 文档。
processor.backoff
属性指定在重试引发异常的接收器时是否应使用指数备份。 缺省值为false
,这意味着在抛出异常之后,将基于循环或随机选择在下一次轮到接收器时再次尝试。 如果设置为true
,则对于每次失败,等待时间从 1 秒开始增加一倍,限制为约 18 小时(2^16 秒)。
备注
在编写本文时,代码中processor.backoff
的默认值为 false,但 Flume 文档显示为 true。 省去让自己头疼的麻烦,指定你想要什么,而不是依赖默认设置。
故障转移
如果您宁愿尝试一个接收器,如果该接收器失败,则尝试另一个接收器,则需要将processor.type
设置为failover
。 接下来,您需要设置其他属性来指定顺序,方法是:设置processor.priority
属性,后跟接收器名称:
钥匙 / 键 / 关键 / 主调
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- |
| processor.type
| String
| 故障切换 |
| processor.priority.NAME
| int
| |
| processor.maxpenality
| int
(毫秒) | 30000 |
让我们看一下这个例子:
agent.sinkgroups.sg1.sinks=k1,k2,k3
agent.sinkgroups.sg1.processor.type=failover
agent.sinkgroups.sg1.processor.priority.k1=10
agent.sinkgroups.sg1.processor.priority.k2=20
agent.sinkgroups.sg1.processor.priority.k3=20
优先级较低的数字排在第一位,在平局的情况下,顺序是任意的。 你可以使用任何对你有意义的编号系统(一、五、十,随便什么)。 在本例中,将首先尝试接收器k1
,如果抛出异常,则接下来将尝试k2
或k3
。 如果首先选择k3
进行尝试,但失败,则 K2 仍会尝试。 如果接收器组中的所有接收器都失败,则回滚通道的事务。
最后,processor.maxPenality
为组中失败的接收器设置指数回退的上限。 第一次失败后,将在一秒钟后才能再次使用。 每次后续故障都会使等待时间加倍,直到达到processor.maxPenality
。
摘要
在本章中,我们深入讨论了 HDFS 接收器,即将流数据写入 HDFS 的 Flume 输出。 我们介绍了 Flume 如何根据 Flume 标头的时间或内容将数据划分到不同的 HDFS 路径中。 还讨论了几种文件滚动技术,包括:
- 时间旋转
- 事件计数轮换
- 大小旋转
- 仅空闲时旋转
讨论了压缩作为减少 HDFS 中存储需求的一种方法,并应在可能的情况下使用压缩。 除了节省存储空间外,读取压缩文件并解压缩到内存中通常比读取未压缩文件更快。 这将提高在此数据上运行的 MapReduce 作业的性能。 压缩数据的可分割性也是决定使用哪种压缩算法的一个因素。
引入事件序列化程序作为将 Flume 事件转换为外部存储格式的机制,包括以下内容:
- 文本(仅正文)
- 文本和页眉(页眉和正文)
- AVRO 序列化(带可选压缩)
接下来,介绍各种文件格式,包括以下内容:
- 序列文件(Hadoop 密钥/值文件)
- 数据流(未压缩的数据文件,如 Avro 容器)
- 压缩数据流
最后,我们介绍了接收器组作为一种使用负载平衡或故障转移路径将事件路由到不同源的方法,这些路径可用于消除将数据路由到其目的地的单点故障。
在下一章中,我们将讨论各种输入机制(源),它们将反馈回第 3 章、通道中介绍的已配置通道。
五、信号源和通道选择器
现在我们已经介绍了通道和接收器,我们将介绍一些将数据传送到您的 Flume 代理的更常见的方法。 正如在第 1 章、概述和体系结构中所讨论的,源是 Flume 代理的输入点。 Flume 发行版有许多可用的源代码,以及许多可用的开源选项。 像大多数开放源码软件一样,如果您找不到需要的东西,您可以通过扩展org.apache.flume.source.AbstractSource
类来编写您自己的软件。 由于本书的主要关注点是将日志文件吸收到 Hadoop 中,因此我们将介绍几个更合适的来源来实现这一点。
使用 Tail 的问题
如果您使用过任何 Flume 0.9 版本,您会注意到 TailSource 不再是 Flume 的一部分。 TailSource 提供了一种机制来tail
(http://en.wikipedia.org/wiki/Tail_(Unix))系统上的任何文件,并为文件的每一行创建 Flume 事件。 许多人已经将文件系统用作创建数据的应用(例如,log4j
)和负责将这些文件移动到其他地方的机制(例如,syslog
)之间的交接点。因此,TailSource 是 syslog 传输的完美替代品,无需对创建数据的应用进行更改。
与通道和接收器的情况一样,事件作为事务的一部分在通道中添加和删除。 当您跟踪文件时,无法正确参与事务。 如果发生向通道成功写入的失败,或者如果通道已满(这是一种比失败更有可能的事件),则数据不能像回滚语义所规定的那样被“放回”。
此外,如果写入文件的数据速率超过 Flume 可以读取数据的速率,则可能会完全丢失一个或多个输入日志文件。 例如,假设您在跟踪/var/log/app.log
。 当该文件达到特定大小时,该文件将被旋转/重命名为/var/log/app.log.1
,并启动一个新文件/var/log/app.log
。 比方说,你在媒体上得到了好评,你的申请记录比往常高得多。 当发生另一次旋转将/var/log/app.log
移动到/var/log/app.log.1
时,Flume 可能仍在从旋转的文件(/var/log/app.log.1
)中读取。 正在读取的文件 Flume 现在已重命名为/var/log/app.log.2
。 当 Flume 完成此文件时,它将移动到它认为是下一个文件/var/log/app.log
的位置,从而跳过现在驻留在/var/log/app.log.1
处的文件。 这种数据丢失完全不会被注意到,如果可能的话,这是我们想要避免的。
出于这些原因,我们决定在重构 Flume 时将tail
功能从 Flume 中移除。 已经删除了 TailSource 的一些解决方法,但应该注意的是,在这些情况下,没有任何解决方法可以消除在负载下丢失数据的可能性。
EXEC 来源
EXEC 源提供了一种机制,用于在 Flume 外部运行命令,然后将输出转换为 Flume 事件。 要使用 EXEC 源,请将type
属性设置为exec
:
agent.sources.s1.type=exec
Flume 中的所有源都需要使用channels
(复数)属性指定要向其写入事件的通道列表。 这是一个或多个通道名称的空格分隔列表:
agent.sources.s1.channels=c1
唯一需要的另一个参数是command
属性,它告诉 Flume 要向操作系统传递什么命令。 例如:
agent.sources=s1
agent.sources.s1.channels=c1
agent.sources.s1.type=exec
agent.sources.s1.command=tail -F /var/log/app.log
在这里,我为名为agent
的代理配置了单个源s1
。 源(EXEC 源)将跟踪/var/log/app.log
文件,并遵循外部应用可能对该日志文件执行的任何轮换。 所有事件都写入c1
通道。 这是 Flume 1.x 中缺少 TailSource 的解决方法之一的一个示例。
备注
如果将tail -F
命令与 EXEC 源一起使用,则当 Flume 代理关闭或重新启动时,派生进程可能不会 100%关闭。 这将使孤立的尾部进程永远不会退出。 tail -F
根据定义,没有终点。 即使您删除了尾随的文件(至少在 Linux 中),正在运行的尾部进程也会无限期地保持文件句柄打开。 这可以防止文件的空间被实际回收,直到尾部进程退出--它不会退出。我想您已经开始明白为什么 Flume 开发人员不喜欢尾部文件了。
如果沿着这条路线走下去,请务必定期扫描进程表,查找父 PID 为 1 的tail -F
。这些进程实际上是死进程,需要手动终止。
以下是您可以与 EXEC 源一起使用的其他属性的列表:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| type
| 是 | String
| exec
|
| channels
| 是 | String
| 以空格分隔的通道列表 |
| command
| 是 | String
| |
| restart
| 没有Колибрисистема | boolean
| false
|
| restartThrottle
| 没有Колибрисистема | long
(毫秒) | 10000 毫秒 |
| logStdErr
| 没有Колибрисистема | boolean
| false
|
| batchSize
| 没有Колибрисистема | int
| 20
|
并不是每个命令都能继续运行,要么是因为它失败了(比如它正在写入的通道已满),要么是因为命令被设计为立即退出。 在本例中,我们希望通过 Linuxuptime
命令记录系统负载,该命令将一些系统信息打印到stdout
并退出:
agent.sources.s1.command=uptime
此命令将立即退出,因此您可以使用restart
和restartThrottle
属性定期运行它:
agent.sources.s1.command=uptime
agent.sources.s1.restart=true
agent.sources.s1.restartThrottle=60000
这将每分钟产生一个事件。 在 Tail 示例中,如果通道填满导致 EXEC 源出现故障,您可以使用这些属性重新启动 EXEC 源。 在这种情况下,设置restart
属性将从当前文件的开头开始拖尾该文件,从而产生重复项。 根据将restartThrottle
值设置为的时间长短,您可能会因文件轮换到 Flume 外部而丢失某些数据。 此外,通道可能仍然不能接受数据,在这种情况下,源将再次出现故障。 将此值设置得太低意味着给通道提供更少的排水时间,而且与我们看到的某些汇不同,没有指数回退的选项。
有时,命令会将要捕获的输出写入StdErr
。 如果还希望包括这些行,请将logStdErr
属性设置为true
。 没有将关闭StdOut
行作为输入的属性(但您可以在第 6 章、拦截器、ETL 和路由中讨论拦截器时过滤掉它们)。
最后,您可以通过更改batchSize
属性指定每个事务要写入的事件数。 如果您的输入数据很大,并且您发现无法足够快地写入通道,则您可能需要将此值设置为大于默认值 20。 使用较大的批处理大小可以降低每个事件的总体平均事务开销。 使用不同的值进行测试并监控通道的投放率是唯一确定的方法。
假脱机目录源
为了避免跟踪文件所固有的所有假设,设计了一个新的源来跟踪哪些文件已转换为 Flume 事件,哪些文件仍需要处理。 假脱机目录源被赋予一个目录来监视新文件的出现。 假定复制到此目录的文件已完成;否则,源可能会尝试发送部分文件。 它还假设文件名永远不会更改;否则,源将在重新启动时失去其位置,不知道哪些文件已经发送,哪些文件没有发送。 使用DailyRollingFileAppender
而不是RollingFileAppender
可以在log4j
中满足文件名条件,但是,当前打开的文件需要写入一个目录,并在关闭后复制到假脱机目录。 发货的log4j
附加设备都没有此功能。
也就是说,如果您在环境中使用 Linuxlogrotate
程序,可能会对此感兴趣。 您可以使用postrotate
脚本将完成的文件移动到单独的目录。
请记住,在标记为已由 Flume 发送之后,您将需要一个单独的过程来清除假脱机目录中的所有旧文件,否则您的磁盘最终将被填满。
要创建假脱机目录源,请将type
属性设置为spooldir
。 您必须设置目录以监视spoolDir
属性:
agent.sources=s1
agent.sources.channels=c1
agent.sources.s1.type=spooldir
agent.sources.s1.spoolDir=/path/to/files
以下是假脱机目录源的属性摘要:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| type
| 是 | String
| spooldir
|
| channels
| 是 | String
| 以空格分隔的通道列表 |
| spoolDir
| 是 | String
| 假脱机目录的路径 |
| fileSuffix
| 没有Колибрисистема | String
| .COMPLETED
|
| fileHeader
| 没有Колибрисистема | boolean
| false
|
| fileHeaderKey
| 没有Колибрисистема | String
| file
|
| batchSize
| 没有Колибрисистема | int
| 10
|
| bufferMaxLines
| 没有Колибрисистема | int
| 100
|
| maxBufferLineLength
| 没有Колибрисистема | int
| 5000
|
文件传输完成后,除非通过设置fileSuffix
属性将其覆盖,否则将使用.COMPLETED
扩展名对其进行重命名。 例如:
agent.source.s1.fileSuffix=.DONE
如果要将绝对文件路径附加到每个事件,请将fileHeader
属性设置为true
。 这将使用file
键创建标题,除非使用fileHeaderKey
属性将其设置为其他值。 例如:
agent.source.s1.fileHeader=true
agent.source.s1.fileHeaderKey=sourceFile
如果事件是从/path/to/files/foo.1234.log
文件中读取的,这将添加标题{sourceFile=/path/to/files/foo.1234.log}
。
batchSize
属性允许您调整写入通道的每个事务的事件数。 增加这可能会提供更好的吞吐量,但代价是更大的事务(可能还会有更大的回滚)。 bufferMaxLines
属性用于设置读取文件时使用的内存缓冲区的大小,方法是将其与maxBufferLineLength
相乘。 如果您的数据非常短,您可以考虑增加bufferMaxLines
,同时减少maxBufferLineLength
。 在这种情况下,它将在不增加内存开销的情况下提高吞吐量。 也就是说,如果事件长度超过 5000 个字符,则需要将maxBufferLineLength
设置得更高。
最后,您需要确保将新文件写入假脱机目录的任何机制都会创建唯一的文件名,比如添加时间戳(可能还有更多)。 重复使用文件名会混淆来源,您的数据可能无法处理。
与往常一样,请记住,重新启动和错误将在假脱机目录中因未标记为完成而重新传输的任何文件上创建重复事件。
系统日志源
Syslog 已经有年的历史,经常被用作在系统中捕获和移动日志的操作系统级机制。 在许多方面,与 Flume 提供的某些功能存在重叠。 甚至有一个用于rsyslog的 Hadoop 模块,它是 syslog(http://www.rsyslog.com/doc/rsyslog_conf_modules.html/omhdfs.html)的一个更现代的变体。 一般来说,我不喜欢将可能独立版本的技术结合在一起的解决方案。 如果您使用此 rsyslog/Hadoop 集成,则需要在将 Hadoop 群集升级到新的主要版本的同时,更新编译到 rsyslog 中的 Hadoop 版本。 如果您有大量的服务器和/或环境,这在后勤上可能会很困难。 Hadoop 有线协议中的向后兼容性是 Hadoop 社区正在积极研究的问题,但目前还不是标准。 我们将在第 7 章,监控 Flume中讨论更多这方面的内容,届时我们将讨论分层数据流。
Syslog 具有较旧的 UDP 传输和较新的 TCP 协议,该协议可以处理大于单个 UDP 数据包可以传输的数据量(约 64k)的数据,以及处理可能需要重新传输数据的与网络相关的拥塞事件。
最后,syslog 源上还有一些未记录的属性,这些属性允许为不符合 RFC 标准的消息添加额外的正则表达式匹配模式。 我不会讨论这些额外的设置,但是如果您经常遇到解析错误,您应该知道它们。 在这种情况下,请查看org.apache.flume.source.SyslogUtils
的来源以了解实现详细信息以查找原因。
有关系统日志术语(如什么是工具)和标准格式的更多详细信息,请参阅RFC3164(http://tools.ietf.org/html/rfc3164)和RFC5424(http://tools.ietf.org/html/rfc5424)。
系统日志 UDP 源
当您从服务器的本地 syslog 进程接收数据时,只要数据足够小(小于 64k),使用 syslog 的 UDP 版本通常是安全的。
备注
无论您的网络实际可以处理什么,该源的实现都选择 2500 字节作为最大有效负载大小。 因此,如果您的有效负载将大于此值,请改用其中一个 TCP 源。
要创建系统日志 UDP 源,请将type
属性设置为syslogudp
。 必须使用port
属性设置要侦听的端口。 可选的host
属性指定绑定地址。 如果未指定主机,则将使用服务器的所有 IP-与指定0.0.0.0
相同。 在此示例中,我们将仅侦听端口 5140 上的本地 UDP 连接:
agent.sources=s1
agent.sources.channels=c1
agent.sources.s1.type=syslogudp
agent.sources.s1.host=localhost
agent.sources.s1.port=5140
如果希望 syslog 转发尾部文件,可以在 syslog 配置文件中添加如下行:
*.err;*.alert;*.crit;*.emerg;kern.* @localhost:5140
这会将任何优先级的所有错误、警报、严重、紧急优先级和内核消息发送到您的 Flume 源中。 单个@
符号表示应使用 UDP 协议。
以下是系统日志 UDP 源的属性摘要:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| type
| 是 | String
| syslogudp
|
| channels
| 是 | String
| 以空格分隔的通道列表 |
| port
| 是 | int
| |
| host
| 没有Колибрисистема | String
| 0.0.0.0
|
由 syslogUDP 源创建的 Flume 标头摘要如下:
|标题键
|
描述 / 描写 / 形容 / 类别
|
| --- | --- |
| Facility
| 系统日志工具。 请参阅 syslog 文档。 |
| Priority
| 系统日志优先级。 请参阅系统日志文档。 |
| timestamp
| 系统日志事件转换为纪元时间戳的时间。 如果不是从标准 RFC 格式之一解析,则省略。 |
| hostname
| 系统日志消息中解析的主机名。 如果未分析则省略。 |
| flume.syslog.status
| 解析系统日志消息的标头时出现问题。 如果有效载荷不符合 RFC,则设置为Invalid
。 如果消息长于eventSize
值,则设置为Incomplete
(对于 UDP,内部设置为 2500 字节)。 如果一切正常,则省略。 |
系统日志 TCP 源
如前所述,syslog TCP 源为 TCP 上的消息提供了一个端点,从而允许更大的有效负载大小和 TCP 重试语义,这些语义应用于任何可靠的服务器间通信。
要创建系统日志 TCP 源,请将type
属性设置为syslogtcp
。 您仍必须设置要侦听的绑定地址和端口:
agent.sources=s1
agent.sources.s1.type=syslogtcp
agent.sources.s1.host=0.0.0.0
agent.sources.s1.port=12345
如果您的 syslog 实施支持基于 TCP 的 syslog,则配置通常相同,只是使用双@
符号表示 TCP 传输。 下面是使用 TCP 的相同示例,其中我将转发到在名为flume-1
的不同服务器上运行的 Flume 代理:
*.err;*.alert;*.crit;*.emerg;kern.* @@flume-1:12345
系统日志 TCP 源有一些可选属性:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| type
| 是 | String
| syslogtcp
|
| channels
| 是 | String
| 以空格分隔的通道列表 |
| port
| 是 | int
| |
| host
| 没有Колибрисистема | String
| 0.0.0.0
|
| eventSize
| 没有Колибрисистема | int
(字节) | 2500 字节 |
由 syslog TCP 源创建的 Flume 标头摘要如下:
|标题键
|
描述 / 描写 / 形容 / 类别
|
| --- | --- |
| Facility
| 系统日志工具。 请参阅系统日志文档。 |
| Priority
| 系统日志优先级。 请参阅 syslog 文档。 |
| timestamp
| 将转换为纪元时间戳的系统日志事件的时间。 如果不是从标准 RFC 格式之一解析,则省略。 |
| hostname
| 系统日志消息中解析的主机名。 如果未分析则省略。 |
| flume.syslog.status
| 解析系统日志消息的标头时出现问题。 如果有效载荷不符合 RFC,则设置为Invalid
。 如果消息比配置的eventSize
长,则设置为Incomplete
。 如果一切正常,则省略。 |
多端口系统日志 TCP 源
多端口 syslogTCP 源在功能上与 syslog TCP 源几乎相同,不同之处在于它可以侦听多个端口的输入。 如果您无法更改 syslog 将在其转发规则中使用的端口(它可能根本不是您的服务器),您可能需要使用此功能。 更有可能的是,您将使用它来读取多个格式,使用一个源来写入不同的通道。 我们稍后将在通道选择器部分介绍这一点。
要配置此源,请将type
属性设置为multiport_syslogtcp
:
agent.sources.s1.type=multiport_syslogtcp
与其他 syslog 源一样,您需要指定端口,但在本例中是空格分隔的端口列表。 只有在指定了一个端口时,才能使用此选项。 其属性为ports
(复数):
agent.sources.s1.type=multiport_syslogtcp
agent.sources.s1.channels=c1
agent.sources.s1.ports=33333 44444
agent.sources.s1.host=0.0.0.0
这会将名为s1
的多端口 syslog TCP 源配置为侦听端口33333
和44444
上的任何传入连接,并将它们发送到通道c1
。
为了知道哪个事件来自哪个端口,您可以将可选的portHeader
属性设置为键的名称,该键的值将是端口号。 如果我将此属性添加到配置中:
agent.sources.s1.portHeader=port
则从端口33333
接收的任何事件都将具有标题键/值{"port"="33333"}
。 正如您在第 4 章,接收器和接收器处理器中看到的,您现在可以使用此值(实际上是任何标头)作为 HDFS 接收器文件路径约定的一部分,如下所示:
agent.sinks.k1.hdfs.path=/logs/%{hostname}/%{port}/%Y/%m/%D/%H
以下是这些属性的完整表格:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| type
| 是 | String
| syslogtcp
|
| channels
| 是 | String
| 以空格分隔的通道列表 |
| ports
| 是 | int
| 以空格分隔的端口号列表 |
| host
| 没有Колибрисистема | String
| 0.0.0.0
|
| eventSize
| 没有Колибрисистема | int
(字节) | 2500 字节 |
| portHeader
| 没有Колибрисистема | String
| |
| batchSize
| 没有Колибрисистема | int
| 100
|
| readBufferSize
| 没有Колибрисистема | int
(字节) | 1024
|
| numProcessors
| 没有Колибрисистема | int
| 自动检测到 |
| charset.default
| 没有Колибрисистема | String
| UTF-8
|
| charset.port.PORT#
| 没有Колибрисистема | String
| |
此 TCP 源在标准 TCP syslog 源的基础上有一些额外的可调选项,您可能希望对其进行优化。 第一个是batchSize
属性。 这是通道的每个事务处理的事件数。 还有readBufferSize
属性指定内部 MINA 库使用的内部缓冲区大小。 最后,numProcessors
属性用于调整 MINA 中的工作线程池的大小。 在调优这些参数之前,您可能希望熟悉 MINA(http://mina.apache.org/),并在偏离默认设置之前查看源代码。
最后,您可以指定在字符串和 byte[]之间进行转换时要使用的默认和每个端口的字符编码。
agent.sources.s1.charset.default=UTF-16
agent.sources.s1.charset.port.33333=UTF-8
此示例配置显示,除了使用 UTF-8 的端口 33333 流量外,所有端口都将使用 UTF-16 编码进行解释。
下面汇总了此源创建的 Flume 标头:
|标题键
|
描述 / 描写 / 形容 / 类别
|
| --- | --- |
| Facility
| Syslog 工具。 请参阅系统日志文档。 |
| Priority
| 系统日志优先级。 请参阅系统日志文档。 |
| timestamp
| 转换为纪元时间戳的系统日志事件的时间。 如果未从标准 RFC 格式之一解析,则省略。 |
| hostname
| 系统日志消息中解析的主机名。 如果未分析则省略。 |
| flume.syslog.status
| 解析 syslog 消息的标头时出现问题。 如果有效载荷不符合 RFC,则设置为Invalid
。 如果消息比配置的eventSize
长,则设置为Incomplete
。 如果一切正常,则省略。 |
通道选择器
正如我们之前在第 1 章、概述和架构中所讨论的,源可以写入一个或多个通道。 这就是该属性是复数(channels
而不是channel
)的原因。 有两种方式可以处理多个通道。 该事件可以写入所有通道,也可以基于某个 Flume 标头值仅写入一个通道。 在 Flume 中的内部机制称为通道选择器。
任何通道的选择器都可以使用selector.type
属性指定。 任何选择器特定的属性都以常用的源前缀开头;代理名称、关键字 Sources 和源名称:
agent.sources.s1.selector.type=replicating
正在复制
默认情况下,如果您没有为信号源指定选择器,则默认值为replicating
。 复制选择器将相同的事件写入信号源的通道列表中的所有通道:
agent.sources.s1.channels=c1 c2 c3
agent.sources.s1.selector.type=replicating
在本例中,每个事件都将写入所有三个通道c1
、c2
和c3
。
此选择器上有一个可选属性,名为optional
。 它是可选通道的空格分隔列表。 也就是说,如果我设置了以下内容:
agent.sources.s1.channels=c1 c2 c3
agent.sources.s1.selector.type=replicating
agent.sources.s1.selector.optional=c2 c3
写入通道c2
或c3
的任何失败都不会导致事务失败,写入c1
的任何数据都将被提交。 在前面没有可选通道的示例中,任何单通道故障都会回滚所有通道的事务。
多路复用
如果要将不同的事件发送到不同的通道,可以通过将selector.type
设置为multiplexing
来使用多路复用通道选择器。 您还需要通过设置selector.header
属性告诉通道选择器使用哪个标题。
agent.sources.s1.selector.type=multiplexing
agent.sources.s1.selector.header=port
假设我们使用多端口 Syslog TCP 源侦听四个端口,分别是 11111、22222、33333 和 44444,portHeader
设置为port
。 请考虑以下配置:
agent.sources.s1.selector.default=c2
agent.sources.s1.selector.mapping.11111=c1 c2
agent.sources.s1.selector.mapping.44444=c2
agent.sources.s1.selector.optional.44444=c3
这将导致端口 22222 和端口 33333 的通信量仅通过通道c2
。 来自端口 11111 的流量将进入通道c1
和c2
。 任一通道上的故障都不会导致向任一通道添加任何内容。 来自端口 44444 的流量将进入通道c2
和c3
;但是,写入c3
失败仍会将事务提交到通道c2
(如果发生该事件,则不会再次尝试c3
)。
摘要
在本章中,我们深入介绍了可用于将日志数据插入 Flume 的各种来源,包括:
- EXEC 消息来源
- 系统日志源(UDP、TCP 和多端口 TCP)
我们讨论了在 Flume 0.9 中复制旧的 TailSource 功能,以及使用 Tail 语义的一般问题。
我们还介绍了通道选择器以及如何将事件发送到多个通道之一。 具体地说,就是:
- 复制通道选择器
- 多路复用信道选择器
还讨论了可选通道,作为一种仅在使用多个通道时使某些通道的通道 PUT 事务失败的方法。
在下一章中,我们将介绍允许在飞行中检查和转换事件的拦截器。 拦截器与通道选择器结合使用,提供了使用 Flume 创建复杂数据流的最后一块。
六、拦截器、ETL 和路由
数据处理管道中需要的最后一项功能是检查和转换飞行中的事件的能力。 这可以使用拦截器来完成。 拦截器,正如我们在第 1 章,概述和体系结构中所讨论的,可以插入源之后或汇之前。
拦截器
拦截器的功能可以用以下方法总结:
public Event intercept(Event event);
它作为 Flume事件传递,并作为 Flume事件返回。 它可能什么也不做;也就是说,返回相同的未更改的事件。 通常,它会以某种有用的方式改变事件。 如果返回null
,则丢弃该事件。
要将拦截器添加到源,只需将interceptors
属性添加到指定的源。 例如:
agent.sources.s1.interceptors=i1 i2 i3
这在名为agent
的代理的s1
源上定义了三个拦截器i1
、i2
和i3
。
备注
拦截器按照列出的顺序运行。 在前面的示例中,i2
将接收来自i1
的输出。 i3
将接收来自i2
的输出。 最后,通道选择器接收来自i3
的输出。
既然我们已经按名称定义了拦截器,我们需要按如下方式指定其类型:
agent.sources.s1.interceptors.i1.type=TYPE1
agent.sources.s1.interceptors.i1.additionalProperty1=VALUE
agent.sources.s1.interceptors.i2.type=TYPE2
agent.sources.s1.interceptors.i3.type=TYPE3
让我们看看 Flume 附带的一些拦截器,以便更好地了解如何配置它们。
加入时间:清华大学 2007 年 01 月 25 日下午 3:33
时间戳拦截器,正如它的名字所暗示的那样,在 Flume 事件中添加一个带有timestamp
键的头(如果不存在的话)。 要使用它,请将type
属性设置为timestamp
。
如果事件已包含时间戳头,则它将被当前时间覆盖,除非通过将preserveExisting
属性设置为true
将其配置为保留原始值。
下表汇总了时间戳拦截器的属性:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| type
| 肯定的回答 / 赞成 / 是 | String
| timestamp
|
| preserveExisting
| 没有Колибрисистема | Boolean
| false
|
如果我们只希望源在不存在时间戳头的情况下添加时间戳头,则源的总配置可能如下所示:
agent.sources.s1.interceptors=i1
agent.sources.s1.interceptors.i1.type=timestamp
agent.sources.s1.interceptors.i1.preserveExisting=true
调用第 4 章,接收器和接收器处理器中的 HDFSSink 路径,使用事件日期:
agent.sinks.k1.hdfs.path=/logs/apache/%Y/%m/%D/%H
timestamp
标头决定了这条路径。 如果缺少该文件,可以肯定 Flume 不知道在哪里创建文件,也不会得到您想要的结果。
主机
在简单性上类似于时间戳拦截器,主机拦截器将向包含当前 Flume 代理的 IP 地址的事件添加标头。 要使用它,请将type
属性设置为host
。
agent.sources.s1.interceptors=i1
agent.sources.s1.interceptors.type=host
除非您使用hostHeader
属性指定其他内容,否则此标头的键将为host
。 与前面一样,除非将preserveExisting
属性设置为true
,否则现有标头将被覆盖。 最后,如果希望使用主机名的反向 DNS 查找代替 IP 作为值,请将useIP
属性设置为false
。 请记住,反向查找会增加数据流的处理时间。
下表汇总了主机拦截器的属性:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| type
| 是 | String
| host
|
| hostHeader
| 没有Колибрисистема | String
| host
|
| preserveExisting
| 没有Колибрисистема | Boolean
| false
|
| useIP
| 没有Колибрисистема | Boolean
| true
|
如果我们只希望源将包含此代理的 DNS 主机名的relayHost
标头添加到每个事件,则源的总配置可能如下所示:
agent.sources.s1.interceptors=i1
agent.sources.s1.interceptors.i1.type=host
agent.sources.s1.interceptors.i1.hostHeader=relayHost
agent.sources.s1.interceptors.i1.useIP=false
例如,如果您想要记录事件通过数据流的路径,此拦截器可能会很有用。 您可能更感兴趣的是事件的起源,而不是它所经历的路径,这就是为什么我还没有用到这一点的原因。
静态
静态拦截器用于在处理的每个 Flume 事件中插入任何单个键/值头。 如果需要多个键/值,只需添加额外的静态拦截器。 与我们到目前为止看到的拦截器不同,默认行为是使用相同的键保留个现有标头。 一如既往,我的建议是始终指定您想要的内容,而不是依赖缺省值。
我不知道为什么键和值属性不是必需的,因为缺省值并不是特别有用。
下表总结了静态侦听器的属性:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| type
| 是 | String
| static
|
| key
| 没有Колибрисистема | String
| key
|
| value
| 没有Колибрисистема | String
| value
|
| preserveExisting
| 没有Колибрисистема | Boolean
| true
|
最后,让我们看一个示例配置,该配置插入两个新的标头,前提是事件中不存在这两个标头:
agent.sources.s1.interceptors=pos env
agent.sources.s1.interceptors.pos.type=static
agent.sources.s1.interceptors.pos.key=pointOfSale
agent.sources.s1.interceptors.pos.value=US
agent.sources.s1.interceptors.env.type=static
agent.sources.s1.interceptors.env.key=environment
agent.sources.s1.interceptors.env.value=staging
正则表达式过滤
如果您希望根据正文内容过滤事件,则正则表达式过滤拦截器是您的选择。 根据您提供的正则表达式,它将过滤出匹配的事件或仅保留匹配的事件。 首先将拦截器的type
属性设置为regex_filter
。 您想要匹配的模式是使用 Java 样式的正则表达式语法指定的。 有关用法详细信息,请参阅以下 javadoc:
http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html。
模式字符串在regex
属性中设置。 最后,您需要通过将excludeEvents
属性设置为true
来告诉拦截器是否要排除匹配的记录。 默认值(false
)表示您只希望保持事件与模式匹配。
下表汇总了正则表达式过滤拦截器的属性:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| type
| 是 | String
| regex_filter
|
| regex
| 没有Колибрисистема | String
| .*
|
| excludeEvents
| 没有Колибрисистема | Boolean
| false
|
在此示例中,将删除包含字符串NullPointerException
的任何事件:
agent.sources.s1.interceptors=npe
agent.sources.s1.interceptors.npe.type=regex_filter
agent.sources.s1.interceptors.npe.regex=NullPointerException
agent.sources.s1.interceptors.npe.excludeEvents=true
正则表达式提取器
有时,您可能会希望将事件主体的部分提取到 Flume 标头中,这样您就可以通过通道选择器执行路由。 您可以使用正则表达式提取器拦截器来执行此功能。 首先将拦截器type
设置为regex_extractor
。
agent.sources.s1.interceptors=e1
agent.sources.s1.interceptors.e1.type=regex_extractor
与正则表达式过滤拦截器类似,正则表达式提取器使用 Java 样式的正则表达式语法。 要提取一个或多个字段,首先需要使用组匹配圆括号指定regex
属性。 假设我们要在事件中查找“Error:N”形式的错误号,其中N是某个数字:
agent.sources.s1.interceptors=e1
agent.sources.s1.interceptors.e1.type=regex_extractor
agent.sources.s1.interceptors.e1.regex=Error:\\s(\\d+)
如您所见,我用捕获括号将数字括起来,数字可能由一个或多个数字组成。 现在我已经匹配了我想要的模式,我需要告诉 Flume 如何处理我的匹配。 这里我们需要介绍序列化程序,它为如何解释每个匹配提供了一种可插拔的机制。 在本例中,我只有一个匹配项,因此我的以空格分隔的序列化程序名称列表只有一个条目:
agent.sources.s1.interceptors=e1
agent.sources.s1.interceptors.e1.type=regex_extractor
agent.sources.s1.interceptors.e1.regex=Error:\\s(\\d+)
agent.sources.s1.interceptors.e1.serializers=ser1
agent.sources.s1.interceptors.e1.serializers.ser1.type=default
agent.sources.s1.interceptors.e1.serializers.ser1.name=error_no
name
属性指定要在值是来自正则表达式的匹配文本的情况下使用的事件键。 default
的类型(如果未指定也是默认类型)是一个简单的直通串行化程序。 对于以下事件正文:
NullPointerException: A problem occurred. Error: 123\. TxnID: 5X2T9E.
以下标头将添加到事件中:
{ "error_no":"123" }
如果我想添加TxnID
值作为标题,我只需添加另一个匹配的模式组和序列化程序:
agent.sources.s1.interceptors=e1
agent.sources.s1.interceptors.e1.type=regex_extractor
agent.sources.s1.interceptors.e1.regex=Error:\\s(\\d+).*TxnID:\\s(\\w+)
agent.sources.s1.interceptors.e1.serializers=ser1 ser2
agent.sources.s1.interceptors.e1.serializers.ser1.type=default
agent.sources.s1.interceptors.e1.serializers.ser1.name=error_no
agent.sources.s1.interceptors.e1.serializers.ser2.type=default
agent.sources.s1.interceptors.e1.serializers.ser2.name=txnid
这将为上述输入创建以下标头:
{ "error_no":"123", "txnid":"5x2T9E" }
但是,如果字段反转,如下所示:
NullPointerException: A problem occurred. TxnID: 5X2T9E. Error: 123.
我最终只得到了TxnID
的头。 处理这种排序的更好方法是使用多个拦截器,这样顺序就不重要了:
agent.sources.s1.interceptors=e1 e2
agent.sources.s1.interceptors.e1.type=regex_extractor
agent.sources.s1.interceptors.e1.regex=Error:\\s(\\d+)
agent.sources.s1.interceptors.e1.serializers=ser1
agent.sources.s1.interceptors.e1.serializers.ser1.type=default
agent.sources.s1.interceptors.e1.serializers.ser1.name=error_no
agent.sources.s1.interceptors.e2.type=regex_extractor
agent.sources.s1.interceptors.e2.regex=TxnID:\\s(\\w+)
agent.sources.s1.interceptors.e2.serializers=ser1
agent.sources.s1.interceptors.e2.serializers.ser1.type=default
agent.sources.s1.interceptors.e2.serializers.ser1.name=txnid
除直通之外,Flume 附带的唯一其他类型的序列化程序实现是指定完全限定的类名org.apache.flume.interceptor.RegexExtractorInterceptorMillisSerializer
。 此序列化程序用于将时间转换回毫秒。 您需要基于org.joda.time.format.DateTimeFormat
模式指定模式属性。
例如,假设您正在接收 Apache Web 服务器访问日志。 例如:
192.168.1.42 - - [29/Mar/2013:15:27:09 -0600] "GET /index.html HTTP/1.1" 200 1037
此表达式的完整正则表达式可能如下所示(以 Java 字符串的形式,使用额外的反斜杠转义反斜杠和引号):
^([\\d.]+) \\S+ \\S+ \\[([\\w:/]+\\s[+\\-]\\d{4})\\] \"(.+?)\" (\\d{3}) (\\d+)
匹配的时间模式对应于org.joda.time.format.DateTimeFormat
模式:
yyyy/MMM/dd:HH:mm:ss Z
这使得我们的配置类似于以下代码:
agent.sources.s1.interceptors=e1
agent.sources.s1.interceptors.e1.type=regex_extractor
agent.sources.s1.interceptors.e1.regex=^([\\d.]+) \\S+ \\S+ \\[([\\w:/]+\\s[+\\-]\\d{4})\\] \"(.+?)\" (\\d{3}) (\\d+)
agent.sources.s1.interceptors.e1.serializers=ip dt url sc bc
agent.sources.s1.interceptors.e1.serializers.ip.name=ip_address
agent.sources.s1.interceptors.e1.serializers.dt.type=org.apache.flume.interceptor.RegexExtractorInterceptorMillisSerializer
agent.sources.s1.interceptors.e1.serializers.dt.pattern=yyyy/MMM/dd:HH:mm:ss Z
agent.sources.s1.interceptors.e1.serializers.dt.name=timestamp
agent.sources.s1.interceptors.e1.serializers.url.name=http_request
agent.sources.s1.interceptors.e1.serializers.sc.name=status_code
agent.sources.s1.interceptors.e1.serializers.bc.name=bytes_xfered
这将为前述样本创建以下个标头:
{ "ip_address":"192.168.1.42", "timestamp":"1364588829", "http_request":"GET /index.html HTTP/1.1", "status_code":"200", "bytes_xfered":"1037" }
正文内容不受影响。 您还会注意到,我没有为其他序列化程序的类型指定default
,因为这是默认设置。
备注
此侦听器类型中没有覆盖检查。 例如,使用timestamp
键将覆盖事件的上一个时间值(如果有)。
您可以通过实现org.apache.flume.interceptor.RegexExtractorInterceptorSerializer
接口为该拦截器实现您自己的序列化程序。 但是,如果您的目标是将数据从事件正文移动到标题,则可能需要实现一个自定义拦截器,以便除了设置标题值之外还可以更改正文内容,否则数据将被有效地复制。
总之,让我们回顾一下此拦截器的属性:
|钥匙 / 键 / 关键 / 主调
|
规定的
|
类型 / 品种 / 象征 / 印刷文字
|
不履行 / 拖欠 / 未到庭 / 不到场
|
| --- | --- | --- | --- |
| type
| 是 | String
| Regex_ 提取器 |
| regex
| 是 | String
| |
| serializers
| 是 | 以空格分隔的序列化程序名称列表 | |
| serializers.NAME.name
| 是 | String
| |
| serializers.NAME.type
| 没有Колибрисистема | 实施的默认或 FQDN | default
|
| serializers.NAME.PROP
| 没有Колибрисистема | 序列化程序特定的属性 | |
自定义拦截器
如果有一段自定义代码要添加到您的 Flume 实现中,那么它很可能是一个自定义拦截器。 如前所述,您实现了org.apache.flume.interceptor.Interceptor
接口和相关的org.apache.flume.interceptor.Interceptor.Builder
接口。
假设我需要对我的事件主体进行 URL 解码。 代码如下所示:
public class URLDecode implements Interceptor {
public void initialize() {}
public Event intercept(Event event) {
try {
byte[] decoded = URLDecoder.decode(new String(event.getBody()), "UTF-8").getBytes("UTF-8");
event.setBody(decoded);
} catch UnsupportedEncodingException e) {
// This shouldn't happen. Fall through to unaltered event.
}
return event;
}
public List<Event> intercept(List<Event> events) {
for (Event event:events) {
intercept(event);
}
return events;
}
public void close() {}
public static class Builder implements Interceptor.Builder {
public Interceptor build() {
return new URLDecode();
}
public void configure(Context context) {}
}
}
然后,要配置我的新拦截器,请使用Builder
类的 FQDN 作为类型:
agent.sources.s1.interceptors=i1
agent.sources.s1.interceptors.i1.type=com.example.URLDecoder$Builder
有关如何传递和验证属性的更多示例,请查看现有拦截器实现中的 Flume 源代码以获取灵感。
请记住,自定义拦截器中的任何繁重处理都会影响总体吞吐量,因此请注意实现中的对象混乱或计算密集型处理。
对数据流进行分层
在第 1 章,概述和体系结构中,我们讨论了对数据流进行分层。 想要这样做有几个原因。 您可能希望限制直接连接到 Hadoop 集群的 Flume 代理的数量,以限制并行请求的数量。 在对 Hadoop 集群执行维护时,您的应用服务器上也可能缺少足够的磁盘空间来存储大量数据。 无论您的原因或用例如何,链接 Flume 代理的最常见机制是使用 Avro Source/Sink 对。
КолибрифайлаAVRO 信源/信宿
当我们讨论将 Avro a 位用作 HDFS 中存储的文件的磁盘序列化格式时,我们在章,Sink and Sink Processor中介绍了 Avro a 位。 在这里,我们将把它用于 Flume 特工之间的通信。 典型配置可能如下所示:
要使用 Avro Source,您需要指定type
属性的值avro
。 您需要提供要侦听的绑定地址和端口号:
collector.sources=av1
collector.sources.av1.type=avro
collector.sources.av1.bind=0.0.0.0
collector.sources.av1.port=42424
collector.sources.av1.channels=ch1
collector.channels=ch1
collector.channels.ch1.type=memory
collector.sinks=k1
collector.sinks.k1.type=hdfs
collector.sinks.k1.channel=ch1
collector.sinks.k1.hdfs.path=/path/in/hdfs
在这里,我们已经配置了右侧的代理,它侦听端口 42424,使用内存通道,并写入 HDFS。 这里,我使用了内存通道来简化这个示例配置。 另外,请注意,为了避免混淆,我为该代理指定了一个不同的名称collector
。
左侧的代理-馈送收集器层-可能具有类似如下的配置。 为简洁起见,我在此配置中去掉了源代码:
client.channels=ch1
client.channels.ch1.type=memory
client.sinks=k1
client.sinks.k1.type=avro
client.sinks.k1.channel=ch1
client.sinks.k1.hostname=collector.example.com
client.sinks.k1.port=42424
hostname
值collector.example.com
与该计算机上的代理名称无关,它是具有接收 Avro 源的目标计算机的主机名(或者您可以使用 IP)。 此名为client
的配置将应用于左侧的两个代理,假设两者具有相似的源配置。
由于我不喜欢单点故障,因此我将使用前面的配置配置两个收集器代理,并使用接收器组将每个客户端代理设置为在这两个代理之间进行循环调度。 为了简短起见,我再一次省略了这些来源:
client.channels=ch1
client.channels.ch1.type=memory
client.sinks=k1 k2
client.sinks.k1.type=avro
client.sinks.k1.channel=ch1
client.sinks.k1.hostname=collectorA.example.com
client.sinks.k1.port=42424
client.sinks.k2.type=avro
client.sinks.k2.channel=ch1
client.sinks.k2.hostname=collectorB.example.com
client.sinks.k2.port=42424
client.sinkgroups=g1
client.sinkgroups.g1=k1 k2
client.sinkgroups.g1.processor.type=load_balance
client.sinkgroups.g1.processor.selector=round_robin
client.sinkgroups.g1.processor.backoff=true
发帖主题:Re:Колибри0.7.0
Avro 源也可以与您在第 2 章、Flume 快速入门中注意到的命令行选项之一结合使用。 您可以将avro-client
参数传递给将一个或多个文件发送到 Avro 源,而不是使用agent
参数运行flume-ng
。 以下是帮助文本中的avro-client
特定选项:
avro-client options:
--dirname <dir> directory to stream to avro source
--host,-H <host> hostname to which events will be sent (required)
--port,-p <port> port of the avro source (required)
--filename,-F <file> text file to stream to avro source [default: std input]
--headerFile,-R <file> headerFile containing headers as key/value pairs on each new line
--help,-h display help text
此变体对于测试、因错误而手动重新发送数据或导入存储在其他地方的较旧数据非常有用。
就像 Avro Sink 一样,您必须指定要向其发送数据的主机名和端口。 您可以使用--filename
选项发送单个文件,也可以使用--dirname
选项发送目录中的所有文件。 如果两者都不指定,则将使用stdin
。 下面介绍如何将名为foo.log
的文件发送到我们之前配置的 Flume 代理中:
$ ./flume-ng avro-client --filename foo.log --host collector.example.com --port 42424
输入的每一行都将转换为单个 Flume 事件。
(可选)可以指定包含键/值对的文件来设置 Flume 标头值。 该文件使用 Java 属性文件语法。 如果我有一个名为headers.properties
的文件:
pointOfSale=US
environment=staging
然后,包括--headerFile
选项将在创建的每个事件上设置这两个标头:
$ ./flume-ng avro-client --filename foo.log --headerFile headers.properties --host collector.example.com --port 42424
Log4J 附加器
正如我们在第 5 章,源和通道选择器中讨论的,使用文件系统文件作为源可能会出现问题。 避免此问题的一种方法是在 Java 应用中使用 Flume Log4J 附加器。 在引擎盖下,它使用与 Avro Sink 相同的 Avro 通信,因此您只需将其配置为将数据发送到 Avro Source。
附加器有两个属性,如 XML 所示:
<appender name="FLUME" class="org.apache.flume.clients.log4jappender.Log4jAppender">
<param name="Hostname" value="collector.example.com"/>
<param name="Port" value="42424"/>
</appender>
正文的格式将由附加器的配置布局(未显示)决定。 下表汇总了映射到 Flume 标题的log4j
字段:
凹槽标题键
|
Log4J LoggingEvent 字段
|
| --- | --- |
| flume.client.log4j.logger.name
| event.getLoggerName()
|
| flume.client.log4j.log.level
| event.getLevel()
作为一个数字。 有关映射,请参见org.apache.log4j.Level
。 |
| flume.client.log4j.timestamp
| event.getTimeStamp()
|
| flume.client.log4j.message.encoding
| 不适用。始终为UTF8
。 |
| flume.client.log4j.logger.other
| 只有当在映射前面的一个字段时出现问题时,才会看到-所以通常不会出现这种情况。 |
有关使用 http://logging.apache.org/log4j/1.2/的更多详细信息,请参见Log4J。
您需要在运行时将flume-ng-sdk
JAR 包含在 Java 应用的类路径中,才能使用 Flume 的 Log4J 附加器。
请记住,如果向 Avro Source 发送数据时出现问题,附加器将抛出异常,并且日志消息将被丢弃,因为没有地方放置它。 将其保存在内存中可能会使您的 JVM 堆很快超载,这通常被认为比删除数据记录更糟糕。
负载均衡 Log4J 附加器
我相信您已经注意到,以前的 Log4j 附加器在其配置中只有一个主机名/端口。 如果要跨多个收集器代理分布负载,无论是为了增加容量还是为了容错,可以使用LoadBalancingLog4jAppender
。 此附加器有一个名为Hosts
的必需属性,它是由冒号分隔的主机名和端口号的空格列表,如下所示:
<appender name="FLUME" class="org.apache.flume.clients.log4jappender.LoadBalancingLog4jAppender">
<param name="Hosts" value="server1:42424 server2:42424"/>
</appender>
有一个可选属性Selector
,它指定要进行负载平衡的方法。 有效值为RANDOM
和ROUND_ROBIN
。 如果未指定,则默认为RANDOM
。 您可以实现自己的选择器,但这超出了本书的范围。 如果您感兴趣,请查看LoadBalancingLog4jAppender
类的文档齐全的源代码。
备注
负载平衡 Log4J 附加器的默认选择器机制(如果未指定)是随机的。 您将注意到,这与第 4 章,接收器和接收器处理器中介绍的接收器组的类似功能不同,后者的默认选择器值是循环调度。
这是为什么您应该始终指定您想要的,而不是依赖缺省值的另一个例子。
最后,还有另一个可选属性,用于在无法联系服务器时覆盖指数回退的最长时间。 最初,如果无法联系到服务器,则需要一秒钟后才能重试该服务器。 每次服务器不可用时,重试时间都会加倍,最长默认为 30 秒。 如果我们想要将此最大值增加到 2 分钟,我们可以指定一个MaxBackoff
属性,单位为毫秒,如下所示:
<appender name="FLUME" class="org.apache.flume.clients.log4jappender.LoadBalancingLog4jAppender">
<param name="Hosts" value="server1:42424 server2:42424"/>
<param name="Selector" value="ROUND_ROBIN"/>
<param name="MaxBackoff" value="120000"/>
</appender>
在本例中,我们还覆盖了默认的随机选择器,以使用循环选择。
工艺路线
既然您已经了解了 Flume 中的所有各种机制,那么基于内容将数据路由到不同的目的地应该相当简单。
第一步是通过源端拦截器将您想要切换到 Flume 头中的数据获取到 Flume 头中(如果头还不可用)。 第二步是在该标头值上使用多路复用通道选择器来将数据切换到备用通道。
例如,假设您希望捕获 HDFS 的所有异常。 在此配置中,您可以看到源s1
上通过端口 42424 上的 avro 传入的事件。 测试该事件以查看正文是否包含文本“Exception”。 如果是,它会创建一个标题键exception
(值为Exception
)。 此标头用于将这些事件切换到通道c1
,并最终切换到 HDFS。 如果事件与模式不匹配,它将没有exception
头,并将通过默认选择器传递到通道c2
,在那里它将通过 AVRO 序列化转发到服务器foo.example.com
上的端口 12345。
agent.sources=s1
agent.sources.s1.type=avro
agent.sources.s1.bind=0.0.0.0
agent.sources.s1.port=42424
agent.sources.s1.interceptors=i1
agent.sources.s1.interceptors.i1.type=regex_extractor
agent.sources.s1.interceptors.i1.regex=(Exception)
agent.sources.s1.interceptors.i1.serializers=ex
agent.sources.s1.intercetpros.i1.serializers.ex.name=exception
agent.sources.s1.selector.type=multiplexing
agent.sources.s1.selector.header=exception
agent.sources.s1.selector.mapping.Exception=c1
agent.sources.s1.selector.default=c2
agent.channels=c1 c2
agent.channels.c1.type=memory
agent.channels.c2.type=memory
agent.sinks=k1 k2
agent.sinks.k1.type=hdfs
agent.sinks.k1.channel=c1
agent.sinks.k1.hdfs.path=/logs/exceptions/%y/%M/%d/%H
agent.sinks.k2.type=avro
agent.sinks.k2.channel=c2
agent.sinks.k2.hostname=foo.example.com
agent.sinks.k2.port=12345
摘要
在本章中,我们介绍了 Flume 附带的以下各种拦截器:
- Timestamp:用于添加时间戳头,可能会覆盖已有的时间戳头。
- 主机:用于将 Flume 代理主机名或 IP 添加为事件中的标头。
- 静态:用于添加静态字符串头。
- 正则表达式过滤:用于根据匹配的正则表达式包括或排除事件。
- 正则表达式提取器:用于从匹配的正则表达式标头创建标头。 它对于使用通道选择器进行布线也很有用。
- Custom:它用于创建您需要的、在其他地方找不到的任何自定义转换。
我们还介绍了使用 Avro Source 和 Sink 对数据流进行分层。
接下来,我们介绍了两个 Log4J Appender,单路径和负载平衡版本,用于与 Java 应用直接集成。
最后,我们给出了一个结合使用拦截器和通道选择器来提供路由决策逻辑的示例。
在下一章中,我们将介绍使用 Ganglia 监控 Flume 数据流。
七、监控 Flume
Flume 用户指南说明:
Flume 监控仍在进行中。 变化可能会经常发生。 几个 Flume 组件向 JMX 平台 MBean 服务器报告度量。 可以使用 JConsole 查询这些指标。
虽然 JMX 可以很好地对度量值进行因果浏览,但当您有数百台或甚至数千台服务器在各地发送数据时,查看 JConsole 的眼球数量不会增加。 你需要的是一种一次看完所有东西的方法。 但是,我们要寻找的重要东西是什么呢? 这是一个非常困难的问题,但我将尝试涵盖我认为重要的几个项目,因为我们将在本章中介绍监控选项。
监控代理进程
您要执行的最明显的监视类型是 Flume 代理进程监视,即确保代理仍在运行。 做这类过程监控的产品有很多,所以我们不可能涵盖所有的产品。 如果你在任何一家合理规模的公司工作,很可能已经有了一套针对这一点的制度。 如果是这样的话,不要离开去建造你自己的。 运营部门最不希望看到的就是又一个可以全天候观看的屏幕。
莫尼特
如果您还没有准备好,一个免费增值选项是MONIT(http://mmonit.com/monit/)。 Monit 的开发者有一个付费版本,它提供了更多您可能想要考虑的花哨功能。 即使是在自由表单中,它也可以为您提供一种检查 Flume 代理是否正在运行的方法,如果没有重新启动它,并在发生这种情况时向您发送一封电子邮件,以便您可以查看其死亡原因。
Monit 的功能要多得多,但这是我们在这里要介绍的功能。 如果您很聪明(我知道您很聪明),除了我们在本章中介绍的内容之外,您将至少添加对磁盘、CPU 和内存使用情况的检查。
纳吉奥斯
Flume 代理进程监控的另一个选项是Nagios(http://www.nagios.org/)。 与 Monit 一样,您可以将 Nagios 配置为监视 Flume 代理,并通过 WebUI、电子邮件或 SNMP 陷阱向您发出警报。 也就是说,它没有重启功能。 该社区相当强大,并且有许多其他应用的插件可用。 我的公司使用它来检查 Hadoop WebUI 的可用性。 虽然不能全面了解运行状况,但它确实为我们的 Hadoop 生态系统的整体监控提供了更多信息。
同样,如果你的公司已经有了工具,在引入另一个工具之前,看看你是否可以重复使用它们。
监控绩效指标
既然我们已经介绍了进程监视的几个选项,那么如何知道您的应用是否真的在执行您认为的工作呢? 在很多情况下,我看到一个停滞的syslog-ng
进程似乎正在运行,但它就是不发送任何数据。 我并不是特别针对syslog-ng
;所有软件都会在出现其设计不能处理的情况时这样做。
在谈论 Flume 数据流时,您需要监视以下内容:
- 输入来源的数据在预期速率范围内
- 数据没有溢出您的通道
- 数据正以预期的速度退出下沉
Flume 有一个可插拔的监控框架,但正如本章开头所提到的,它在很大程度上仍在进行中。 这并不意味着你不应该使用它,因为那样做是愚蠢的。 这意味着无论何时升级,您都需要准备额外的测试和集成时间。
虽然 Flume 文档中没有介绍,但通常会在 Flume JVM(http://bit.ly/javajmx)中启用 JMX,并使用 Nagios JMX 插件(http://bit.ly/nagiosjmx)来警告 Flume 代理中的性能异常。
神经节
监视 Flume 内部指标的可用监视选项之一是Ganglia 集成。 Ganglia(http://ganglia.sourceforge.net/)是一个开源监控工具,用于收集指标、显示图表,并且可以分层来处理非常大的安装。 要将 Flume 指标发送到 Ganglia 群集,您需要在启动时将一些属性传递给您的代理:
|Java 属性
|
重要性 / 价值观念 / 标准 / 值
|
描述 / 描写 / 形容 / 类别
|
| --- | --- | --- |
| flume.monitoring.type
| gangla
| 设置为gangla
。 |
| flume.monitoring.hosts
| host1:port1,host2:port2
| 以逗号分隔的gmond
进程的主机:端口对列表。 |
| flume.monitoring.pollInterval
| 60
| 发送数据之间的间隔秒数(默认值:60 秒)。 |
| flume.monitoring.isGanglia3
| false
| 如果使用较旧的 Ganglia 3 协议,则设置为true
。 默认使用 v3.1 协议发送。 |
查看同一网络广播域中的gmond
的每个实例(因为可达性基于组播数据包),并找到gmond.conf
中的udp_recv_channel
块。 假设我有两台附近的服务器,具有这两个相应的配置块:
udp_recv_channel {
mcast_join = 239.2.14.22
port = 8649
bind = 239.2.14.22
retry_bind = true
}
udp_recv_channel {
mcast_join = 239.2.11.71
port = 8649
bind = 239.2.11.71
retry_bind = true
}
在本例中,第一台服务器的 IP 和端口为239.2.14.22/8649
,第二台服务器的 IP 和端口为239.2.11.71/8649
,导致以下启动属性:
-Dflume.monitoring.type=gangla
-Dflume.monitoring.hosts=239.2.14.22:8649,239.2.11.71:8649
在这里,我使用轮询间隔的默认值,并使用较新的 Ganglia 有线协议。
备注
虽然 Ganglia 支持通过 TCP 接收数据,但当前的 Flume/Ganglia 集成仅支持使用多播 UDP 发送数据。 如果您有一个大型/复杂的网络设置,如果事情不像您期望的那样工作,您会想要接受网络工程师的培训。
内部 HTTP 服务器
您可以将 Flume 代理配置为启动 HTTP 服务器,该服务器将输出可由使用外部机制的查询使用的 JSON。 与 Ganglia 集成不同,某些外部实体必须进入 Flume 代理来轮询数据。 理论上,您可以使用 Nagios 轮询此 JSON 数据并在某些情况下发出警报,但我个人从未尝试过。 当然,此设置在开发和测试中非常有用,特别是当您编写自定义 Flume 组件以确保它们生成有用的指标时。 以下是在启动 Flume 代理时需要设置的 Java 属性的摘要:
|Java 属性
|
重要性 / 价值观念 / 标准 / 值
|
描述 / 描写 / 形容 / 类别
|
| --- | --- | --- |
| flume.monitoring.type
| http
| 设置为http
|
| flume.monitoring.port
| 端口号 | 绑定 HTTP 服务器的端口号 |
指标的 URL 如下所示:
http://SERVER_OR_IP_OF_AGENT:PORT/metrics
这用于以下 Flume 配置:
agent.sources = s1
agent.channels = c1
gent.sinks = k1
agent.sources.s1.type=avro
agent.sources.s1.bind=0.0.0.0
agent.sources.s1.port=12345
agent.sources.s1.channels=c1
agent.channels.c1.type=memory
agent.sinks.k1.type=avro
agent.sinks.k1.hostname=192.168.33.33
agent.sinks.k1.port=9999
agent.sinks.k1.channel=c1
此外,还有以下启动参数:
-Dflume.monitoring.type=http
-Dflume.monitoring.port=44444
转到http://SERVER_OR_IP:44444/metrics
、,您可能会看到类似以下内容:
{
"SOURCE.s1":{
"OpenConnectionCount":"0",
"AppendBatchAcceptedCount":"0",
"AppendBatchReceivedCount":"0",
"Type":"SOURCE",
"EventAcceptedCount":"0",
"AppendReceivedCount":"0",
"StopTime":"0",
"EventReceivedCount":"0",
"StartTime":"1365128622891",
"AppendAcceptedCount":"0"},
"CHANNEL.c1":{
"EventPutSuccessCount":"0",
"ChannelFillPercentage":"0.0",
"Type":"CHANNEL",
"StopTime":"0",
"EventPutAttemptCount":"0",
"ChannelSize":"0",
"StartTime":"1365128621890",
"EventTakeSuccessCount":"0",
"ChannelCapacity":"100",
"EventTakeAttemptCount":"0"},
"SINK.k1":{
"BatchCompleteCount":"0",
"ConnectionFailedCount":"4",
"EventDrainAttemptCount":"0",
"ConnectionCreatedCount":"0",
"BatchEmptyCount":"0",
"Type":"SINK",
"ConnectionClosedCount":"0",
"EventDrainSuccessCount":"0",
"StopTime":"0",
"StartTime":"1365128622325",
"BatchUnderflowCount":"0"}
}
如您所见,每个信源、信宿和信道都与其对应的度量分开。 每种类型的源、通道和接收器都提供了自己的一组度量键,尽管它们有一些共同点,因此一定要检查哪些内容看起来很有趣。 例如,此 Avro Source 有OpenConnectionCount,
,这是连接的客户端数(最有可能发送数据)。 这可能会帮助您确定是否有预期数量的客户端依赖于数据,或者客户端可能太多而需要开始对代理进行分层。
一般来说,通道的ChannelSize
或ChannelFillPercentage
会让您很好地了解数据传入的速度是否快于数据传出的速度。 它还会告诉您是否已将其设置得足够大,以满足您的数据卷的维护/停机需求。
查看接收器,与尝试的次数相比,EventDrainSuccessCount
与EventDrainAttemptCount
将告诉您输出成功的频率。 在本例中,我将 Avro Sink 配置为一个不存在的目标。 正如您所看到的,ConnectionFailedCount
值正在增长,这是持久性连接问题的一个很好的指示器。 即使是增长的ConnectionCreatedCount
也可能表明连接中断和重新打开的频率太高。
真的,除了看ChannelSize
/ChannelFillPercentage
之外,没有硬性规定。 每个用例都有自己的性能配置文件,所以从小处着手,设置您的监控,并边学边学。
自定义监控挂钩
如果您已经有了监控系统,那么您可能需要做额外的工作来开发一个自定义的监控报告机制。 您可能认为它与实现org.apache.flume.instrumentation.MonitorService
接口一样简单。 您确实需要这样做,但是查看界面时,您将只看到一个start()
方法和一个stop()
方法。 与更明显的拦截器范例不同,如果您的MonitorService
实现是将数据发送到接收服务的类型,则代理期望它将启动/停止线程,以在预期或配置的间隔内发送数据。 如果您要操作一项服务,比如 HTTP 服务,那么 Start/Stop 将用于启动和停止您的侦听服务。 指标本身由各种源、汇点、通道和拦截器使用以org.apache.flume
开头的对象名称在内部发布到 JMX。 您的实现将需要从MBeanServer
读取这些内容。 如果您决定实现您自己的实现,我能给您的最好建议是查看两个现有实现的源代码,并按照它们所做的去做。 要使用监视挂钩,请将flume.monitoring.type
属性设置为实现类的 FQDN。 预计必须使用新的 Flume 版本重新制作任何自定义钩子,直到框架成熟和稳定。
摘要
在本章中,我们讨论了从过程级别和内部指标(它是否在工作?)监视 Flume 代理。
Monit 和 Nagios 是作为进程监视的开源选项引入的。
接下来,我们介绍了 Apache Flume 附带的 Ganglia 和 JSON over HTTP 实现的 Flume 代理内部监视指标。
最后,我们介绍了如何在需要直接集成到默认情况下 Flume 不支持的其他工具的情况下集成自定义监视实现。
在最后一章中,我们将讨论有关 Flume 部署的一些一般注意事项。
八、实时分布式数据采集的实际情况
在这最后一章中,我想我们将讨论一些我对 Hadoop 数据收集的不太具体、更随机的想法。 这背后并没有严格的科学依据,你完全可以放心地反对我的观点。
虽然 Hadoop 是一个使用大量数据的伟大工具,但我经常会想起 1886 年发生在明尼苏达州圣克罗伊河上的堵塞(http://www.nps.gov/sacn/historyculture/stories.htm)。 在处理太多数据时,您需要确保不会堵塞河流。 请务必认真对待上一章中关于监控的内容,而不是仅仅将其视为一件好事。
传输时间与日志时间
我遇到过这样一种情况:使用文件名中的日期模式放置数据,并且/或者 HDFS 中的路径与目录的内容不匹配。 预计 2013/03/29 年度的数据将包含 2013 年 3 月 29 日的所有数据。 但现实情况是,日期是从运输工具中取消的。 事实证明,我们使用的 syslog 版本正在重写标题,包括日期部分,导致数据占用传输时间,而不反映记录的原始时间。 通常情况下,偏移量很小--只有一两秒钟--所以没有人真正注意到。 但有一天,其中一台中继服务器死了,当滞留在上游服务器上的数据最终被发送时,它的时间是当前的。 在这种情况下,它被移了几天。 啊!怎么这么乱呀。
如果您按日期放置数据,请确保这种情况不会发生在您身上。 检查日期边缘案例以确定它们是否符合您的预期,并确保在真正投入生产之前测试您的停机场景。
正如我前面提到的,由于计划内或计划外维护(甚至是微小的网络故障)而导致的这些重新传输很可能会导致重复和无序事件的到来,因此在处理原始数据时一定要考虑到这一点。 Flume 不提供单次交货/订购保证。 如果需要,可以改用事务性数据库。
时区是邪恶的
如果你错过了我在第 4 章,Sink and Sink Processor中反对使用本地时间的偏见,我在这里再重复一遍--时区是邪恶的。 邪恶如邪恶博士(http://en.wikipedia.org/wiki/Dr._Evil)--让我们不要忘记它的“迷你我”(http://en.wikipedia.org/wiki/Mini-Me)对应的夏令时。
我们现在生活在一个全球化的世界里。 您正在将数据从各地拉到您的 Hadoop 集群中。 您甚至可能在国家(或世界)的不同地区拥有多个数据中心。 在尝试分析数据时,您最不想做的事情就是处理歪曲的数据。 夏令时在地球上的某个地方一年中至少会改变十几次。 只需查看历史(ftp://ftp.iana.org/tz/releases/
)。 省得自己头疼,把它正常化到协调世界时就行了。 如果你想在它变成人类眼球的路上把它转换成“当地时间”,那就放心吧。 但是,当它位于您的集群中时,请将其标准化为 UTC。 考虑通过此 Java 启动参数随时随地采用 UTC(如果您不能在系统范围内设置它):
-Duser.timezone=UTC
我住在芝加哥,我们工作的电脑使用的是中部时间,根据夏令时进行调整。 在我们的 Hadoop 集群中,我们喜欢将数据保存在YYYY/MM/DD/HH
布局中。 一年两次,有些东西会轻微破碎。 在秋天,我们凌晨 2 点的数据是原来的两倍。 目录。 春天没有凌晨 2 点。 目录。 疯了!
能力规划
无论您认为您拥有多少数据,情况都会随着时间的推移而改变。 新项目将弹出,现有项目的数据创建率将发生变化(向上或向下)。 数据量通常会随着当天的流量而起伏不定。 最后,为 Hadoop 集群提供支持的服务器数量将随着时间的推移而变化。
对于 Hadoop 群集中应该保留多少额外的存储容量,有许多流派的看法(我们使用 20%这个完全不科学的值-这意味着我们通常在订购额外硬件时计划 80%的存储容量已满,但在达到 85%到 90%的利用率数字之前不要开始恐慌)。
您可能还需要在单个代理内设置多个流。 源处理器和宿处理器目前都是单线程的,因此当数据量很大时,调优批处理大小的能力是有限的。
供给 Hadoop 的 Flume 代理数量应根据实数进行调整。 观察通道大小,查看正常负载下的写入保持情况。 调整最大信道容量,以处理任何让您感觉良好的开销。 您总是可以花费远远超过您所需的费用,但即使是长时间的停机也可能超出最保守的估计。 这就是你必须挑选和选择哪些数据对你更重要,并调整你的通道容量以反映这一点的时候。 这样,如果您超出了您的限制,不太重要的数据将首先被丢弃。
您的公司可能没有无限的资金,在某个时候,数据的价值与继续扩展集群的成本之间的关系将开始受到质疑。 这就是为什么对收集的数据量设定限制是非常重要的。 任何将数据发送到 Hadoop 的项目都应该能够说明这些数据的价值,以及如果我们删除较旧的内容会有什么损失。 只有这样,开支票的人才能做出明智的决定。
多个数据中心的注意事项
如果您在个数据中心之外运营业务,并且收集了大量数据,您可能需要考虑在每个数据中心设置 Hadoop 群集,而不是将所有收集的数据发送回单个数据中心。 这将使分析数据变得更加困难,因为您不能只对所有数据运行一个 MapReduce 作业。 相反,您必须运行并行作业,然后在第二遍中合并结果。 您可以通过搜索和计算问题来做到这一点,但不能像平均数这样的东西-平均数的平均数与平均数不同。
将您的所有数据集中到一个集群中也可能超出您的网络所能处理的范围。 根据您的数据中心之间的连接方式,您可能无法传输所需的数据量。 最后,考虑到完全群集故障或损坏可能会抹去所有内容,因为大多数群集通常太大,无法备份除高价值数据以外的所有内容。 在这种情况下,拥有一些旧数据有时总比什么都没有要好。 对于多个 Hadoop 集群,如果您不想等待发送到本地集群,则可以使用故障转移接收器处理器将数据转发到不同的集群。
如果您确实选择将所有数据发送到单个目的地,请考虑添加一台大磁盘容量的计算机作为数据中心的中继服务器。 这样,如果出现通信问题或延长集群维护时间,您可以让数据堆积在与试图服务客户的机器不同的机器上。 即使在单个数据中心的情况下,这也是合理的建议。
合规性和数据过期
请记住,贵公司正在收集的客户数据可能包含敏感信息。 您可能受到其他访问数据的法规限制,如支付卡行业(PCI-http://en.wikipedia.org/wiki/PCI_DSS)或萨班斯·奥克斯利(SOX-http://en.wikipedia.org/wiki/Sarbanes%E2%80%93Oxley_Act)。 如果您没有正确处理对集群中这些数据的访问,政府将依靠您,或者更糟的是,如果他们觉得您没有保护他们的权利和身份,您将不再有客户。 考虑对您的个人信息数据进行加扰、修剪或模糊处理。 你想要的商业洞察力可能更多地落入“有多少搜索锤子的人真的买了?”这一类。 而不是“有多少客户叫 Bob?” 正如您在第 6 章,拦截器中看到的,当您四处移动时,编写一个拦截器来混淆个人身份信息(PII-http://en.wikipedia.org/wiki/Personally_identifiable_information)是非常容易的。
您的公司可能有一个文档保留策略,其中很可能包括您要放入 Hadoop 的数据。 确保你删除了你的政策规定你不应该再保留的数据。 你最不想看到的就是律师的来访。
摘要
在本章中,我们介绍了规划 Flume 实施时需要考虑的几个实际注意事项,包括:
- 传输时间并不总是与事件时间匹配
- 在你基于时间的逻辑中引入夏令时的混乱
- 容量规划注意事项
- 当您拥有多个数据中心时需要考虑的事项
- 数据合规性
- 数据过期
我希望你喜欢这本书。 希望您能够在您的应用/Hadoop 集成工作中直接应用这些信息。
谢谢。 这很有趣。