Kafka问题记录&总结

如何保证消息有序消费

方案1:

生产者发送消息时指定相同的key发往相同的分区,发送的顺序是有序的,消息队列的数据接口是一个队列,先进先出原则,消费者依次消费,此时即可保证有序消费;

方案2:

单线程有序 ,但效率极低,但貌似作用不大;

方案3:

生产者按相同key发相同分区不变,消费者接收消息对key进行hash取模(防止线程之间快慢不一导致无序),对于相同的key放到同一个队列再去消费;即高效又保证有序
方案3可配合策略模式再来一套组合拳(不同的key不同的处理策略)!

Cunsumer是被动推还是主动拉

所有消息服务消费者获取消息都会有两种形式,一种是消费者被动的接收broker推送过来的消息,另一种是消费者主动到broker上去拿消息。
首先说明Kafka这个消息队列是采用的主动拉取消息的形式;接下来说一说push和pull的优缺点;

**push缺:**消息推送频率由broker控制,下游服务处理消息的速度跟不上推送速度容易把下游consumer搞崩溃了
push优: 消息系统都致力于让 consumer 以最大的速率最快速的消费消息

**pull优:**消息的处理速度由consumer自己控制,可根据自己的消费能力自己配置;不会出现consumer崩溃问题
pull缺: 如果 broker 没有可供消费的消息,将导致 consumer 不断在循环中轮询,直到新消息到达。为了避免这点,Kafka 有个参数可以让 consumer阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发送)。

kafka 维护消费状态跟踪的方法

1、当broker中的一条消息被consumer拉取后立即删除该消息;

优:减少broker的空间占用
缺:consumer拉取消息后消息消费失败了,broker也删除了,导致消息未消费

2、consumer拉取完消息,broker将消息置为已发送,consumer消费成功后通知broker更改消息状态为以消费

优:解决消息丢失问题
缺:1、consumer消费成功,但是没有正常通知broker,会重复消费

3、broker 必须维护每条消息的状态,并且每次都要先锁住消息然后更改状态然后释放锁;如果一条消息长时间没有收到消费成功的通知,就会一致被锁定,且消息量打起来还要维护状态,不太可

解决方案: Topic 被分成了若干分区,每个分区在同一时间只被一个 consumer 消费 ;每个分区消费的就是一个offset(整数),同时该方案还可以消费历史数据。

Zookeeper VS Kafka

zookeeper作用于Kafka的分布式应用,也就是说对单节点的Kafka没啥大用
主要为kafka提供以下功能:
1、 用于提交偏移量 , 如果节点在任何情况下都失败了,它都可以从之前提交的偏移量中获取
2、 leader 检测
3、 分布式同步
4、 配置管理
5、 识别新节点何时离开或连接、集群、节点实时状态

如何判断kafka集群中的某一节点是否还存活

1、心跳机制:kafka的每一个节点都会在zookeeper中注册,如果zookeeper发现过了自己的心跳机制该节点还没有给到反馈,则认为其死亡
2、同步机制:如果某一结点为 follower ,如果不能正常的同步leader节点的数据则也可认为其死亡

拦截器&序列化器&分区器

生产者生产消息到broker需经过拦截器 -> 序列化器 -> 分区器一系列的数据封装、转化、发送;
拦截器
作用:
1、可以用来在消息发送前做一些预处理工作,比如给消息添加前缀内容等;
2、可以用来在发送回调逻辑前做一些定制化的工作,比如统计成功率等
使用:

public interface ProducerInterceptor<K, V> extends Configurable {
    // 该方法会在消息发送之前被调用
    ProducerRecord<K, V> onSend(ProducerRecord<K, V> record);
    // 该方法会在消息成功提交或发送失败之后被调用
    void onAcknowledgement(RecordMetadata metadata, Exception exception);
    // 关闭interceptor,主要用于执行一些资源清理工作
    void close();
}

