问题导读:

1.kafka的消费者组的消费偏移存储,kafka支持两个版本?
        2.ConsumerOffsetChecker类的作用是什么?
        3.Kafka如何通过源码实现监控?

一,基本思路介绍
Kafka作为一个好用的且应用很广泛的消息队列,在大数据处理系统中基本是必不可少的。当然,作为缓存消息的消息队列,我们对其进行流量监控及消费滞后告警就显得异常重要了。
读过前面的文章,<Kafka源码系列之源码解析SimpleConsumer的消费过程>和<Kafka源码系列之Consumer高级API性能分析>这两篇文章的兄弟姐妹应该看本篇文章会很简单。实际就是利用SimpleConsumer获取Partition最新的offset,用Zookeeper的工具获取消费者组的各个分区的消费偏移,两者做差就是lagSize。
但是实际kafka的消费者组的消费偏移存储,kafka支持两个版本的:
1,基于Zookeeper。OffsetFetchRequest.CurrentVersion为0。
2,基于kafka自身。OffsetFetchRequest.CurrentVersion为1(默认)。
那么要实现一个消费者消费滞后预警,就要兼容两种方式,那么我们就详细的来介绍这两种方式的实现。
 
二,重要工具类
1,ConsumerOffsetChecker
Kafka提供的检查消费者消费偏移,LogEndSize和lagsize的工具。我们实现自己的监控均可以模仿该类实现。本文也仅限于基于该类将实现过程。
2,ZkUtils
Kafka提供的操作Zookeeper的工具类。
3,SimpleConsumer
Kafka消费者实现类。Kafka的副本同步,低级消费者,高级消费者都是基于该类实现从kafka消费消息的。
4,OffsetRequest
消费者去获取分区数据偏移的请求类,对应的请求key是:RequestKeys.OffsetsKey。在kafka的服务端kafkaApis的处理函数是:handleOffsetRequest(request)
5,OffsetFetchRequest
这个是请求某个topic的某个消费组的消费偏移,对应的请求key:RequestKeys.OffsetFetchKey。在kafka的服务端kafkaApis的处理函数是:handleOffsetFetchRequest(request)
6,OffsetManager
偏移管理器。内部维护了一个Scheduler,会定时执行compact,进行偏移的合并。
 
三,源代码实现
1,首先是获得消费者的消费偏移
ConsumerOffsetChecker当main方法中首先是获得topic列表
[Bash shell] 纯文本查看 复制代码
 

val topicList = topics match {
  case Some(x) => x.split(",").view.toList
  case None => ZkUtils.getChildren(zkClient, groupDirs.consumerGroupDir +  "/owners").toList
}

接着是建立到Broker链接,然后从kafka获取消费者偏移

[Bash shell] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
val topicPartitions = topicPidMap.flatMap { case(topic, partitionSeq) => partitionSeq.map(TopicAndPartition(topic, _)) }.toSeq
val channel = ClientUtils.channelToOffsetManager(group, zkClient, channelSocketTimeoutMs, channelRetryBackoffMs)
 
debug("Sending offset fetch request to coordinator %s:%d.".format(channel.host, channel.port))
channel.send(OffsetFetchRequest(group, topicPartitions))
val offsetFetchResponse = OffsetFetchResponse.readFrom(channel.receive().buffer)
debug("Received offset fetch response %s.".format(offsetFetchResponse))
 
offsetFetchResponse.requestInfo.foreach { case (topicAndPartition, offsetAndMetadata) =>
  if (offsetAndMetadata == OffsetMetadataAndError.NoOffset) {
    val topicDirs = new ZKGroupTopicDirs(group, topicAndPartition.topic)
    // this group may not have migrated off zookeeper for offsets storage (we don't expose the dual-commit option in this tool
    // (meaning the lag may be off until all the consumers in the group have the same setting for offsets storage)
    try {
      val offset = ZkUtils.readData(zkClient, topicDirs.consumerOffsetDir + "/%d".format(topicAndPartition.partition))._1.toLong
      offsetMap.put(topicAndPartition, offset)
    } catch {
      case z: ZkNoNodeException =>
        if(ZkUtils.pathExists(zkClient,topicDirs.consumerOffsetDir))
          offsetMap.put(topicAndPartition,-1)
        else
          throw z
    }
  }
  else if (offsetAndMetadata.error == ErrorMapping.NoError)
    offsetMap.put(topicAndPartition, offsetAndMetadata.offset)
  else {
    println("Could not fetch offset for %s due to %s.".format(topicAndPartition, ErrorMapping.exceptionFor(offsetAndMetadata.error)))
  }
}

