想象下有这样的对话.html
你: 什么是Apache Kafka?
我: Apache Kafka是发布-订阅消息系统,分布式的提交日志
你: …什么?
我: 是的,它是一个分布式的,分区的,复制的提交日志服务
你: 你到底在说什么?java
上面的描述(我)是正确的. 你只须要知道这些术语是什么意思, 可是若是你不知道这些术语,就会感到很困惑.web
那就让咱们以另一种方式来解释吧. 我喜欢经过例子来学习, 而且在学习的时候经过和我已经知道的东西互相比较,
我发现这种学习方式很是有帮助. 那么咱们就以这种举例子,而且比较的方式来描述什么是Kafka吧.算法
我会给一些例子来讲明Kafka能干什么, 比较的对象是不少人都熟悉的: 命令行的Unix管道数据库
看一个简单的例子:apache
1 |
$ cat *.txt | tr A-Z a-z | grep hello |
这段脚本找出以.txt结尾的文件中全部包含单词”hello”的行.它包含了三个步骤/阶段:服务器
全部这些步骤的每个都写到标准输出流,后面的阶段会从标准的输入流中读取.网络
最简单的来看, Kafka就像一个Unix的管道: 你将数据写到它的一端, 而后数据从另外一端出来.
(严格来讲,你写的数据会经过网络传输,你读取的数据也是经过网络,不过如今咱们暂时忽略这些.)分布式
若是这就是Kafka所能作的,那有什么了不得的,对吧?实际上Kafka还有一些额外的特征,带来新的能力.wordpress
Unix的管道在文本数据行之间流动,一般是以新的一行为结束(这条管道). 这些行能够很长,可是工做单元仍然是一行文本.
若是你处理的不是ASCII数据,或者你处理的数据不能以一一行来表示就会有点麻烦. 而Kafka支持任意的格式和任意大小.
这就容许你能够存储任何数据到Kafka中: 文本,CSV,二进制数据,自定义编码数据等等. 对于Kafka而言,它只是一系列的
消息,其中每条消息都是一系列的字节. 好比能够(模拟)写一个Kafka的”命令行”:
1 |
$ TwitterFeed | filter_tweets From @apachekafka |
这里的filter_tweets命令可能不是一个简单的基于字符串的grep,而是一种可以理解从TwitterFeed输出的数据格式.
好比TwitterFeed可能输出JSON,则filter_tweets须要作些JSON的处理.TwitterFeed若是返回的是二进制数据,
则filter_tweets须要知道二进制的格式/协议. 这种灵活性可让Kafka成为一种发送任何数据类型的Unix管道.
咱们可能有一个复杂的会花费一些时间才能跑完的命令.若是只运行一次,你可能不关心.可是若是你要屡次迭代运行,
你可能会会将输出结果先写到一个文件中, 这样以后的阶段能够更快地迭代,而不须要从新屡次运行很慢的那部分命令.
1 2 |
$ find . -type f | grep \.java > javafiles.txt $ cat javafiles.txt | xargs grep ClassName |
这个模式工做的很好,可是这意味着你须要提早计划去作(先写文件). 若是管道自身可以作这件时间就方便多了.
Kafka会持久化你发送的全部数据到磁盘上.持久化很是方便,不只节省了你的一些时间,它还容许你能作以前不能作的一些事情.
就像上面的命令行同样,每一个阶段的输出都被保存下来. 因为第一个阶段的输出被保存了,第二个阶段甚至不要求正在运行.
这种方式, Kafka做为生产者数据和消费者数据之间的缓冲区. 它保持了数据,容许消费者可用而且准备好的时候才读取数据.
Kafka是高性能的,它甚至能够运行在多台机器上,而且能够复制统一分数据到多台机器防止数据丢失形成的风险.
三个Kafka节点组成的集群可以处理每秒钟两百万的写入, 并能使网卡饱和.
因为数据被持久化到了Kafka中,并无要求消费者要多快去读取数据.消费者能够想多快就多快,想多慢就多慢地读取数据.
所以它容许一个高性能的生产者, 并不会由于一个很慢的消费者而江堤生产者的性能. 看一个很慢的消费者的例子.
1 2 |
$ produceNumbers > numbers.txt $ cat numbers.txt | xargs findPrimeFactorization |
从密码学咱们知道,将一个数字因式分解成质数是很慢的.假设咱们分解了100万个数字,程序挂掉了.
当下次重启程序的时候若是可以从上次离开位置的那个点继续处理,而不是重复不少工做,那就很友好了.
以这个例子中,我指望的是从numbers.txt中的第一百万零一行开始继续处理.
Kafka有相似的概念叫作”offset”.Kafka中的每条记录都被分配了有序的offset,消费者能够选择在指定的offset从新开始.
数据持久化和offsets这两个特性,容许你构建一个消费者数据和生产者数据分开的系统.
数据持久化–很是快的数据持久化–意味着它能很快地吸取大批量的数据.
它容许消费者按照它可以读取的任何速度读取数据.它容许持久化数据, 即便消费者挂掉了.
offsets容许消费者继续执行, 不管它上次在什么地方退出,而不会重复工做.
在某种状况下,这是颇有意义的: 你并不想在一次汇款中从银行帐号中扣了两次钱.
另外一方面,这是出于效率方面考虑的: 你并不想从新对已经处理的数字从新进行因式分解.
不管哪一种状况, 这两个特性都容许你作传统的Unix管道所不能作的事情.
再看下第一个例子:
1 |
$ cat *.txt | tr A-Z a-z | grep hello |
在这里例子中,第一个阶段(cat)输出全部的行而后就结束了. 整个管道会找到全部包含单词”hello”的行最后命令结束.
和下面的命令进行比较:
1 |
$ tail -F *.txt | tr A-Z a-z | grep hello |
这个命令不会结束, 第一个阶段(tail)输出一些行,可是仍然保持着监听更多的数据.
若是你在以后往其中的一个添加了一行,tail命令会输出这个新行, 而后接下来的命令会处理它.
Kafka支持相同的概念.数据写到到Kafka而且被消费者读取能够看作一个流.
若是消费者到达数据的末尾, 它会继续等待即将到来的更多的数据. 当新的数据写入到Kafka,它会很快地被发送到消费者.
我在以前说过数据流进Kafka是很快的, 实际上数据从Kafka流出也是很快的.
一条记录被添加到Kafka后,可以在20ms以内发送给一个正在等待的消费者.
如今咱们知道Kafka除了支持数据持久化,也支持流数据. 咱们复习下以前的例子
1 2 |
$ produceNumbers > numbers.txt $ cat numbers.txt | xargs findPrimeFactorization |
上面的命令看起来向上一种批处理模式,由于produceNumbers最终会结束的.
可是数字是无限的,它永远不会结束, 因此实际上看起来应该是这样的:
1 |
$ produceNumbers |* findPrimeFactorization |
这里我本身造了一个语法: |*
表示这是一个Kafka管道.它可以归档全部东西到磁盘,而且发送流式的更新.
streaming updates流式更新, 数据是流式传入的,下游的方法基于最新的流数据作更新操做. 即对流数据更新操做
这种流式的数据容许你建立一个实时的管道,这里有个例子:
1 |
$ tail -F /var/log/httpd/access_log |* grep index.html |* get_load_time |* make_fancy_graph |
这个管道会查询你的web服务器日志. 它会提取主页的全部pageload,获取出页面加载的时间,建立一个可视化的图,并及时更新.
太棒了,你刚刚建立了一台服务器的监控面板. 若是页面加载时间抖动,你能够在几秒内从图中观察到.
全部的这些Kafka管道(每一个|*
)都会持久化和缓冲数据. 管道中的任何一个阶段均可能出错,并在任什么时候候重启,
而且能够在它们上次离开的地方继续. 它们能够处理的很慢,或者一直牢牢跟着(上一个阶段).
或者若是它们落后的太多,能够被中止,并移到新拥有更快CPU的服务器上,也可以从上一次做业离开的地方继续.
你还能够建立一些其余类型的实时管道:
Kafka同时也支持多个生产者往相同的地方写数据. 想象下前面的场景,但如今从多个服务器上收集web服务器日志.
全部的服务器以漏斗形式的数据流入到Kafka管道. 你只有一个grep的进程在运行, 获取加载时间的进程在运行,
只有一个绘图的进程在运行. 可是它们是基于全部web服务器的输出日志的聚合.恭喜你,如今建立了一个数据中心的监控面板.
这里的好处是你能够从不少的地方收集数据, 可是只在一个中央的地方存储并处理全部这些收集到的数据.
Kafka能够成为你的公司中全部数据的中心收集节点. 将分散在各个服务器上的数据都收集到统一一个节点.
Kafka不只支持多个生产者写到同一个地方,也支持多个消费者从相同的地方读取数据.
再强调一次,Kafka在多个阶段之间可以缓冲数据. 上面的三个管道:find_ip_address
, grep index.html
,
get_login_attempts
–都可以按照本身的步伐(消费速度)从access_log这个Kafka管道中读取数据.
前面两个看起来会至关快,可是第三个可能会慢点.可是不要紧,Kafka会保持这些数据(不会由于其余消费者消费了就删除数据)
这样的好处是一个单一的数据源可能用不一样的方式处理,每种使用方式都和其余方式都是独立的,而且不会相互影响.
假设咱们找到了一种检测黑客的方式. 咱们能够将detect_hackers
实例部署在已有的实例旁(共存),而后一块儿测试.
对于相同的输入,看看他们都有什么不一样的表现(验证咱们的新的检测方式是否达到了预期的效果).
一旦咱们决定选择使用新的方式会更好点,咱们会通知下游的notify_security
做业监听更好的检测方式.
而且新的方式真的很稳定了,咱们能够将老的检测方式移除掉.
看看咱们都作了什么?
这个特性使得Kafka带给咱们的威力很是大.经过将同一份数据分散到多个地方,咱们能够从数据中得到多个分组的能力.
每一个管道的工做都是独立的而且都是以本身的消费速度进行的. 而且让咱们在开发新的功能时可以重用已经存在的数据.
让咱们专一于上面多个管道中的其中一个.
假设geoip(地理位置)数据库是很是慢的. Kafka会在这个阶段以前缓冲全部的数据,因此即便很慢,也不会丢失任何数据.
可是查询geoip会拖慢整个管道的速度. 因此你会部署一个很快速的geoip数据库. 可是这并不能帮你太多, 由于你每次
都是从find_ip_address的输出结果中一条接着一条地查询. 你真正须要的是并行!
Kafka支持在你的Kafka管道中添加子管道(sub-pipes). 你能够将全部以1结束的IP地址发送到第一个子管道,将全部以
2结束的IP地址发送到第二个管道,等等. 如今你的请求可以经过round-robin的方式发送到数据库中. 看起来是这样的:
Kafka管道中的数字0到9表示全部以这个数字结束的IP地址,会被放到相同的管道中(图中每一个geoip_lookup都是一个子管道)
每一个geoip_lookup做业都只会从find_ip_address管道中读取一部分数据,能够容许你以并行的方式查询:一次10个线程.
这种方式应该能知足你在geoip阶段快速地在地球图形上绘点, 这下你满意了吧!
Kafka称全部的这些是partitions
. 它容许你将数据以逻辑的分组方式分到多个通道中,可是每一个函数都是独立的.
一批数据会分散到多个节点, 每一个节点之间都作一样的工做. 可是它们之间不会相互影响的.
仔细看看上面的例子,你会发现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哲学运用到工程师急待解决的大数据量,低延迟,网络之间的问题.
在这篇文章中,我简化了一些事情,如今咱们解释下以前遗留的东西.
EOF.