8620-84511745

Blog Post

Kafka和Unix管道的示例

来源:zqhxuyuan.github.io 原文:http://zqhxuyuan.github.io/2016/01/05/2016-01-05-Kafka-Unix/

                           什么是Kafka

想象下有这样的对话.

你: 什么是Apache Kafka? 我: Apache Kafka是发布-订阅消息系统,分布式的提交日志 你: …什么? 我: 是的,它是一个分布式的,分区的,复制的提交日志服务 你: 你到底在说什么?

上面的描述(我)是正确的. 你只需要知道这些术语是什么意思, 但是如果你不知道这些术语,就会感到很困惑.

那就让我们以另外一种方式来解释吧. 我喜欢通过例子来学习, 并且在学习的时候通过和我已经知道的东西互相比较,

我发现这种学习方式非常有帮助. 那么我们就以这种举例子,并且比较的方式来描述什么是Kafka吧.

                        Kafka就像Unix的管道

我会给一些例子来说明Kafka能干什么, 比较的对象是很多人都熟悉的: 命令行的Unix管道

看一个简单的例子:

屏幕快照 2016-06-13 下午2.39.23.png 这段脚本找出以.txt结尾的文件中所有包含单词”hello”的行.它包含了三个步骤/阶段:

  • 从所有文件中输出所有行
  • 将所有文本转换为小写
  • 找出含有”hello”单词的行

所有这些步骤的每一个都写到标准输出流,后面的阶段会从标准的输入流中读取. 最简单的来看, Kafka就像一个Unix的管道: 你将数据写到它的一端, 然后数据从另一端出来.

(严格来说,你写的数据会通过网络传输,你读取的数据也是通过网络,不过现在我们暂时忽略这些.)

如果这就是Kafka所能做的,那有什么了不起的,对吧?实际上Kafka还有一些额外的特征,带来新的能力.

                       结构化数据

Unix的管道在文本数据行之间流动,通常是以新的一行为结束(这条管道). 这些行可以很长,但是工作单元仍然是一行文本.

如果你处理的不是ASCII数据,或者你处理的数据不能以一一行来表示就会有点麻烦. 而Kafka支持任意的格式和任意大小.

这就允许你可以存储任何数据到Kafka中: 文本,CSV,二进制数据,自定义编码数据等等. 对于Kafka而言,它只是一系列的 消息,其中每条消息都是一系列的字节. 比如可以(模拟)写一个Kafka的”命令行”:

屏幕快照 2016-06-13 下午2.41.23.png 这里的filter_tweets命令可能不是一个简单的基于字符串的grep,而是一种能够理解从TwitterFeed输出的数据格式.

比如TwitterFeed可能输出JSON,则filter_tweets需要做些JSON的处理.TwitterFeed如果返回的是二进制数据,

则filter_tweets需要知道二进制的格式/协议. 这种灵活性可以让Kafka成为一种发送任何数据类型的Unix管道.

                         数据持久化

我们可能有一个复杂的会花费一些时间才能跑完的命令.如果只运行一次,你可能不关心.但是如果你要多次迭代运行,

你可能会会将输出结果先写到一个文件中, 这样之后的阶段可以更快地迭代,而不需要重新多次运行很慢的那部分命令.

屏幕快照 2016-06-13 下午2.42.30.png 这个模式工作的很好,但是这意味着你需要提前计划去做(先写文件). 如果管道自身能够做这件时间就方便多了.

Kafka会持久化你发送的所有数据到磁盘上.持久化非常方便,不仅节省了你的一些时间,它还允许你能做之前不能做的一些事情.

就像上面的命令行一样,每个阶段的输出都被保存下来. 由于第一个阶段的输出被保存了,第二个阶段甚至不要求正在运行.

这种方式, Kafka作为生产者数据和消费者数据之间的缓冲区. 它保持了数据,允许消费者可用并且准备好的时候才读取数据.

Kafka是高性能的,它甚至可以运行在多台机器上,并且可以复制统一分数据到多台机器防止数据丢失造成的风险.

三个Kafka节点组成的集群能够处理每秒钟两百万的写入, 并能使网卡饱和.

由于数据被持久化到了Kafka中,并没有要求消费者要多快去读取数据.消费者可以想多快就多快,想多慢就多慢地读取数据.

因此它允许一个高性能的生产者, 并不会因为一个很慢的消费者而江堤生产者的性能. 看一个很慢的消费者的例子.

屏幕快照 2016-06-13 下午2.43.05.png

从密码学我们知道,将一个数字因式分解成质数是很慢的.假设我们分解了100万个数字,程序挂掉了.

当下次重启程序的时候如果能够从上次离开位置的那个点继续处理,而不是重复很多工作,那就很友好了.

以这个例子中,我期望的是从numbers.txt中的第一百万零一行开始继续处理.

Kafka有类似的概念叫做”offset”.Kafka中的每条记录都被分配了有序的offset,消费者可以选择在指定的offset重新开始.