假如,获得的偏移信息为空,那么就从Zookeeper获取消费者偏移。
解决获取topic的分区的最大偏移,实际思路是构建simpleConsumer,然后由其 去请求偏移,再跟获取的消费者偏移做差就得到消费者最大偏移。

[Bash shell] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
topicList.sorted.foreach {
  topic => processTopic(zkClient, group, topic)
}
topicPidMap.get(topic) match {
  case Some(pids) =>
    pids.sorted.foreach {
      pid => processPartition(zkClient, group, topic, pid)
    }
  case None => // ignore
}

在processPartition中

[Bash shell] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
val offsetOpt = offsetMap.get(topicPartition)
val groupDirs = new ZKGroupTopicDirs(group, topic)
val owner = ZkUtils.readDataMaybeNull(zkClient, groupDirs.consumerOwnerDir + "/%s".format(pid))._1
ZkUtils.getLeaderForPartition(zkClient, topic, pid) match {
  case Some(bid) =>
    val consumerOpt = consumerMap.getOrElseUpdate(bid, getConsumer(zkClient, bid))
    consumerOpt match {
      case Some(consumer) =>
        val topicAndPartition = TopicAndPartition(topic, pid)
        val request =
          OffsetRequest(immutable.Map(topicAndPartition -> PartitionOffsetRequestInfo(OffsetRequest.LatestTime, 1)))
        val logSize = consumer.getOffsetsBefore(request).partitionErrorAndOffsets(topicAndPartition).offsets.head

然后做差得到LagSize

[Bash shell] 纯文本查看 复制代码
1
2
3
val lagString = offsetOpt.map(o => if (o == -1) "unknown" else (logSize - o).toString)
println("%-15s %-30s %-3s %-15s %-15s %-15s %s".format(group, topic, pid, offsetOpt.getOrElse("unknown"), logSize, lagString.getOrElse("unknown"),
                                                       owner match {case Some(ownerStr) => ownerStr case None => "none"}))

getConsumer方法中

[Bash shell] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
private def getConsumer(zkClient: ZkClient, bid: Int): Option[SimpleConsumer] = {
  try {
    ZkUtils.readDataMaybeNull(zkClient, ZkUtils.BrokerIdsPath + "/" + bid)._1 match {
      case Some(brokerInfoString) =>
        Json.parseFull(brokerInfoString) match {
          case Some(m) =>
            val brokerInfo = m.asInstanceOf[Map[String, Any]]
            val host = brokerInfo.get("host").get.asInstanceOf[String]
            val port = brokerInfo.get("port").get.asInstanceOf[Int]
            Some(new SimpleConsumer(host, port, 10000, 100000, "ConsumerOffsetChecker"))
          case None =>
            throw new BrokerNotAvailableException("Broker id %d does not exist".format(bid))
        }
      case None =>
        throw new BrokerNotAvailableException("Broker id %d does not exist".format(bid))
    }
  } catch {
    case t: Throwable =>
      println("Could not parse broker info due to " + t.getCause)
      None
  }
}

四,总结
该工具类的使用

[Bash shell] 纯文本查看 复制代码
1
bin/kafka-consumer-offset-checker.sh --group yourgroup -topic yourtopic --zookeeper  localhost:2181

输出结果

Offset是消费者消费到的偏移,logsize是kafka数据的最大偏移,Lag是二者的差。也即
LagSize = LogSize - Offset
得到我们消费组的滞后情况后,我们就可以根据需求(比如,设定滞后多少消息后给出告警),给出相应的告警。

转自:http://www.aboutyun.com/forum.php?mod=viewthread&tid=22215&extra=page%3D1&page=1&

Kafka详解六:Kafka如何通过源码实现监控的更多相关文章

  1. kafka详解(一)--kafka是什么及怎么用

    kafka是什么 在回答这个问题之前,我们需要先了解另一个东西--event streaming. 什么是event streaming 我觉得,event streaming 是一个动态的概念,它描 ...

  2. kafka详解(二)--kafka为什么快

    前言 Kafka 有多快呢?我们可以使用 OpenMessaging Benchmark Framework 测试框架方便地对 RocketMQ.Pulsar.Kafka.RabbitMQ 等消息系统 ...

  3. Linux下通过源码编译安装程序

    本文简单的记录了下,在linux下如何通过源码安装程序,以及相关的知识.(大神勿喷^_^) 一.程序的组成部分 Linux下程序大都是由以下几部分组成: 二进制文件:也就是可以运行的程序文件 库文件: ...

  4. 通过源码了解ASP.NET MVC 几种Filter的执行过程

    一.前言 之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神的工作,而且很多人觉得平时根本不需要知道这些,会用就行了.其实阅读源 ...

  5. 大数据入门第七天——MapReduce详解(二)切片源码浅析与自定义patition

    一.mapTask并行度的决定机制 1.概述 一个job的map阶段并行度由客户端在提交job时决定 而客户端对map阶段并行度的规划的基本逻辑为: 将待处理数据执行逻辑切片(即按照一个特定切片大小, ...

  6. 通过源码了解ASP.NET MVC 几种Filter的执行过程 在Winform中菜单动态添加“最近使用文件”

    通过源码了解ASP.NET MVC 几种Filter的执行过程   一.前言 之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神 ...

  7. 在centos6.7通过源码安装python3.6.7报错“zipimport.ZipImportError: can't decompress data; zlib not available”

    在centos6.7通过源码安装python3.6.7报错: zipimport.ZipImportError: can't decompress data; zlib not available 从 ...

  8. 通过源码编译安装VIM

    开发中使用的是Ubuntu 12.04 LTS,通过sudo apt-get install vim安装的版本较低,不支持YCM,所以,用源码编译并安装最新的Vim. 卸载旧版本的Vim: sudo ...

  9. echarts 通过源码方法 传入对应data数据获取分割步长值

    通过源码方法获取这里的分割数字长度 /** * Quantity of a number. e.g. 0.1, 1, 10, 100 * * @param {number} val * @return ...

随机推荐

  1. Unity3d 创建线程 子线程与主线程通信

    创建子线程 一,不带参数 Thread   resourcesLoadThread=new Thread (this.resourceLoadTxt); resourcesLoadThread.Sta ...

  2. jQuery倒计时实例

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. 【转】哈希(Hash)与加密(Encrypt)的基本原理、区别及工程应用

    0.摘要 今天看到吉日嘎拉的一篇关于管理软件中信息加密和安全的文章,感觉非常有实际意义.文中作者从实践经验出发,讨论了信息管理软件中如何通过哈希和加密进行数据保护.但是从文章评论中也可以看出很多朋友对 ...

  4. C#快速整理代码格式

    删除最后一个大括号,再添加.vs自动整理代码结构.

  5. 如何生成动态库 .dll 的符号 .lib 文件?

    在知道拥有动态库和头文件的情况下,但没有动态库符号文件的情况下,如何静态链接动态库? 1.使用 Microsoft Visual Studio Tools 命令行命令生成 xxx.def 文件,进而使 ...

  6. ORACLE中RECORD、VARRAY、TABLE的使用具体解释

     1     说明 1.1       RECORD 定义记录数据类型. 它类似于C语言中的结构数据类型(STRUCTURE).PL/SQL提供了将几个相关的.分离的.基本数据类型的变量组成一个总 ...

  7. 0102-使用 API 网关构建微服务

    一.移动客户端如何访问这些服务 1.1.客户端与微服务直接通信[很少使用] 从理论上讲,客户端可以直接向每个微服务发送请求.每个微服务都有一个公开的端点(https ://.api.company.n ...

  8. 2.1 使用ARDUINO控制MC20打电话

    需要准备的硬件 MC20开发板 1个 https://item.taobao.com/item.htm?id=562661881042 GSM/GPRS天线 1根 https://item.taoba ...

  9. JAVA抠取Excel中的图片

    EXCEL中扔了一堆的图片,老大让对应到数据库中的数据上.思路先把图片抠出存成单个图片.然后上传到服务器,取下路径更新到数据库中. 注释掉的部分为有多个Excel时使用. package com.** ...

  10. HDF及HDF-EOS数据格式简介

    HDF-EOS数据格式介绍 HDF(Hierarchy Data Format )数据格式是美国伊利诺伊大学国家超级计算应用中心(NCSA ,National Central for Super co ...