kafka入门到跑路

kafka组件模型

1.kafka和rocketmq的区别

特性

RocketMQ

Kafka

1.单机吞吐量/性能差异

10 万级 

百万级

2.开发语言

Java

Scala和Java

3.客户端支持语言

Java及C++,其中C++还不成熟

Java、.Net、PHP、Ruby、Python、Go

4.持久化

内存、文件

文件

5.topic 数量对吞吐量的影响

topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic

topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源

6.消息可靠性

经过参数优化配置,可以做到 0 丢失

同 RocketMQ

7.功能支持

MQ 功能较为完善,还是分布式的,扩展性好

功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用

8.延迟消息

支持

不支持

9.死信队列

10.重试机制

生产者消费者都可以重试

仅生产者可重试

11.刷盘方式

1.1 性能差异分析

1.1.1 文件布局

Kafka 文件布局

文件的组织以 topic + 分区进行组织,每一个 topic 可以创建多个分区,每一个分区包含单独的文件夹,并且是多副本机制。即 topic 的每一个分区会有 Leader 与 Follow,并且 Kafka 内部有机制保证 topic 的某一个分区的 Leader 与 follow 不会存在在同一台机器,并且每一台 broker 会尽量均衡的承担各个分区的 Leader,当然在运行过程中如果不均衡,可以执行命令进行手动重平衡。Leader 节点承担一个分区的读写,follow 节点只负责数据备份。

Kafka 的负载均衡

主要依靠分区 Leader 节点的分布情况

分区的 Leader 节点负责读写,而从节点负责数据同步,如果Leader分区所在的Broker节点发生宕机,会触发主从节点的切换,会在剩下的 follow 节点中选举一个新的 Leader 节点,其数据的流入流程如下图所示:

分区 Leader 收到客户端的消息发送请求时,是写入到 Leader 节点后就返回还是要等到它的从节点全部写入后再返回,这里非常关键,会直接影响消息发送端的时延,故 Kafka 提供了 ack 这个参数来进行策略选择:

ack = 0不等broker端确认就直接返回,即客户端将消息发送到网络中就返回发送成功。

ack = 1Leader 节点接受并存储后向客户端返回成功。

ack = -1Leader节点和所有的Follow节点接受并成功存储再向客户端返回成功。

RocketMQ 文件布局

RocketMQ 所有主题的消息都会写入到 commitlog 文件中,然后基于 commitlog 文件构建消息消费队列文件(Consumequeue)消息消费队列的组织结构按照 /topic/{queue} 进行组织。从集群的视角来看如下图所示:

文件布局对比

Kafka 中文件的布局是以 Topic/partition ,每一个分区一个物理文件夹,在分区文件级别实现文件顺序写,如果一个Kafka集群中拥有成百上千个主题,每一个主题拥有上百个分区,消息在高并发写入时,其IO操作就会显得零散,其操作相当于随机IO,即 Kafka 在消息写入时的IO性能会随着 topic 、分区数量的增长,其写入性能会先上升,然后下降。

 RocketMQ在消息写入时追求极致的顺序写,所有的消息不分主题一律顺序写入 commitlog 文件,并不会随着 topic 和 分区数量的增加而影响其顺序性。一个文件无法充分利用磁盘IO的性能。

两者文件组织方式,除了在磁盘的顺序写方面有所区别后,由于其粒度的问题,Kafka 的 topic 扩容分区会涉及分区在各个 Broker 的移动,其扩容操作比较重,而 RocketMQ 数据存储是基于 commitlog 文件的,扩容时不会产生数据移动,只会对新的数据产生影响,RocketMQ 的运维成本对 Kafka 更低。

1.1.2 数据写入方式

Kafka 消息写入方式

Kafka 的消息写入使用的是 FileChannel,其代码截图如下:

并且在消息写入时使用了 transferTo 方法,根据网上的资料说 NIO 中网络读写真正是零拷贝的就是需要调用 FileCha nnel 的 transferTo或者 transferFrom 方法,其内部机制是利用了 sendfile 系统调用。

RocketMQ 消息写入方式

RocketMQ 的消息写入支持内存映射FileChannel 写入两种方式, 示例如下图所示:

消息写入方式对比

尽管 RocketMQ 与 Kafka 都支持 FileChannel 方式写入,但 RocketMQ 基于 FileChannel 写入时调用的 API 却并不是 transferTo,而是先调用 writer,然后定时 flush 刷写到磁盘,其代码截图如下:

个人观点:

