Kafka是为大数据而生的消息中间件,在数据采集、传输、存储的过程中发挥着举足轻重的作用。
优点:
缺点:
消息队列优点:低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,成为异步RPC的主要手段之一。
分析设计一个消息队列时需要考虑到的问题,如RPC、高可用、顺序和重复消息、可靠投递、消费关系解析等。 当你需要使用消息队列时,首先需要考虑它的必要性。可以使用mq的场景有很多,最常用的几种,是做业务解耦/最终一致性/广播/错峰流控等。反之,如果需要强一致性,关注业务逻辑的处理结果,则RPC显得更为合适。
Kafka是最初由Linkedin公司开发,是一个分布式、分区的、多副本的、多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),
常见可以用于web/nginx日志、访问日志,消息服务等等,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。
Kakfa具有高吞吐、低延迟等特点,在大数据、日志收集等应用场景下被广泛使用。
Kafka部分名词解释如下:
体系结构图:
Topic
一个topic是对一组消息的归纳,一个topic可以认为是一类消息。对每个topic,它被分成多个partition,,每个partition在存储层面就是append log文件。也就是说Kafka通过partition对topic做了【日志分区】。
任何发布到此partition的消息都会被追加到log文件的尾部,每条消息在文件中的位置称为offset(偏移量),offset为一个long型的数字,它唯一标记分区中的一条消息。每条消息都被append到partition中,是顺序写磁盘,因此效率非常高(经验证,顺序写磁盘效率比随机写内存还要高,这是Kafka高吞吐率的一个很重要的保证)。连续追加到partition中的消息是有序的、不可变的。在一个可配置的时间段内,Kafka集群保留所有发布的消息,不管这些消息有没有被消费。比如,如果消息的保存策略被设置为2天,那么在一个消息被发布的两天时间内,它都是可以被消费的。之后它将被丢弃以释放空间。Kafka的性能是和数据量无关的常量级的,所以保留太多的数据并不是问题。
LOG consumer offset
每个consumer唯一需要维护的数据是消息在【日志】中的位置,也就是offset。
这个offset由consumer来维护:一般情况下随着consumer不断的读取消息,offset的值会不断增加,其实consumer可以以任意的顺序读取消息,
比如它可以将offset设置成为一个旧的值来重读之前的消息。这使得consumer非常的轻量级:它们可以在不对集群和其他consumer造成影响的情况下读取消息。
使用命令行来'tail'消息而不会对其他正在消费消息的consumer造成影响。 将日志分区可以达到以下目的:首先这使得每个日志的数量不会太大,可以在单个服务上保存。另外每个分区可以单独发布和消费,为并发操作topic提供了一种可能。
分布式
每个分区在Kafka集群的若干服务中都有副本,这样这些持有副本的服务可以共同处理数据和请求,副本数量是可以配置的。副本使Kafka具备了容错能力。
每个分区都由一个服务器作为"leader",零或若干服务器作为"followers",leader负责处理消息的读和写,followers则去复制leader。
如果leader 宕机了,followers中的一台则会自动成为leader。集群中的每个服务节点都会【同时】扮演两个角色:作为它所持有的一部分分区的leader,同时作为其他分区的followers,这样集群就会据有较好的负载均衡。
Producer
Producer将消息发布到它指定的topic中,并负责决定发布到哪个分区。通常简单的由负载均衡机制随机选择分区,但也可以通过特定的分区函数选择分区。一般使用的更多的是第二种。 每一条消息被发送到broker中,会根据partition规则选择被存储到哪一个partition。如果partition规则设置的合理,所有消息可以均匀分布到不同的partition里,这样就实现了水平扩展。(如果一个topic对应一个文件,那这个文件所在的机器I/O将会成为这个topic的性能瓶颈,而partition解决了这个问题)。在创建topic时可以'$KAFKA_HOME/config/server.properties'中指定这个partition的数量,当然可以在topic创建之后去修改partition的数量。 在发送一条消息时,可以指定这个消息的key,producer根据这个key和partition机制来判断这个消息发送到哪个partition。partition机制可以通过指定producer的partition.class这一参数来指定,该class必须实现'kafka.producer.Partitioner'接口。
Consumer
发布消息通常有两种模式:队列模式(queuing)和发布/订阅模式(publish-subscribe)。 1)队列模式:consumers可以同时从服务端读取消息,每个消息只被其中一个consumer读到; 2)发布/订阅模式:消息被广播到所有的consumer中,每个consumer都能收到。 Consumers可以加入一个consumer组,在同一个组内共同竞争一个topic,topic中的消息将被分发到组中的一个成员中。同一组中的consumer可以在不同的程序中,也可以在不同的机器上。如果所有的consumer都在一个组中,都抢一个topic,这就成为了传统的队列模式,只有一个会收到消息,在各consumer中实现负载均衡。如果所有的consumer都在不同的组中,这就成为了发布/订阅模式,消息会被分发到所有的consumer中。更常见的是,每个topic都有若干数量的consumer组,每个组都是一个逻辑上的"订阅者",为了容错和更好的稳定性,每个组由若干consumer组成。这其实就是一个发布/订阅模式,只不过订阅者是个组而不是单个consumer。
有序性
相比传统的消息系统,Kafka可以很好的保证有序性。传统的队列在服务器上保存有序的消息,如果多个consumers同时从这个服务器消费消息,服务器就会以消息存储的顺序向consumer分发消息。虽然服务器按顺序发布消息,但是消息是被异步的分发到各consumer上,所以当消息到达时可能已经失去了原来的顺序,这意味着并发消费将导致顺序错乱。为了避免故障,这样的消息系统通常使用"专用consumer"的概念,其实就是只允许一个消费者消费消息,当然这就意味着失去了并发性。
Kafka通过分区的概念,可以在多个consumer组并发的情况下提供较好的有序性和负载均衡。将每个分区分只分发给一个consumer组,这样一个分区就只被这个组的一个consumer消费,就可以顺序的消费这个分区的消息。因为有多个分区,依然可以在多个consumer组之间进行负载均衡。注意consumer组中consumer的数量不能多于分区的数量,也就是有多少分区就允许多少并发消费。 Kafka只能保证一个分区之内消息的有序性,在不同的分区之间是不可以的,这已经可以满足大部分应用的需求。如果需要topic中所有消息的有序性,那就只能让这个topic只有一个分区,当然也就只有一个consumer组消费它。