kafka读书笔记-消费者-多线程实现及消费者参数配置

本文介绍了KafkaConsumer的线程安全性,分析了其非线程安全的原因,探讨了多线程消息消费的三种方式:线程封闭、自定义消费流程和分离消息拉取与业务处理逻辑,其中,线程封闭最简单,分离处理易于扩展消费能力,自定义流程复杂。同时,提到了消费者端参数的重要性。

KafkaProducer是线程安全的,可以多线程共用一个实例,而KafkaConsumer是非线程安全的,这点我对比了下KafkaProducer和KafkaConsumer中的成员变量。KafkaConsumer非线程安全的原因应该在于下面这个成员变量:

private boolean cachedSubscriptionHashAllFetchPositions;

cachedSubscriptionHashAllFetchPositions在每回poll方法调用时都会去判断订阅分区位移是否有效,有效为true,无效则在poll的指定时间内重置分区offset,这个参数多线程条件下修改是不安全的。

KafkaConsumer除了wakeUp()的其它公共方法调用时都会进行一次acquire()检测,类似于乐观锁的机制,判断是否被不同线程调用。acquire方法如下:

private void acquire() {
        long threadId = Thread.currentThread().getId();
        if (threadId != currentThread.get() && !currentThread.compareAndSet(NO_CURRENT_THREAD, threadId))
            throw new ConcurrentModificationException("KafkaConsumer is not safe for multi-threaded access");
        refcount.incrementAndGet();
    }

通过本地维护一个原子变量currentThread来判断当前调用的线程id是否与之前存入的原子变量中的currentThread一致,若不一致则抛出异常。

消息消费多线程方式有以下几种:
1.线程封闭:每个线程实例化一个KafkaConsumer对象,每个KafkaConsumer实例可以消费一个或多个分区中的消息。由于之前介绍的默认情况下一个分区最多只能被一个消费者消费的关系,这种方式KafkaConsumer最多不能超过分区的数量,超过分区数量的消费者线程就会处于空闲状态。
2.assign、seek自定义消费流程。通过assign和seek方法可以打破消费线程不能超过分区数的限制,提高消费能力,但多个消费者消费同一分区时的位移提交和顺序处理会变得异常复杂,不推荐使用。
3.分离消息拉取与业务处理逻辑。采用A线程拉取消息,拉取到的消息丢入线程B或者线程池B进行处理,线程池B进行处理时需注意offsets读写需加锁,避免并发问题。同时考虑到会出现线程池消费数据0~99时异常未能提交offsets,但是消费数据100~199时正常提交offsets就会出现offsets覆盖的问题,所以滑动窗口的概念,就是说窗口中总共有0~99、100~199、200~299三组消息,三组消息同时在线程池B中进行业务逻辑处理,采用startOffset记录0~99所在位置数据,endOffsets记录200~299所在位置数据,只有当startOffset所在位置数据处理完之后,整个滑动窗口才能后移一格,如果100~199先处理完,那么需要等待0~199处理完之后整个窗口才能后移两格。为了避免startOffset所在窗口异常数据无法处理造成的停顿,还可以加上超时时间的设置。

上面几种多线程实现方式1是最简单的,3易于横向扩展消费能力的,并且减少了TCP连接,2逻辑处理麻烦,基本没啥用。

下表记录一些消费者端参数:

参数名称默认值参数释义
fetch.min.bytes1(B)一次拉取请求拉取最小数据量 ,不足时会等待
fetch.max.bytes50(MB)一次拉取请求拉取最大数据量,单条消息若超过,则仍可以拉取(所以限制做啥用的?—保留疑问)
fetch.max.wait.ms500(ms)一次拉取最长等待时间,与fetch.min.bytes对应,不足时的等待时间
max.partition.fetch.bytes1048576(B)每个分区返回的最大值,一次拉取中单个分区返回的消息大小限制,也是非强制,消息过大时仍可消费
max.poll.records500(条)一次拉取请求中拉取的最大消息数
connection.max.idle.ms540000(ms)多久之后关闭闲置的连接
exclude.internal.mstrue内部主题(__consumer_offsets和__transaction_state)是否可以向消费者公开,true代表不公开,false代表公开,公开则可以通过订阅正则表达式的方式订阅到内部主题
receive.buffer.bytes65536(B)Socket接收消息缓冲区的大小,设为-1时代表使用操作系统默认值
send.buffer.bytes131072(B)Socket发送消息缓冲区大小,设为-1时采用操作系统默认值
request.timeout.ms30000(ms)Consumer等待请求响应的最长时间
metadata.max.age.ms300000(ms)元数据强制更新的间隔时间
reconnect.backoff.ms50(ms)连接失败重试的间隔时间
retry.backoff.ms100(ms)发送请求失败重试的间隔时间
isolation.levelread_uncommitted事务隔离级别
bootstrap.servers连接kafka集群所需的broker地址清单
key.deserializerkey对应的反序列化类
value.deserializervalue对应的反序列化类
group.id消费组名称
client.id消费者客户端id
heartbeat.interval.ms3000消费者到消费者组协调器的心跳包时间
session.timeout.ms10000组管理协议中检测消费者失效的超时时间
max.poll.interval.ms300000poll拉取消息的最长空闲时间,超时则认为消费者挂掉了,将其剔除出消费组
auto.offset.resetlatest消费位移自动重置的选项,可为earliest、latest或none
enable.auto.committrue是否开启自动提交消费位移功能
auto.commit.interval.ms5000开启自动提交消费位移时,自动提交的时间间隔
partition.assignment.strategyorg.apache.kafka.clients.consumer.RangeAssignor消费者的分区策略
interceptor.class消费者客户端的拦截器
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值