sendfile 系统调用相比内存映射多了一次从用户缓存区拷贝到内核缓存区,但对于超过64K的内存写入时往往 sendfile 的性能更高,可能是由于 sendfile 是基于块内存的。

1.1.3 消息发送对比

Kafka 在消息发送客户端采用了一个双端队列,引入了批处理思想。其消息发送机制如下图所示:

客户端通过调用 kafka 的消息发送者发送消息时,消息会首先存入到一个双端队列中,双端队列中单个元素为 ProducerBatch,表示一个发送批次,其最大大小受参数 batch.size 控制,默认为 16K。然后会单独开一个 Send 线程,从双端队列中获取一个发送批次,将消息按批发送到 Kafka集群中,这里引入了 linger.ms 参数来控制 Send 线程的发送行为。

为了提高 kafka 消息发送的高吞吐量,即控制在缓存区中未积满 batch.size 时来控制消息发送线程的行为,是立即发送还是等待一定时间,如果linger.ms 设置为 0表示立即发送,如果设置为大于0,则消息发送线程会等待这个值后才会向broker发送。linger.ms 参数者会增加响应时间,但有利于增加吞吐量。有点类似于 TCP 领域的 Nagle 算法。

Kafka 的消息发送,在写入 ProducerBatch 时会按照消息存储协议组织好数据,在服务端可以直接写入到文件中。

RocketMQ 消息发送机制

RocketMQ 消息发送在客户端主要是根据路由选择算法选择一个队列,然后将消息发送到服务端,消息会在服务端按照消息的存储格式进行组织,然后进行持久化等操作。

消息发送对比

Kafka 在消息发送方面比 RokcetMQ 有一个显著的优势就是消息格式的组织是发生在客户端,这样会有一个大的优势节约了 Broker 端的CPU压力,客户端“分布式”的承接了其优势,其架构方式有点类似 shardingjdbc 与 MyCat 的区别。

Kafka 在消息发送端另外一个特点是引入了双端缓存队列,Kafka 无处不在追求批处理,这样显著的特点是能提高消息发送的吞吐量,但与之带来的是增大消息的响应时间,并且带来了消息丢失的可能性,因为 Kafka 追加到消息缓存后会返回成功,如果消息发送方异常退出,会带来消息丢失。

Kafka 中的 linger.ms = 0 可类比 RocketMQ 消息发送的效果。

但 Kafka 通过提供 batch.size 与 linger.ms 两个参数按照场景进行定制化,比 RocketMQ灵活。

1.1.8.延迟消息

1.1.10 消息重试

概述:Producer对发送失败的消息进行重新发送的机制,称为消息发送重试机制,也称为消息重投机制

kafka重试策略

生产者重试

kafaka到底有没有重试机制?

KafkaProducer通过设定参数retries

,如果发送消息到broker时抛出异常,且是允许重试的异常,那么就会最大重试retries参数指定的次数。

哪些异常可以重试

  1. 异常是RetriableException类型或者TransactionManager允许重试;

transactionManager.canRetry()后面会分析;先看看哪些异常是RetriableException类型异常。

  • RetriableException类型异常

kafka对RetriableException异常注释是:短暂性的通过重试可以成功的异常;通过RetriableException类关系图可知,可重试异常有图中RetriableException的子类那些异常(可以通过异常是否继承自RetriableException判断是否可重试异常)

消费者重试

1.kafka批量消费消息,使用containerFactory 监听消费失败消息 

RoketMq 重复策略

消息发送重试有三种策略可以选择:同步发送失败策略、异步发送失败策略和消息刷盘失败策略

同步失败策略

DefaultMQProducer producer = new DefaultMQProducer("pg");

producer.setNamesrvAddr("rocketmqOS:9876");

// 设置同步发送失败时重试发送的次数,默认为2次

producer.setRetryTimesWhenSendFailed(3);

// 设置发送超时时限为5s,默认3s

producer.setSendMsgTimeout(5000);

异步失败策略

DefaultMQProducer producer = new DefaultMQProducer("pg");

producer.setNamesrvAddr("rocketmqOS:9876");

// 指定异步发送失败后不进行重试发送

producer.setRetryTimesWhenSendAsyncFailed(0);

消息刷盘失败策略

消息刷盘超时(Master、Slave),默认是不会将消息尝试发送到其他Broker。对于重要消息可以通过在Broker的配置文件设置retryAnotherBrokerWhenNotStoreOK属性为true来开启。

生产者消息重试

有时因为网路等原因生产者也可能发送消息失败,也会进行消息重试,生产者消息重试比较简单,在springboot中只要在配置文件中配置一下就可以了。