使用该拦截器仅需自定义拦截器类并实现该接口,在onSend()方法写相关业务代码;且在KafkaProducer配置好自定义拦截器参数(properties.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG,自定义拦截器.class.getName());)

onAcknowledgement() 是KafkaProducer在消息被成功应答或者发送失败时被调用,还要优于用户设定的Callback方法之前执行。(ps:该方法一般在producer的I/O线程中运行,所以要足够快,不然会影响消息发送效率)

close() 见名知意,不多解释啦

PS:
可定义多个拦截器;发送消息的时候这条消息就像过减速带一样一个拦截器一个拦截器的过,并且后一个拦截器入参为前一个拦截器处理后的数据;
拦截器执行顺序取决于生产者配置拦截器的顺序
序列化器
生产者序列化器将消息按指定的序列化格式序列化成字节码;生产者序列化器将消息按指定的序列化格式再序列化回来;
如果生产者和消费者的序列化器用的不是一个;数据将会序列化不成功;
分区器

public interface Partitioner extends Configurable, Closeable {
    //计算分区号  参数分别是主题、键、序列化后的键、值、序列化后的值和集群的元数据信息
    int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster);
    //表示通知分区程序用来创建新的批次。
    void close();
    //表示通知分区程序用来创建新的批次
    default void onNewBatch(String topic, Cluster cluster, int prevPartition) {
    }
}

PS:其中重点关注的是key这个参数;如果key不为空,则默认的分区器会先对key进行hash,保证相同的key进入到同一个分区;如果key为空,那么消息以轮询的方式发送到topic的各个可用的分区;

Kafka高吞吐量设计

分布式架构:Kafka采用分布式架构,将数据分散保存在多个Broker上,每个节点都有自己的副本,可以实现数据的高可用和负载均衡,从而提高整个系统的吞吐量
零拷贝技术:减少了数据复制和传输次数;也就是说Kafka不需要将数据从内核缓冲区复制到用户空间,也不需要将数据从一个应用程序复制到另外一个应用程序,减少了数据开销
批量处理:Kafka能将多个消息批量处理,减少网络传输次数
磁盘顺序写入:Kafka将数据写入磁盘采用顺序写入,比随机写入快的多

零拷贝&传统传输

传统传输:A首先将文件从磁盘读取到内核缓冲区,然后将数据从内核缓冲区复制到A的用户空间。接下来,A将数据从它的用户空间复制到B的用户空间,最后B将数据写入磁盘或者进行其他的操作。

零拷贝:A将文件从磁盘读取到内核缓冲区后,直接将数据从内核缓冲区传输到B的用户空间,而不需要经过A的用户空间。

PS:同时零拷贝相较于传传统数据传输也有一些缺点;
(兼容性较差:零拷贝需要操作系统和硬件平台的支持,不兼容会导致无法使用)
(安全性较低:零拷贝的绕过用户空间,直接在内核缓冲区传输,可能出现数据被其他程序或软件篡改)
磁盘随机写入&顺序写入
磁盘的读写速度受到寻道时间、旋转延迟和数据传输速率三个因素的影响。其中,寻道时间是指磁头从磁盘的一个位置移动到另一个位置所需要的时间;旋转延迟是指等待磁盘旋转到正确的扇区的时间;数据传输速率是指磁盘的读写速度。
顺序写入:磁盘可以预先将需要写入的数据缓存到内存中,并将其按照顺序写入到磁盘的相邻扇区中。这样,磁盘的磁头只需要沿着一个方向移动,不需要进行频繁的寻道操作,旋转延迟也可以被最小化。因此,顺序写入的速度会更快。
随机写入:写入的数据可能会分散在磁盘的不同区域,并且可能会在不同时间进行写入。这样,磁头需要频繁地移动到不同的位置进行寻道操作,旋转延迟也会增加。这会导致磁盘的读写速度变慢,因此随机写入的速度会比顺序写入慢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值