数据持久化和offsets这两个特性,允许你构建一个消费者数据和生产者数据分开的系统. 数据持久化–非常快的数据持久化–意味着它能很快地吸收大批量的数据.

它允许消费者按照它能够读取的任何速度读取数据.它允许持久化数据, 即使消费者挂掉了.

offsets允许消费者继续执行, 无论它上次在什么地方退出,而不会重复工作.

在某种情况下,这是很有意义的: 你并不想在一次汇款中从银行账号中扣了两次钱.

另一方面,这是出于效率方面考虑的: 你并不想重新对已经处理的数字重新进行因式分解.

无论哪种情况, 这两个特性都允许你做传统的Unix管道所不能做的事情.

                          流数据

再看下第一个例子: 屏幕快照 2016-06-13 下午2.43.48.png 在这里例子中,第一个阶段(cat)输出所有的行然后就结束了. 整个管道会找到所有包含单词”hello”的行最后命令结束.

和下面的命令进行比较:

屏幕快照 2016-06-13 下午2.44.41.png

这个命令不会结束, 第一个阶段(tail)输出一些行,但是仍然保持着监听更多的数据.

如果你在之后往其中的一个添加了一行,tail命令会输出这个新行, 然后接下来的命令会处理它.

Kafka支持相同的概念.数据写到到Kafka并且被消费者读取可以看做一个流.

如果消费者到达数据的末尾, 它会继续等待即将到来的更多的数据. 当新的数据写入到Kafka,它会很快地被发送到消费者.

我在之前说过数据流进Kafka是很快的, 实际上数据从Kafka流出也是很快的.

一条记录被添加到Kafka后,能够在20ms之内发送给一个正在等待的消费者.

现在我们知道Kafka除了支持数据持久化,也支持流数据. 我们复习下之前的例子

屏幕快照 2016-06-13 下午3.27.19.png 上面的命令看起来向上一种批处理模式,因为produceNumbers最终会结束的.

但是数字是无限的,它永远不会结束, 所以实际上看起来应该是这样的:

屏幕快照 2016-06-13 下午3.28.30.png 这里我自己造了一个语法: |*表示这是一个Kafka管道.它能够归档所有东西到磁盘,并且发送流式的更新.

streaming updates流式更新, 数据是流式传入的,下游的方法基于最新的流数据做更新操作. 即对流数据更新操作

这种流式的数据允许你创建一个实时的管道,这里有个例子:

屏幕快照 2016-06-13 下午3.29.51.png 这个管道会查询你的web服务器日志. 它会提取主页的所有pageload,获取出页面加载的时间,创建一个可视化的图,并及时更新.

太棒了,你刚刚创建了一台服务器的监控面板. 如果页面加载时间抖动,你可以在几秒内从图中观察到.

所有的这些Kafka管道(每个|*)都会持久化和缓冲数据. 管道中的任何一个阶段都可能出错,并在任何时候重启,

并且可以在它们上次离开的地方继续. 它们可以处理的很慢,或者一直紧紧跟着(上一个阶段).

或者如果它们落后的太多,可以被停止,并移到新拥有更快CPU的服务器上,也能够从上一次作业离开的地方继续.

你还可以创建一些其他类型的实时管道:

● 在黑色星期五这天实时更新你的店铺的销量.你不仅能够实时获知哪些物品的销 量,还能实时地响应:对畅销品订阅更多的库存.

● 实时收集登陆次数,并注入到指令监测系统用来检查正在进行的攻击,并且能够 屏 蔽欺诈的IP地址

● 实时更新交通速度传感器,你能够分析交通模式,并控制交通灯的时间

                          Fan in

Kafka同时也支持多个生产者往相同的地方写数据. 想象下前面的场景,但现在从多个服务器上收集web服务器日志.

屏幕快照 2016-06-13 下午3.31.57.png 所有的服务器以漏斗形式的数据流入到Kafka管道. 你只有一个grep的进程在运行, 获取加载时间的进程在运行,

只有一个绘图的进程在运行. 但是它们是基于所有web服务器的输出日志的聚合.恭喜你,现在创建了一个数据中心的监控面板.

这里的好处是你可以从很多的地方收集数据, 但是只在一个中央的地方存储并处理所有这些收集到的数据.

Kafka可以成为你的公司中所有数据的中心收集节点. 将分散在各个服务器上的数据都收集到统一一个节点.

                            Fan out

Kafka不仅支持多个生产者写到同一个地方,也支持多个消费者从相同的地方读取数据.

屏幕快照 2016-06-13 下午3.33.42.png 再强调一次,Kafka在多个阶段之间能够缓冲数据. 上面的三个管道:find_ip_address, grep index.html,

get_login_attempts–都能够按照自己的步伐(消费速度)从access_log这个Kafka管道中读取数据.

前面两个看起来会相当快,但是第三个可能会慢点.但是没关系,Kafka会保持这些数据(不会因为其他消费者消费了就删除数据)

