前言

之前写过一篇《从源码分析如何优雅的使用 Kafka 生产者》 ,有生产者自然也就有消费者。

建议对 Kakfa 还比较陌生的朋友可以先看看。

就我的使用经验来说,大部分情况都是处于数据下游的消费者角色。也用 Kafka 消费过日均过亿的消息(不得不佩服 Kakfa 的设计),本文将借助我使用 Kakfa 消费数据的经验来聊聊如何高效的消费数据。

单线程消费

以之前生产者中的代码为例,事先准备好了一个 Topic:data-push,3个分区。

先往里边发送 100 条消息,没有自定义路由策略,所以消息会均匀的发往三个分区。

先来谈谈最简单的单线程消费,如下图所示:

由于数据散列在三个不同分区,所以单个线程需要遍历三个分区将数据拉取下来。

单线程消费的示例代码:

这段代码大家在官网也可以找到:将数据取出放到一个内存缓冲中最后写入数据库的过程。

先不讨论其中的 offset 的提交方式。

通过消费日志可以看出:

取出的 100 条数据确实是分别遍历了三个分区。

单线程消费虽然简单,但存在以下几个问题:

  • 效率低下。如果分区数几十上百个,单线程无法高效的取出数据。
  • 可用性很低。一旦消费线程阻塞,甚至是进程挂掉,那么整个消费程序都将出现问题。

多线程消费

既然单线程有诸多问题,那是否可以用多线程来提高效率呢?

在多线程之前不得不将消费模式分为两种进行探讨:消费组、独立消费者。

这两种消费模式对应的处理方式有着很大的不同,所以很有必要单独来讲。

独立消费者模式

先从独立消费者模式谈起,这种模式相对于消费组来说用的相对小众一些。

看一个简单示例即可知道它的用法:

值得注意的是:独立消费者可以不设置 group.id 属性。

也是发送100条消息,消费结果如下:

通过 API 可以看出:我们可以手动指定需要消费哪些分区。

比如 data-push Topic 有三个分区,我可以手动只消费其中的 1 2 分区,第三个可以视情况来消费。

同时它也支持多线程的方式,每个线程消费指定分区进行消费。

为了直观,只发送了 10 条数据。

根据消费结果可以看出:

c1 线程只取 0 分区;c2 只取 1 分区;c3 只取 2 分区的数据。

甚至我们可以将消费者多进程部署,这样的消费方式如下:

假设 Topic:data-push 的分区数为 4 个,那我们就可以按照图中的方式创建两个进程。

每个进程内有两个线程,每个线程再去消费对应的分区。

这样当我们性能不够新增 Topic 的分区数时,消费者这边只需要这样水平扩展即可,非常的灵活。

这种自定义分区消费的方式在某些场景下还是适用的,比如生产者每次都将某一类的数据只发往一个分区。这样我们就可以只针对这一个分区消费。

但这种方式有一个问题:可用性不高,当其中一个进程挂掉之后;该进程负责的分区数据没法转移给其他进程处理。

消费组模式

消费组模式应当是使用最多的一种消费方式。

我们可以创建 N 个消费者实例(new KafkaConsumer()),当这些实例都用同一个 group.id 来创建时,他们就属于同一个消费组。

在同一个消费组中的消费实例可以收到消息,但一个分区的消息只会发往一个消费实例。

还是借助官方的示例图来更好的理解它。

某个 Topic 有四个分区 p0 p1 p2 p3,同时创建了两个消费组 groupA,groupB

  • A 消费组中有两个消费实例 C1、C2
  • B 消费组中有四个消费实例 C3、C4、C5、C6

这样消息是如何划分到每个消费实例的呢?

通过图中可以得知:

  • A 组中的 C1 消费了 P0 和 P3 分区;C2 消费 P1、P2 分区。
  • B 组有四个实例,所以每个实例消费一个分区;也就是消费实例和分区是一一对应的。

需要注意的是:

这里的消费实例简单的可以理解为 new KafkaConsumer它和进程没有关系


比如说某个 Topic 有三个分区,但是我启动了两个进程来消费它。

其中每个进程有两个消费实例,那其实就相当于有四个实例了。

这时可能就会问 4 个实例怎么消费 3 个分区呢?

消费组自平衡

这个 Kafka 已经帮我做好了,它会来做消费组里的 Rebalance

比如上面的情况,3 个分区却有 4 个消费实例;最终肯定只有三个实例能取到消息。但至于是哪三个呢,这点 Kakfa 会自动帮我们分配好。

看个例子,还在之前的 data-push 这个 Topic,其中有三个分区。

当其中一个进程(其中有三个线程,每个线程对应一个消费实例)时,消费结果如下:

里边的 20 条数据都被这个进程的三个实例消费掉。

这时我新启动了一个进程,程序和上面那个一模一样;这样就相当于有两个进程,同时就是 6 个实例。

我再发送 10 条消息会发现:

进程1 只取到了分区 1 里的两条数据(之前是所有数据都是进程1里的线程获取的)。


同时进程2则消费了剩下的 8 条消息,分别是分区 0、2 的数据(总的还是只有三个实例取到了数据,只是分别在不同的进程里)。


当我关掉进程2,再发送10条数据时会发现所有数据又被进程1里的三个线程消费了。

通过这些测试相信大家已经可以看到消费组的优势了。

我们可以在一个消费组中创建多个消费实例来达到高可用、高容错的特性,不会出现单线程以及独立消费者挂掉之后数据不能消费的情况。同时基于多线程的方式也极大的提高了消费效率。

而当新增消费实例或者是消费实例挂掉时 Kakfa 会为我们重新分配消费实例与分区的关系就被称为消费组 Rebalance

