Kafka的项目实践

最近在项目中使用了Kafka作为高并发处理的一个环节,即多台机器采集数据后同时发送到Kafka,消费端接收数据后保存到elasticsearch里面。
在这个过程中,原本以为用Kafka就那样,建个topic分几个分区,数据生产端直接发送数据到该topic,然后消费端poll数据到本机处理就ok了。实际上等项目跑起来后发现,数据消费速度很慢,完全达不到要求。我们的要求是每秒起码要有1万条的处理效率。
看到这里,很多人可能会说多开启几个消费端进程处理就行。因为Kafka消费端是非线程安全的,所以一个进程只能有一个消费端。实际上我后面加了几个消费端之后,处理的效率还是比较差的。
所以只能从代码逻辑入手了。首先使用排除法,去除了插入es的影响,因为es使用bulk插入的速度也是验证过没问题的。后面我们通过重构Kafka消费端处理就解决了该问题,原因及解决办法如下:

1、幂等性验证降低速度
由于我们知道消息队列都是有可能导致的消息重复发送的,不管是网络抖动还是人为配置的原因,所以我在代码中原本是使用了redis存储消息的key,然后每次拉消息回来后,全部过滤一遍剔除重复的消息。后面我们重新思考了一下,对于我们的业务场景,多台机器采集了主机、CPU、内存等监控数据并发送到Kafka,这种监控数据不是特别在意重复问题,不像电商里面的订单一样涉及到了金钱,另外一方面我们做了一些其他的处理降低重复的概率。所以幂等性我就直接去掉了,处理如下:
生产者配置:props.put(“request.required.acks”, -1);//保证消息必须发送到Kafka
消费者配置:props.put(“auto.offset.reset”, “latest”);//只消费最新的数据
利用es的特性,如果插入的数据中ID一样的话会进行覆盖。

2、Kafka拉取消息和处理逻辑分开处理
这个是解决速度慢的关键,由于Kafka消费端是非线程安全,所以消费端拉取数据是使用单线程,处理数据则是多线程处理,代码逻辑如下:
创建线程池:

private final ExecutorService threadPool = new ThreadPoolExecutor(10,
            30, 30, TimeUnit.MINUTES,
            new ArrayBlockingQueue<>(30),
            Executors.defaultThreadFactory(),
            (r, executor) -> {
                try {
                    executor.getQueue().put(r);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });

单线程消费数据:

List<MessageModel> msgList = new ArrayList<>();
                ConsumerRecords<String, String> records = alarmsConsumer.readRecords();
                for (TopicPartition partition : records.partitions()){
                    for (ConsumerRecord<String, String> record : records.records(partition)){
                        MessageModel message = new MessageModel(record.key(),record.value());
                        msgList.add(message);
                    }
                }

多线程处理数据

if (msgList!=null && !msgList.isEmpty()){
                    threadPool.execute(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                DealMessageInterface processor = new DealMessage();
                                //实际的处理逻辑
                                processor.dealMessage(msgList);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }

稍微总结一下:消息队列的幂等性校验要根据实际的情况来,有些甚至都不用考虑这个的;凡是需要快速处理的,根据情况尽量使用多线程模式。当然,很多时候不用去怀疑Kafka的效率,基本上都是自己的代码逻辑问题导致的速度慢。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gemini技术窝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值