# 异步消息发送失败重试次数,默认为2
rocketmq.producer.retry-times-when-send-async-failed=2
# 消息发送失败重试次数,默认为2
rocketmq.producer.retry-times-when-send-failed=2

也可以通过下面这种方式配置

DefaultMQProducer defaultMQProducer = new DefaultMQProducer();
defaultMQProducer.setRetryTimesWhenSendFailed(2);
defaultMQProducer.setRetryTimesWhenSendAsyncFailed(2);

消费者消息重试

消费者消费某条消息失败后,会根据消息重试机制将该消息重新投递,若达到重试次数后消息还没有成功被消费,则消息将被投入死信队列。

一条消息无论重试多少次,这些重试消息的Message ID不会改变。

顺序消息的消费重试

顺序消息,当Consumer消费消息失败后,为了保证消息的顺序性,其会自动不断地进行消息重试,直到消费成功。消费重试默认间隔时间为1000ms。重试期间应用会出现消息消费被阻塞的情况。

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("cg");
// 顺序消息消费失败的消费重试时间间隔,单位毫秒,默认为1000,其取值范围为[10, 30000]
consumer.setSuspendCurrentQueueTimeMillis(100);

由于对顺序消息的重试是无休止的,不间断的,直至消费成功,所以,对于顺序消息的消费,务必要保证应用能够及时监控并处理消费失败的情况,避免消费被永久性阻塞。

注意:顺序消息没有发送失败重试机制,但具有消费失败重试机制

并发消息的消费重试

在并发消费中,可能会有多个线程同时消费一个队列的消息,因此即使发送端通过发送顺序消息保证消息在同一个队列中按照FIFO的顺序,也无法保证消息实际被顺序消费,所有并发消费也可以称之为无序消费。

对于无序消息(普通消息、延时消息、事务消息),当Consumer消费消息失败时,可以通过设置返回状态达到消息重试的效果。

注意:无序消息的重试只针对集群消费模式生效;广播消费模式不提供失败重试特性,即消费失败后,失败消息不再重试,继续消费新的消息。

对于无序消息集群消费下的重试消费,默认允许每条消息最多重试16次,如果消息重试16次后仍然失败,消息将被投递至死信队列。消息重试间隔时间如下:

2.消息准确性(仅介绍kafka相关)

kafka如何做到精准一次性

消息发送幂等特性

引入幂等之前

  在正常情况下,produce向Broker投递消息,broker将消息追加写到对应的流(即某一个topic的某一partition)中,并向Producer返回ACK信号,表示确认收到。

引入幂等性之前

  上图的实现流程是一种理想状态下的消息发送情况,但是实际情况中,会出现各种不确定的因素,比如在Producer在发送给Broker的时候出现网络异常。比如以下这种异常情况的出现: 

上图这种情况,当Producer第一次发送消息给Broker时,Broker将消息(x2,y2)追加到了消息流中,但是在返回Ack信号给Producer时失败了(比如网络异常) 。此时,Producer端触发重试机制,将消息(x2,y2)重新发送给Broker,Broker接收到消息后,再次将该消息追加到消息流中,然后成功返回Ack信号给Producer。这样下来,消息流中就被重复追加了两条相同的(x2,y2)的消息。 

引入幂等之后

kafka 为实现幂等性,在底层引入了ProducerID和SequenceNumber。

ProducerID:在每一个新的Producer初始化时,或被分配一个唯一的ProducerID,这个ProducerID对客户端使用者是不可见的。

SequenceNumber:对于每个producerID,Producer发送数据的每个Topic和Partition都对应一个从0开始递增的SequenceNumber值。

数据发送到kafka中会对数据增加Pid 和SequenceId

幂等的缺陷

单会话有效

        只能实现单会话上的幂等性,不能实现跨会话的幂等性。这里的会话,你可以理解为 Producer 进程的一次运行。当你重启了 Producer 进程之后,这种幂等性保证就丧失了。

        原因:重启之后标识producer的PID就变化了,导致broker无法根据这个<PID,TP,SEQNUM>条件去去判断是否重复。

单分区有效

        只能保证单分区上的幂等性。即一个幂等性 Producer 能够保证某个主题的一个分区上不出现重复消息,它无法实现多个分区的幂等性。

        原因:在某一个partition 上判断是否重复是通过一个递增的sequence number,也就是说这个递增是针对当前特定分区的,如果你要是发送到其他分区上去了,那么递增关系就不存在了。

2.1 消息防重复

2.2 消息丢失问题

3.其他特性对比

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值