kafka简介
kafka是一种分布式的,基于发布/订阅的消息系统。主要设计目标如下:
以时间复杂度为o(1)的方式提供消息持久化能力,即使对tb级以上数据也能保证常数时间的访问性能
高吞吐率。即使在非常廉价的商用机器上也能做到单机支持每秒100k条消息的传输
支持kafka server间的消息分区,及分布式消费,同时保证每个partition内的消息顺序传输
同时支持离线数据处理和实时数据处理
为什么要用消息系统
解耦
在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。消息队列在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口。这允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束
冗余
有些情况下,处理数据的过程会失败。除非数据被持久化,否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。在被许多消息队列所采用的”插入-获取-删除”范式中,在把一个消息从队列中删除之前,需要你的处理过程明确的指出该消息已经被处理完毕,确保你的数据被安全的保存直到你使用完毕。
扩展性
因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的;只要另外增加处理过程即可。不需要改变代码、不需要调节参数。扩展就像调大电力按钮一样简单。
灵活性 & 峰值处理能力
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见;如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
可恢复性
当体系的一部分组件失效,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。而这种允许重试或者延后处理请求的能力通常是造就一个略感不便的用户和一个沮丧透顶的用户之间的区别。
送达保证
消息队列提供的冗余机制保证了消息能被实际的处理,只要一个进程读取了该队列即可。在此基础上,ironmq提供了一个”只送达一次”保证。无论有多少进程在从队列中领取数据,每一个消息只能被处理一次。这之所以成为可能,是因为获取一个消息只是”预定”了这个消息,暂时把它移出了队列。除非客户端明确的表示已经处理完了这个消息,否则这个消息会被放回队列中去,在一段可配置的时间之后可再次被处理。
顺序保证
在大多使用场景下,数据处理的顺序都很重要。消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。ironmo保证消息通过fifo(先进先出)的顺序来处理,因此消息在队列中的位置就是从队列中检索他们的位置。
缓冲
在任何重要的系统中,都会有需要不同的处理时间的元素。例如,加载一张图片比应用过滤器花费更少的时间。消息队列通过一个缓冲层来帮助任务最高效率的执行—写入队列的处理会尽可能的快速,而不受从队列读的预备处理的约束。该缓冲有助于控制和优化数据流经过系统的速度。
理解数据流
在一个分布式系统里,要得到一个关于用户操作会用多长时间及其原因的总体印象,是个巨大的挑战。消息队列通过消息被处理的频率,来方便的辅助确定那些表现不佳的处理过程或领域,这些地方的数据流都不够优化。
异步通信
很多时候,你不想也不需要立即处理消息。消息队列提供了异步处理机制,允许你把一个消息放入队列,但并不立即处理它。你想向队列中放入多少消息就放多少,然后在你乐意的时候再去处理它们。
常用message queue对比
rabbitmq
rabbitmq是使用erlang编写的一个开源的消息队列,本身支持很多的协议:amqp,xmpp, smtp, stomp,也正因如此,它非常重量级,更适合于企业级的开发。同时实现了broker构架,这意味着消息在发送给客户端时先在中心队列排队。对路由,负载均衡或者数据持久化都有很好的支持。
redis
redis是一个基于key-value对的nosql数据库,开发维护很活跃。虽然它是一个key-value数据库存储系统,但它本身支持mq功能,所以完全可以当做一个轻量级的队列服务来使用。对于rabbitmq和redis的入队和出队操作,各执行100万次,每10万次记录一次执行时间。测试数据分为128bytes、512bytes、1k和10k四个不同大小的数据。实验表明:入队时,当数据比较小时redis的性能要高于rabbitmq,而如果数据大小超过了10k,redis则慢的无法忍受;出队时,无论数据大小,redis都表现出非常好的性能,而rabbitmq的出队性能则远低于redis。
zeromq
zeromq号称最快的消息队列系统,尤其针对大吞吐量的需求场景。zmq能够实现rabbitmq不擅长的高级/复杂的队列,但是开发人员需要自己组合多种技术框架,技术上的复杂度是对这mq能够应用成功的挑战。zeromq具有一个独特的非中间件的模式,你不需要安装和运行一个消息服务器或中间件,因为你的应用程序将扮演了这个服务角色。你只需要简单的引用zeromq程序库,可以使用nuget安装,然后你就可以愉快的在应用程序之间发送消息了。但是zeromq仅提供非持久性的队列,也就是说如果宕机,数据将会丢失。其中,twitter的storm 0.9.0以前的版本中默认使用zeromq作为数据流的传输(storm从0.9版本开始同时支持zeromq和netty作为传输模块)。
activemq
activemq是apache下的一个子项目。 类似于zeromq,它能够以代理人和点对点的技术实现队列。同时类似于rabbitmq,它少量代码就可以高效地实现高级应用场景。
kafka/jafka
kafka是apache下的一个子项目,是一个高性能跨语言分布式发布/订阅消息队列系统,而jafka是在kafka之上孵化而来的,即kafka的一个升级版。具有以下特性:快速持久化,可以在o(1)的系统开销下进行消息持久化;高吞吐,在一台普通的服务器上既可以达到10w/s的吞吐速率;完全的分布式系统,broker、producer、consumer都原生自动支持分布式,自动实现负载均衡;支持hadoop数据并行加载,对于像hadoop的一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。kafka通过hadoop的并行加载机制来统一了在线和离线的消息处理。apache kafka相对于activemq是一个非常轻量级的消息系统,除了性能非常好之外,还是一个工作良好的分布式系统。
kafka解析
terminology
broker
kafka集群包含一个或多个服务器,这种服务器被称为broker
topic
每条发布到kafka集群的消息都有一个类别,这个类别被称为topic。(物理上不同topic的消息分开存储,逻辑上一个topic的消息虽然保存于一个或多个broker上但用户只需指定消息的topic即可生产或消费数据而不必关心数据存于何处)
partition
parition是物理上的概念,每个topic包含一个或多个partition,创建topic时可指定parition数量。每个partition对应于一个文件夹,该文件夹下存储该partition的数据和索引文件
producer
负责发布消息到kafka broker
consumer
消费消息。每个consumer属于一个特定的consumer group(可为每个consumer指定group name,若不指定group name则属于默认的group)。使用consumer high level api时,同一topic的一条消息只能被同一个consumer group内的一个consumer消费,但多个consumer group可同时消费这一消息。
kafka架构
如上图所示,一个典型的kafka集群中包含若干producer(可以是web前端产生的page view,或者是服务器日志,系统cpu、memory等),若干broker(kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干consumer group,以及一个zookeeper集群。kafka通过zookeeper管理集群配置,选举leader,以及在consumer group发生变化时进行rebalance。producer使用push模式将消息发布到broker,consumer使用pull模式从broker订阅并消费消息。
push vs. pull
作为一个messaging system,kafka遵循了传统的方式,选择由producer向broker push消息并由consumer从broker pull消息。一些logging-centric system,比如facebook的scribe和cloudera的flume,采用非常不同的push模式。事实上,push模式和pull模式各有优劣。
push模式很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的。push模式的目标是尽可能以最快速度传递消息,但是这样很容易造成consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而pull模式则可以根据consumer的消费能力以适当的速率消费消息。
topic & partition
topic在逻辑上可以被认为是一个queue。每条消费都必须指定它的topic,可以简单理解为必须指明把这条消息放进哪个queue里。为了使得kafka的吞吐率可以水平扩展,物理上把topic分成一个或多个partition,每个partition在物理上对应一个文件夹,该文件夹下存储这个partition的所有消息和索引文件。
每个日志文件都是“log entries”序列,每一个log entry包含一个4字节整型数(值为n),其后跟n个字节的消息体。每条消息都有一个当前partition下唯一的64字节的offset,它指明了这条消息的起始位置。磁盘上存储的消息格式如下:
message length : 4 bytes (value: 1+4+n)
“magic” value : 1 byte
crc : 4 bytes
payload : n bytes
这个“log entries”并非由一个文件构成,而是分成多个segment,每个segment名为该segment第一条消息的offset和“.kafka”组成。另外会有一个索引文件,它标明了每个segment下包含的log entry的offset范围,如下图所示。
因为每条消息都被append到该partition中,是顺序写磁盘,因此效率非常高(经验证,顺序写磁盘效率比随机写内存还要高,这是kafka高吞吐率的一个很重要的保证)。
每一条消息被发送到broker时,会根据paritition规则选择被存储到哪一个partition。如果partition规则设置的合理,所有消息可以均匀分布到不同的partition里,这样就实现了水平扩展。(如果一个topic对应一个文件,那这个文件所在的机器i/o将会成为这个topic的性能瓶颈,而partition解决了这个问题)。在创建topic时可以在$kafka_home/config/server.properties中指定这个partition的数量(如下所示),当然也可以在topic创建之后去修改parition数量。
# the default number of log partitions per topic. more partitions allow greater
# parallelism for consumption, but this will also result in more files across
# the brokers.
num.partitions=3
在发送一条消息时,可以指定这条消息的key,producer根据这个key和partition机制来判断将这条消息发送到哪个parition。paritition机制可以通过指定producer的paritition. class这一参数来指定,该class必须实现kafka.producer.partitioner接口。本例中如果key可以被解析为整数则将对应的整数与partition总数取余,该消息会被发送到该数对应的partition。(每个parition都会有个序号)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
importkafka.producer.partitioner;
importkafka.utils.verifiableproperties;
publicclassjasonpartitionerimplementspartitioner {
publicjasonpartitioner(verifiableproperties verifiableproperties) {}
@override
publicintpartition(object key,intnumpartitions) {
try{
intpartitionnum = integer.parseint((string) key);
returnmath.abs(integer.parseint((string) key) % numpartitions);
}catch(exception e) {
returnmath.abs(key.hashcode() % numpartitions);
}
}
}
并通过如下代码发送20条消息(key分别为0,1,2,3)至topic2(包含4个partition)。
1
2
3
4
5
6
7
8
9
10
publicvoidsendmessage()throwsinterruptedexception{
for(inti =1; i <=5; i++){
list messagelist =newarraylist();
for(intj =0; j <4; j++){
messagelist.add(newkeyedmessage(topic2, j+,the + i + message for key + j));
}
producer.send(messagelist);
}
producer.close();
}{this broker id},如果创建成功(只可能有一个创建成功),则该broker会成为controller,若创建不成功,则该broker会等待新controller的命令。
consumer group
(本节所有描述都是基于consumer hight level api而非low level api)。
每一个consumer实例都属于一个consumer group,每一条消息只会被同一个consumer group里的一个consumer实例消费。(不同consumer group可以同时消费同一条消息)
很多传统的message queue都会在消息被消费完后将消息删除,一方面避免重复消费,另一方面可以保证queue的长度比较少,提高效率。而如上文所将,kafka并不删除已消费的消息,为了实现传统message queue消息只被消费一次的语义,kafka保证保证同一个consumer group里只有一个consumer会消费一条消息。与传统message queue不同的是,kafka还允许不同consumer group同时消费同一条消息,这一特性可以为消息的多元化处理提供了支持。实际上,kafka的设计理念之一就是同时提供离线处理和实时处理。根据这一特性,可以使用storm这种实时流处理系统对消息进行实时在线处理,同时使用hadoop这种批处理系统进行离线处理,还可以同时将数据实时备份到另一个数据中心,只需要保证这三个操作所使用的consumer在不同的consumer group即可。下图展示了kafka在linkedin的一种简化部署。
为了更清晰展示kafka consumer group的特性,笔者作了一项测试。创建一个topic (名为topic1),创建一个属于group1的consumer实例,并创建三个属于group2的consumer实例,然后通过producer向topic1发送key分别为1,2,3r的消息。结果发现属于group1的consumer收到了所有的这三条消息,同时group2中的3个consumer分别收到了key为1,2,3的消息。如下图所示。
consumer rebalance
(本节所讲述内容均基于kafka consumer high level api)
kafka保证同一consumer group中只有一个consumer会消费某条消息,实际上,kafka保证的是稳定状态下每一个consumer实例只会消费某一个或多个特定partition的数据,而某个partition的数据只会被某一个特定的consumer实例所消费。这样设计的劣势是无法让同一个consumer group里的consumer均匀消费数据,优势是每个consumer不用都跟大量的broker通信,减少通信开销,同时也降低了分配难度,实现也更简单。另外,因为同一个partition里的数据是有序的,这种设计可以保证每个partition里的数据也是有序被消费。
如果某consumer group中consumer数量少于partition数量,则至少有一个consumer会消费多个partition的数据,如果consumer的数量与partition数量相同,则正好一个consumer消费一个partition的数据,而如果consumer的数量多于partition的数量时,会有部分consumer无法消费该topic下任何一条消息。
如下例所示,如果topic1有0,1,2共三个partition,当group1只有一个consumer(名为consumer1)时,该 consumer可消费这3个partition的所有数据。
增加一个consumer(consumer2)后,其中一个consumer(consumer1)可消费2个partition的数据,另外一个consumer(consumer2)可消费另外一个partition的数据。
再增加一个consumer(consumer3)后,每个consumer可消费一个partition的数据。consumer1消费partition0,consumer2消费partition1,consumer3消费partition2
再增加一个consumer(consumer4)后,其中3个consumer可分别消费一个partition的数据,另外一个consumer(consumer4)不能消费topic1任何数据。
新增!工信部5G/6G频率
华为手机如何换鸿蒙
2023年深圳国际连接器线束及加工设备展会
工字电感厂家科普插件工字功率电感如何识别质量优劣
天猫精灵CCH智能音箱更注重的是关爱和陪伴
kafka设计原理的深度探讨
XP Power推出稳压输出、自然对流冷却的AC-DC电源LCS系列
钛深科技获数千万元Pre-A轮融资,因为这个黑科技?
OpenHarmony高校技术俱乐部分论坛召开,共话OpenHarmony高校生态发展
广和通基于联发科技 T830平台的5G模组FG370率先通过CE认证测试
基于CCP协议利用CANape进行电控单元标定
用于音频放大器的多路输出反激式电源
喷iPhone夸安卓是工作,用iPhone才是生活
如何延长USB线缆长度
如何利用ESP8266制作一个简单的四轴飞行器
打造常态化智慧旅游全民健身新时代和文化旅游发展新业态将成为潮流
嫦娥五号任务计划实现三大工程目标
农业小型自动气象站
极致转写体验,讯飞智能办公本X2值得拥有
双网传真机的编译码电路设计