发生这个的前提条件一般有以下几个:

  • 消费组中新增消费实例。
  • 消费组中消费实例 down 掉。
  • 订阅的 Topic 分区数发生变化。
  • 如果是正则订阅 Topic 时,匹配的 Topic 数发生变化也会导致 Rebalance

所以推荐使用这样的方式消费数据,同时扩展性也非常好。当性能不足新增分区时只需要启动新的消费实例加入到消费组中即可。

总结

本次只分享了几个不同消费数据的方式,并没有着重研究消费参数、源码;这些内容感兴趣的话可以在下次分享。

文中提到的部分源码可以在这里查阅:

https://github.com/crossoverJie/JCSprout

欢迎关注公众号一起交流:

分享一些 Kafka 消费数据的小经验的更多相关文章

  1. kafka消费数据策略

    单线程消费 以之前生产者中的代码为例,事先准备好了一个 Topic:data-push,3个分区. 先往里边发送 100 条消息,没有自定义路由策略,所以消息会均匀的发往三个分区. 先来谈谈最简单的单 ...

  2. Kafka消费时报错:Producer connection to xxx:9092 unsuccessful

    使用kafka消费数据时报Producer错误,具体错误如下: kafka.producer.SyncProducer:103 Producer connection to xxx:9092 unsu ...

  3. 分享调试SI4432的一些小经验(转)

    分享调试SI4432的一些小经验 最近使用 STM8F103 + SI4432 调无线,遇到问题不少,此处有参考过前辈的经验: 1.硬件把板给到我时USB烧录线带供电(5V),此供电接到LDO输出,就 ...

  4. 分享工作中遇到的问题积累经验 事务日志太大导致insert不进数据

    分享工作中遇到的问题积累经验 事务日志太大导致insert不进数据 今天开发找我,说数据库insert不进数据,叫我看一下 他发了一个截图给我 然后我登录上服务器,发现了可疑的地方,而且这个数据库之前 ...

  5. Flume简介与使用(三)——Kafka Sink消费数据之Kafka安装

    前面已经介绍了如何利用Thrift Source生产数据,今天介绍如何用Kafka Sink消费数据. 其实之前已经在Flume配置文件里设置了用Kafka Sink消费数据 agent1.sinks ...

  6. 关于kafka重新消费数据问题

    我们在使用consumer消费数据时,有些情况下我们需要对已经消费过的数据进行重新消费,这里介绍kafka中两种重新消费数据的方法. 1. 修改offset 我们在使用consumer消费的时候,每个 ...

  7. kafka丢失和重复消费数据

    Kafka作为当下流行的高并发消息中间件,大量用于数据采集,实时处理等场景,我们在享受他的高并发,高可靠时,还是不得不面对可能存在的问题,最常见的就是丢包,重发问题. 1.丢包问题:消息推送服务,每天 ...

  8. python+kafka,从指定位置消费数据

    # @staticmethoddef get_kafka_reviews(self): # print type(self.bootstrap_servers) consumer = kafka.Ka ...

  9. kafka无法消费数据

    遇到一个问题,使用Python kafka客户端和kafka命令行都无法消费数据,但是在kafka命令行后面添加--partition 0后就可以消费数据. bin/kafka-console-con ...

随机推荐

  1. ftp研究

    工作中经常用到ftp,最近闲下心来,仔细研究下ftp这个协议. FTP(文件传输协议)工作原理 目前在网络上,如果你想把文件和其他人共享.最方便的办法莫过于将文件放FTP服务器上,然后其他人通过FTP ...

  2. BZOJ_1800_[Ahoi2009]fly 飞行棋_乱搞

    BZOJ_1800_[Ahoi2009]fly 飞行棋_乱搞 Description 给出圆周上的若干个点,已知点与点之间的弧长,其值均为正整数,并依圆周顺序排列. 请找出这些点中有没有可以围成矩形的 ...

  3. 【爆料】-《亚伯大学毕业证书》Aber一模一样原件

    ☞亚伯大学毕业证书[微/Q:865121257◆WeChat:CC6669834]UC毕业证书/联系人Alice[查看点击百度快照查看][留信网学历认证&博士&硕士&海归&am ...

  4. OutputStream与PrintWriter的使用与区别

    1.OutputStream 使用步骤: 获取输出流 设置中文 将字符串转换成字节数组 调用outputStream.write() 这里只贴出doGet方法的内容: protected void d ...

  5. MySQL(七)DQL之分组查询

    一.语法 select 分组函数,分组后的字段from 表[where 筛选条件]group by 分组的字段[having 分组后的筛选][order by 排序列表] 二.特点 分组前筛选:whe ...

  6. mysql5.7连接不上可能的问题(针对新安装的mysql5.7可能出现的问题)

    "ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost' (10061)" 今天刚刚安装好的mysql5. ...

  7. Java 11 新功能来了!

    关键时刻,第一时间送达! 目前 Oracle 已经发布了 Java Development Kit 10,下个版本 JDK 11 也即将发布.本文介绍 Java 11 的新功能. 根据Oracle新出 ...

  8. 安卓开发常用icon/png图样

    平时开发当中会用到很多png图样,但是自己又不太会做,因此做好的办法就是直接在网上下载下来用了,这里给大家推荐一个比较好用的网站,免费的,有很多好的png可以直接下载下来使用. http://www. ...

  9. 神奇的Scala Macro之旅(二)- 一个实例

    优化的日志方式 package macros_demo import scala.language.experimental.macrosimport org.slf4j._import scala. ...

  10. re模块的方法总结

    re模块的方法总结 一,查找 1:match 匹配string 开头,成功返回Match object, 失败返回None,只匹配一个. 示例: s="abc221kelvin4774&qu ...