kafka就多方面理论实践总结

说明:转载本人掘金文章 https://juejin.cn/post/7094074279477641246

分区立论实践总结

理论

1.从架构上来说,分区可以达到系统伸缩的目的。增加节点,增加分区,提高整个系统的生产,消费,服务端吞吐量。

2.每个分区都是一个消息集合,都是有顺序的。

3.消息决定放入哪个分区,可以自己去选择相应的分区策略或者自定义一些自己的分区策略。kafka自带了三种分区策略,分别是:轮询,随机,key有序(key的hash一致特性),默认是轮询,适合大部分场景。

实践

1.业务中有需要保证消息有序性需求。其实一般就可以根据自定义分区来解决,而且一般是局部有序,比如订单,支付,库存这种有严密的顺序性业务,我们只要保证三个消息在一个分区,那么就可以保证业务有序性;如果真要保证全局有序性,那么就一个分区就好了,但是性能肯定存在问题的,这种需求一般不太存在,一般都是单个用户级别业务有序性。

2.业务中经常会碰到读写碰到瓶颈的时候,一般是消费瓶颈。其实这个时候我们就可以利用这个分区伸缩特性来解决。一般增加分区,同时增加消费组消费者,同时增加单个消费者吞吐量,那么基本就可以了。

消息压缩算法实践总结

理论

1.kafka中生产,消费,服务端都可以指定压缩算法,消息一旦压缩,那么从生产到服务端到消费端,那么网络io都会有所降低,可以提高系统io能力。

2.压缩算法有很多中,目前支持GZIP、Snappy , LZ4,zstd.我们需要根据我们需求去选择合适的压缩算法,选择在于压缩比(网络io)和吞吐量(cpu)去考虑。压缩可以减小包字节数,但是需要进行解压,需要cpu资源。

实践

一般而言,服务端不太关心,而且避免服务端去关心压缩的问题(生产者和服务端配置的压缩算法一致)。主要考虑生产者和消费端压缩算法问题,如果对于io资源比较关注,那么生产和消费端选择压缩比比较高算法,如果对于cpu资源比较关注,那么生产和消费端选择吞吐量比较高算法,否则找个居中的就可以(建议配置压缩算法,找个适中的,其实对io和cpu资源影响并不大,但是io肯定是减少了点,当然cpu肯定也高点)

消息丢失问题实践总结

理论

1.生产者端消息丢失。发送了消息,但是不关心结果,那么丢失了也不知道

2.服务端消息丢失。收到消息的服务端宕机了

3.消费端消息丢失。消费消息错误处理了位移

实践

1.生产端,建议使用有回调的方法进行回调,进行发送失败消息进行后置处理(producer.send(msg, callback));同时增加生产者生产重试次数

2.服务端,建议设置好分区副本数量,同时设置好min.insync.replicas > 1保证多个副本写入。

3.消费端,建议管理好位移,最好手动提交,同时保证消息幂等性。

rebalance问题实践总结

理论

1.rebalance设计的初衷是为了同一个消费组的消费者平均分配分区消费,从而提高消费者tps.所以当分区,消费者发生变化的时候,都会发生rebalance。

2.分区变化,一般是到达一个瓶颈,所以增加分区,这种rebalance是不可避免的。主要我们是要注意消费端使用不合理而导致消费者发生变化,引发的rebalance

实践

1.max.poll.interval.ms指的是拉去消息间隔时间,如果你一批消息处理太久了,超过了这个时间,那么消费者会从组里离开,并且offset无法提交。rebalance发生,消息也开始积压。(根据业务测试一批数据消耗时间),如果是手动提交还会发生CommitFailedException。

2.heartbeat.interval.ms和session.timeout.ms,如果消费者没在session.timeout.ms时间内收到心跳包,那么这个消费者会被移除出消费者,导致rebalance.所以发送心跳包速度也要设置合理,否则有时候rebalance太久,会导致其它在消费者组的消费者也无法消费(rebalance期间不可以消费,类似stw)

3.根据我测试,上面两种情况,导致消费者离开发生,如果只是网络抖动或者依赖组件宕机,导致时间过长,后期消费者还是会尝试重新加入,消费者线程还在。但是设置参数不合理导致的,会一直rebalance

@Component
public class RebalanceConsumerTest {

    /**
     * 1.默认concurrency为1
     * 2.每启动一个@KafkaListener,消费者1增加1
     * @param record
     */
    @KafkaHandler
    //@KafkaListener(topics = "quickstart-events",groupId = "test-consumer-group-666",concurrency ="2" )
    @KafkaListener(topics = "quickstart-events",groupId = "test-consumer-group-666",properties = {"heartbeat.interval.ms=3000"} )
    public void test1(ConsumerRecord record){
        System.out.println("test1接收到消息:"+new Date().toLocaleString()+"---"+Thread.currentThread().hashCode());
    }

    @KafkaHandler
    //@KafkaListener(topics = "quickstart-events",groupId = "test-consumer-group-666",concurrency ="5")
    //测试max.poll.interval.ms 处理消息太慢,会导致超过拉取下一批的时间,消费者会离开,导致rebalance,一直,导致lag
    //@KafkaListener(topics = "quickstart-events",groupId = "test-consumer-group-666",properties = {"max.poll.interval.ms=500" +
            //""})
    //测试 heartbeat.interval.ms,session.timeout.ms,如果有网络抖动,无法发送心跳包,或者频率比较低,消费者也会离开,导致rebalance,无法消费
    @KafkaListener(topics = "quickstart-events",groupId = "test-consumer-group-666",properties = {"heartbeat.interval.ms=9999"})
    public void test2(ConsumerRecord record) throws InterruptedException {
        System.out.println("test2接收到消息:"+new Date().toLocaleString()+"---"+Thread.currentThread().hashCode());
        //Thread.sleep(1000);
    }
}

说明:笔者测试代码

消息积压问题实践总结

理论

消息积压一般是生产消息大于消费消息,那么其实只要提高消费速度就行了。方案也比较简单,单个消费者线程内部使用多线程处理即可。

实践

可以在消费者线程里面使用多线程异步处理,但是线程池一般有队列,如果重启消息会丢失,所以最好需要配合手动提交即可.同时由于多线程处理,提交offset是无序的,注意消息重复消费,保证幂等性

//正常示例--手动提交(关闭自动提交)
//@KafkaHandler
//@KafkaListener(topics = "quickstart-events",groupId = "test-consumer-group")
public void test4(String msg,Acknowledgment ack){

    executorService.submit(()->{
        System.out.println("接收到消息:"+msg);
        //手动提交
        ack.acknowledge();
    });
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值