这样的好处是一个单一的数据源可能用不同的方式处理,每种使用方式都和其他方式都是独立的,并且不会相互影响.

假设我们找到了一种检测黑客的方式. 我们可以将detect_hackers实例部署在已有的实例旁(共存),然后一起测试.

对于相同的输入,看看他们都有什么不同的表现(验证我们的新的检测方式是否达到了预期的效果).

屏幕快照 2016-06-13 下午3.34.27.png 一旦我们决定选择使用新的方式会更好点,我们会通知下游的notify_security作业监听更好的检测方式.

屏幕快照 2016-06-13 下午3.34.57.png 并且新的方式真的很稳定了,我们可以将老的检测方式移除掉.

屏幕快照 2016-06-13 下午3.35.26.png 看看我们都做了什么?

  • 我们在生产环境的数据上直接运用新的算法,并做了真实的测试
  • 对相同的数据,将新的算法和旧的算法一起测试
  • 仅仅使用了一个开关就更改了notify_security作业的输入
  • 保持旧的算法继续运行,以防需要切回去(上面的场景实际是将旧的算法删除了)

这个特性使得Kafka带给我们的威力非常大.通过将同一份数据分散到多个地方,我们可以从数据中获得多个分组的能力.

每个管道的工作都是独立的并且都是以自己的消费速度进行的. 并且让我们在开发新的功能时能够重用已经存在的数据.

                            并行

让我们专注于上面多个管道中的其中一个.

屏幕快照 2016-06-13 下午3.36.55.png 假设geoip(地理位置)数据库是非常慢的. Kafka会在这个阶段之前缓冲所有的数据,所以即使很慢,也不会丢失任何数据.

但是查询geoip会拖慢整个管道的速度. 所以你会部署一个很快速的geoip数据库. 但是这并不能帮你太多, 因为你每次 都是从find_ip_address的输出结果中一条接着一条地查询. 你真正需要的是并行!

Kafka支持在你的Kafka管道中添加子管道(sub-pipes).

你可以将所有以1结束的IP地址发送到第一个子管道,将所有以 2结束的IP地址发送到第二个管道,等等. 现在你的请求能够通过round-robin的方式发送到数据库中. 看起来是这样的:

屏幕快照 2016-06-13 下午3.38.01.png Kafka管道中的数字0到9表示所有以这个数字结束的IP地址,会被放到相同的管道中(图中每个geoip_lookup都是一个子管道)

每个geoip_lookup作业都只会从find_ip_address管道中读取一部分数据,可以允许你以并行的方式查询:一次10个线程.

这种方式应该能满足你在geoip阶段快速地在地球图形上绘点, 这下你满意了吧!

Kafka称所有的这些是partitions. 它允许你将数据以逻辑的分组方式分到多个通道中,但是每个函数都是独立的.

一批数据会分散到多个节点, 每个节点之间都做同样的工作. 但是它们之间不会相互影响的.

                        Kafka和Unix哲学

仔细看看上面的例子,你会发现Kafka的管道这个角色是很小的.Kafka管道并不会做过滤IP地址的工作,不会做查询IP地址的工作,

也不会对很大的数字做因式分解. 这都取决于你. Kafka做的事情是将你的所有工具都联系在一起.这样看来它就像胶水/粘合物.

但是它这个粘合物能够让你构建出很多有趣的东西. Kafka负责很多平凡的事情,而这些是作为事情的解决者的你并不愿意去做的.

它能够帮你保存数据,能在任何一个点开始读取数据,可以从多个数据源聚合数据,并将数据同时发送给多个目标.

Kafka这种能力让你重新思考解决问题的方式. 将一个问题分解成多个阶段,每个阶段可以单独开发实现,并独立地测试.

这一切都是基于Kafka能将所有的组件都粘合在一起. 而且Kafka可以在网络之间完成这些事情, 所以你甚至可以将你的计算组件 分布在多个节点, 也就有了水平扩展, 分布式处理, 高可用性等特点.

这种将一个大问题分解成多个小问题的思想和Unix的哲学是一致的. 实际上Unix管道的发明人Doug McIlroy这么说过:

This is the Unix philosophy: Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.

Kafka允许你将Unix哲学运用到工程师急待解决的大数据量,低延迟,网络之间的问题.

                              声明

在这篇文章中,我简化了一些事情,现在我们解释下之前遗留的东西.

  • Kafka是一个软件,你能够通过网络和它对话. 它有自己定制的网络协议,但有客户端库帮你做这些事情了.
  • 有方便的命令行kafka-console-producer.sh读取标准输入流写到Kafka中.
  • kafka-console-consumer.sh可以从Kafka中读取输出,输出到标准输出流.你可以使用他们实现上面的命令.
  • Kafka客户端使得你能够从Kafka中读写数据构建自己的应用程序
  • Kafka的管道实际上是叫做”topics”
  • Kafka的topic都有名称. 每个topic的数据集和其他topic都是分开的.

Posted in 其他 on Jun 13, 2016