| 解耦 | 消息系统在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口。这允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。 |
| 冗余(副本) | 有些情况下,处理数据的过程会失败。除非数据被持久化,否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。 |
| 扩展性 | 因为消息队列解耦了处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。不需要改变代码、不需要调节参数。 |
| 灵活性&峰值处理能力 | 在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见,如果为能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。 |
| 可恢复性 | 系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。 |
| 顺序保证 | 在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。Kafka保证一个Partition内的消息的有序性。 |
| 缓冲 | 在任何重要的系统中,都会有需要不同的处理时间的元素。例如,加载一张图片比应用过滤器花费更少的时间。消息队列通过一个缓冲层来帮助任务最高效率的执行———写入队列的处理会尽可能的快速。该缓冲有助于控制和优化数据流经过系统的速度。 |
| 异步通信 | 很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。 |
Kafka基本概念
Kafka的相关术语以及之间的关系
上图中一个topic配置了3个partition。Partition1有两个offset:0和1。Partition2有4个offset。Partition3有1个offset。副本的id和副本所在的机器的id恰好相同。
如果一个topic的副本数为3,那么Kafka将在集群中为每个partition创建3个相同的副本。集群中的每个broker存储一个或多个partition。多个producer和consumer可同时生产和消费数据。
| 术语 | 说明 |
| broker | Kafka 集群包含一个或多个服务器,每个服务器节点称为broker。 |
| broker存储topic的数据。如果某topic有N个partition,集群有N个broker,那么每个broker存储该topic的一个partition。 | |
| 如果某topic有N个partition,集群有(N+M)个broker,那么其中有N个broker存储该topic的一个partition,剩下的M个broker不存储该topic的partition数据。 | |
| 如果某topic有N个partition,集群中broker数目少于N个,那么一个broker存储该topic的一个或多个partition。在实际生产环境中,尽量避免这种情况的发生,这种情况容易导致Kafka集群数据不均衡。 | |
| Topic | 每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。(物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个broker上但用户只需指定消息的Topic即可生产或消费数据而不必关心数据存于何处)。 |
| 类似于数据库的表名。 | |
| Partition | topic中的数据分割为一个或多个partition。每个topic至少有一个partition。每个partition中的数据使用多个segment文件存储。partition中的数据是有序的,不同partition间的数据丢失了数据的顺序。如果topic有多个partition,消费数据时就不能保证数据的顺序。在需要严格保证消息的消费顺序的场景下,需要将partition数目设为1。 |
| Producer | 生产者即数据的发布者,该角色将消息发布到Kafka的topic中。broker接收到生产者发送的消息后,broker将该消息追加到当前用于追加数据的segment文件中。生产者发送的消息,存储到一个partition中,生产者也可以指定数据存储的partition。 |
| Consumer | 消费者可以从broker中读取数据。消费者可以消费多个topic中的数据。 |
| Consumer Group | 每个Consumer属于一个特定的Consumer Group(可为每个Consumer指定group name,若不指定group name则属于默认的group)。 |
| Leader | 每个partition有多个副本,其中有且仅有一个作为Leader,Leader是当前负责数据的读写的partition。 |
| Follower | Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出一个新的Leader。当Follower与Leader挂掉、卡住或者同步太慢,leader会把这个follower从“in sync replicas”(ISR)列表中删除,重新创建一个Follower。 |
一个典型的Kafka集群中包含若干Producer,若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干Consumer Group,以及一个Zookeeper集群。Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息。
Topics和PartitionTopic在逻辑上可以被认为是一个queue,每条消费都必须指定它的Topic,可以简单理解为必须指明把这条消息放进哪个queue里。为了使得Kafka的吞吐率可以线性提高,物理上把Topic分成一个或多个Partition,每个Partition在物理上对应一个文件夹,该文件夹下存储这个Partition的所有消息和索引文件。创建一个topic时,同时可以指定分区数目,分区数越多,其吞吐量也越大,但是需要的资源也越多,同时也会导致更高的不可用性,kafka在接收到生产者发送的消息之后,会根据均衡策略将消息存储到不同的分区中。因为每条消息都被append到该Partition中,属于顺序写磁盘,因此效率非常高(经验证,顺序写磁盘效率比随机写内存还要高,这是Kafka高吞吐率的一个很重要的保证)。
对于传统的message queue而言,一般会删除已经被消费的消息,而Kafka集群会保留所有的消息,无论其被消费与否。当然,因为磁盘限制,不可能永久保留所有数据(实际上也没必要),因此Kafka提供两种策略删除旧数据。一是基于时间,二是基于Partition文件大小。
Kafka以主题为单位进行归类。主题为逻辑上的概念。
Partition也可以理解为逻辑上的概念。
| 一个分区只能属于单个主题,一个主题下可以有多个分区,分区里有不同的消息,类似于一个追加的日志文件。 |
| 主题与分区一对多。 |
| 分区的目的:分散磁盘IO。 |
Producer发送消息到broker时,会根据Paritition机制选择将其存储到哪一个Partition。如果Partition机制设置合理,所有消息可以均匀分布到不同的Partition里,这样就实现了负载均衡。如果一个Topic对应一个文件,那这个文件所在的机器I/O将会成为这个Topic的性能瓶颈,而有了Partition后,不同的消息可以并行写入不同broker的不同Partition里,极大的提高了吞吐率。
消费者与消费者组 Consumer Group:
同一Partion的一条消息只能被同一个Consumer Group内的一个Consumer消费,但多个Consumer Group可同时消费这一消息。
消息中间件模型:点对点(P2P,Point-to-Point)模式和发布/订阅(Pub/Sub)模式。
| 点对点是基于队列的,消息生产者发送消息到消息队列,消费者从队列中接收消息。 |
| 发布订阅模式定义了如何向一个内容节点发布和订阅消息,这个内容节点称为主题(Topic),主题可以认为是消息传递的中介,消息发布者将消息发布到某个主题,而消息订阅者从主题中订阅消息。 |
| Kafka同时支持两种消息投递模式,而这得益于消费者与消费者组模型的契合。 |
| 消费者都隶属于同一个消费组,相当于点对点模型。 |
| 消费者都隶属于不同的消费者组,相当于发布/定于模式应用。 |
Kafka的设计理念之一就是同时提供离线处理和实时处理。根据这一特性,可以使用Storm这种实时流处理系统对消息进行实时在线处理,同时使用Hadoop这种批处理系统进行离线处理,还可以同时将数据实时备份到另一个数据中心,只需要保证这三个操作所使用的Consumer属于不同的Consumer Group即可。
| KafkaProducer是线程安全的。 |
| KafkaConsumer不是线程安全的。 |
| 可重试异常 | 网络抖动(NetworkException)如果重试达到重试次数后仍然异常,仍会抛出异常. |
| 不可重试异常 | 如RecordTypeNotSupportedException |
拦截器(interceptor):kafka对应着有生产者和消费者两种拦截器。
| 生产者实现接口 | org.apache.kafka.clients.producer.ProducerInterceptor |
| 消费者实现接口 | org.apache.kafka.clients.consumer.ConsumerInterceptor |
| 拦截器添加 | 在properties属性中添加,可以配置多个。 |
序列化和反序列化
生产者需要用序列化器(Serializer)把对象转换成字节数组才能通过网络发送给Kafka ;而在对侧,消费者需要用反序列化器(Deserializer)把从Kafka中收到的字节数组转换成相应的对象。
| 序列化接口 | org.apache.kafka.common.serialization.Serializer |
| 反序列化接口 | org.apache.kafka.common.serialization.Deserializer |
提交方式:commitSync & commitAsync
同步提交:整体提交 & 分区提交
对于consumer消息的订阅subscribe方法:可以订阅一个或多个topic,也可以支持正则表达式的订阅方式。
assign:可以订阅某一个主题下的某一个或者多个partition。
Kafka消费者再均衡:
Kafka消费者多线程:
KafkaProducer是线程安全的,但是KafkaConsumer却是线程非安全的。
KafkaConsumer中定义了一个acquire的方法用来检测是否只有一个线程在操作,如果有其他线程操作则会抛出ConcurrentMidifactionException。
KafkaConsumer在执行所有动作时都会先执行acquire方法检测是否线程安全。
Kafka多线程模型:
MDC



