Kafka-4614问题复盘 (MappedByteBuffer未关闭导致慢磁盘访问)
上面提到的Remote、Total和RequestQueue分别表示RemoteTimeMs、TotalTimeMs和RequestQueueTimeMs这3个JMX指标,即副本备份时间(设置了acks=-1)、请求处理总时间以及请求在队列中的等待时间。具体来说,remote time就是leader broker等待所有follower副本拉取消息的时间。显然,只有在acks=-1下才会有remote time;RequestQueueTimeMs指客户端发过来的PRODUCE请求在broker端的请求队列中待的时间,由于请求队列是使用阻塞队列实现的,所以如果该值过大的话,说明broker端负载过大,需要增大请求队列的大小,即增加参数queued.max.requests的值。
global target_pid = KAFKA_PID
global target_dev = DATA_VOLUME probe ioblock.request {
if (rw == BIO_READ && pid() == target_pid && devname == target_dev) {
t_ms = gettimeofday_ms() + 9 * 3600 * 1000 // timezone adjustment
printf("%s,%03d: tid = %d, device = %s, inode = %d, size = %d\n", ctime(t_ms / 1000), t_ms % 1000, tid(), devname, ino, size)
print_backtrace()
print_ubacktrace()
}
}
不了解SystemTap也没关系,上面的脚本就是打印Kafka进程读磁盘时内核态和用户态的栈trace,输出如下:
Thu Dec 22 17:21:39 2016,209: tid = 126123, device = sdb1, inode = -1, size = 4096 0xffffffff81275050 : generic_make_request+0x0/0x5a0 [kernel]
0xffffffff81275660 : submit_bio+0x70/0x120 [kernel]
0xffffffffa036bcaa : _xfs_buf_ioapply+0x16a/0x200 [xfs]
0xffffffffa036d95f : xfs_buf_iorequest+0x4f/0xe0 [xfs]
0xffffffffa036db46 : _xfs_buf_read+0x36/0x60 [xfs]
0xffffffffa036dc1b : xfs_buf_read+0xab/0x100 [xfs]
0xffffffffa0363477 : xfs_trans_read_buf+0x1f7/0x410 [xfs]
0xffffffffa033014e : xfs_btree_read_buf_block+0x5e/0xd0 [xfs]
0xffffffffa0330854 : xfs_btree_lookup_get_block+0x84/0xf0 [xfs]
0xffffffffa0330edf : xfs_btree_lookup+0xbf/0x470 [xfs]
0xffffffffa032456f : xfs_bmbt_lookup_eq+0x1f/0x30 [xfs]
0xffffffffa032628b : xfs_bmap_del_extent+0x12b/0xac0 [xfs]
0xffffffffa0326f34 : xfs_bunmapi+0x314/0x850 [xfs]
0xffffffffa034ad79 : xfs_itruncate_extents+0xe9/0x280 [xfs]
0xffffffffa0366de5 : xfs_inactive+0x2f5/0x450 [xfs]
0xffffffffa0374620 : xfs_fs_clear_inode+0xa0/0xd0 [xfs]
0xffffffff811affbc : clear_inode+0xac/0x140 [kernel]
0xffffffff811b0776 : generic_delete_inode+0x196/0x1d0 [kernel]
0xffffffff811b0815 : generic_drop_inode+0x65/0x80 [kernel]
0xffffffff811af662 : iput+0x62/0x70 [kernel]
0x37ff2e5347 : munmap+0x7/0x30 [/lib64/libc-2.12.so]
0x7ff169ba5d47 : Java_sun_nio_ch_FileChannelImpl_unmap0+0x17/0x50 [/usr/jdk1.8.0_66/jre/lib/amd64/libnio.so]
关于这段输出,Yuto并没有给出过多的解释。不过我特意标红了第一行和最后两行:
- 第一行中最重要的就是tid信息,即线程ID。我们需要记下这个ID:126123
- 最后这两行告诉我们目前Kafka进程下某个读磁盘的线程正在执行munmap系统调用试图删除某段地址空间的内存映射。虽然进程结束时该映射会被自动取消,但如果只是在程序中关闭了文件部署符(file descriptor)——比如调用File.close(),那么这段映射是不会自动被移除的。被标红的第二行显示出了是哪段Java代码执行的这个系统调用。从输出中我们已知是由Java的FileChannelImpl的unmap方法发出的。
$ grep 0x1ecab /tmp/jstack.dump
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007ff278d0c800 nid=0x1ecab in Object.wait() [0x00007ff17da11000]
$ grep --text 'Total time for which application threads were stopped' kafkaServer-gc.log | grep --text '11T01:4' | perl -ne '/^(.*T\d{2}:\d{2}).*stopped: ([0-9\.]+)/; $h{$1} += $2 * 1000; END { print "$_ = $h{$_}\n" for sort keys %h }'
2017-01-11T01:40 = 332.0601
2017-01-11T01:41 = 315.1805
2017-01-11T01:42 = 318.4317
2017-01-11T01:43 = 317.8821
2017-01-11T01:44 = 302.1132
2017-01-11T01:45 = 950.5807 # << 这里非常耗时
2017-01-11T01:46 = 344.9449
2017-01-11T01:47 = 328.936
2017-01-11T01:48 = 296.3204
2017-01-11T01:49 = 299.2834
- 索引文件仅仅被VFS(虚拟文件系统)标记为“已删除”。由于对应的映射对象没有被清除,所以依然有引用指向该文件,故物理文件块仍然在磁盘上
- Kafka的索引对象OffsetIndex在JVM中变为不可达,此时可以被GC收集器回收
- GC线程开始工作清除MappedByteBuffer对象,并执行对应的删除映射操作
- OffsetIndex对象通常都会在堆上保留很长时间(因为日志段文件通常都会保留很长时间),所以该对象应该早就被提升到老年代了
- OffsetIndex.delete()操作将文件标记为“已删除”,VFS稍后会更新inode缓存
- 由于G1收集器是以region为单位收集的,且它通常都会挑选包含了最多“垃圾”的region进行收集,所以包含mmap对象的region通常都不属于这类region,从而有可能很长时间都不会被收集
- 在没有被收集的这段时间内,inode缓存被更新,正式把标记为“已删除”的那个索引文件元数据信息“踢出”缓存
- 最终当GC开始回收mmap对象并调用munmap时,内核就需要执行物理删除IO操作。但此时,该索引文件的元数据信息已经从缓存中被剔除,因此文件系统就必须要从磁盘中读取这些信息。而同一时刻Kafka磁盘正在用于高速写操作(producer还在飞速运行着),所以读磁盘操作就遭遇竞争从而拉长整体GC时间。对Kafka broker来说,表现为所有Kafka线程全部暂停,极大地拉长了请求的平均响应时间
那条粗线代表bug的修复点。如图所示,在那之后请求的平均响应时间非常稳定,再也没有毛刺的现象出现,这足以说明这次修复是有效的。纵观整个bug的修复过程,Yuto对于Kafka索引文件机制、JVM GC和操作系统VFS都有很深的造诣,我自己也在这个过程中学到了很多东西。今总结出来以供大家讨论。最后贴一份Kafka committer Ijuma关于此bug的评论,足见此问题的分量:

Kafka-4614问题复盘 (MappedByteBuffer未关闭导致慢磁盘访问)的更多相关文章
- but has failed to stop it. This is very likely to create a memory leak(c3p0在Spring管理中,连接未关闭导致的内存溢出)
以下是错误日志信息: 严重: The web application [/news] registered the JDBC driver [com.mysql.jdbc.Driver] but fa ...
- android开发之数据库游标未关闭导致
replacements=[Ljava.lang.String;@4192fea8HARDWARE=hw7d501lmatchers=[Ljava.lang.String;@4192fe28RADIO ...
- 【Pod Terminating原因追踪系列之二】exec连接未关闭导致的事件阻塞
前一阵有客户docker18.06.3集群中出现Pod卡在terminating状态的问题,经过排查发现是containerd和dockerd之间事件流阻塞,导致后续事件得不到处理造成的. 定位问题的 ...
- 未关闭InputStream 引起的血案
下面的方法是从aws s3 读取文件对象下载到本地 public int downloadFile(HttpServletResponse httpResponse, String storePath ...
- 数据库连接未关闭,conn与rs未关闭
场景: 2000多人使用系统,早上打卡签到,时间点比较集中. 程序:会创建connction连接.但是未关闭,导致tomcat挂了.导致连接池已满 解决:conn.close,rs.close.记住一 ...
- 在系统启动时,Windows Vista 中、 在 Windows 7 中,Windows Server 2008 中和在 Windows Server 2008 R2 中的 497 天后未关闭 TIME_WAIT 状态的所有 TCP/IP 端口
在系统启动时,Windows Vista 中. 在 Windows 7 中,Windows Server 2008 中和在 Windows Server 2008 R2 中的 497 天后未关闭 TI ...
- 记一次排查mysql数据库连接未关闭问题的过程
在一些项目中由于一些特殊原因仍然保留着显示的获取数据库连接(Connection).提交事务.回滚事务.关闭连接等操作:其中关闭连接是比较容易疏忽又比较难在前期发现的问题. 我是如何排查连接未关闭的问 ...
- Activity has leaked window that was originally added -界面退出时未关闭对话框异常 android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running? -
退出Activity时弹出登录框,点击确定finish当前Activity,结果报了这个错,随后查找资料知道 原因: 是因为退出Activity时没有关闭弹出框,出现了这个错误 解决方法: 只需要在a ...
- 如何让Visual Studio 清除最近打开项目 关闭上次未关闭的标签窗口
删除最近打开的文件: 打开HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\FileMRUList 删除最近打开的项目: 打开HKEY_CUR ...
随机推荐
- 关于js的parseInt方式在不同浏览器下的表现
今天开发期间遇到个需求要把日期格式转换成毫秒数 日期为:2015-08-10 split之后使用parseInt将2015,08,10分别转化为数字格式. 但是使用parseInt('08')的时候却 ...
- PHP基础学习
PHP基础学习 <?php //类型 $test=123; var_dump($test);//打印出类型和数值 echo '<hr/>'; echo gettype($test); ...
- .net学习网站汇总
http://chs.gotdotnet.com/quickstart/简介:本站点是微软.NET技术的快速入门网站,我们不必再安装.NET Framework中的快速入门示例程序,直接在网上查看此示 ...
- 前端总结·基础篇·CSS(一)布局
目录 这是<前端总结·基础篇·CSS>系列的第一篇,主要总结一下布局的基础知识. 一.显示(display) 1.1 盒模型(box-model) 1.2 行内元素(inline) &am ...
- WP8.1开发中关于如何显示.gif格式动态格式图片方法
这几天又遇到个问题,就是如何显示动态图片,本来以为和显示静态图片一样,谁知不行,在网上一查才知道WP8.1不支持.gif格式动态图片的显示: 后来又在MSDN论坛上查找,也有人问类似的问题,后来就大概 ...
- web前端学习路线推荐(讲的很细致)
前端要学习三个部分:HTML,CSS,JavaScript(简称JS),因此首先明确三个概念:HTML是内容层,它的目的是表示一个HTML标签在页面里是个什么角色. CSS是样式层,它的目的是表示一块 ...
- 2017 CVTE春招内推专场 C/C++软件开发岗笔试编程题
先来一波吐槽:选择题全是不定项选择,考的内容在我看来,"反正我接受唔到咯". 比如: 1.Windows操作系统某个通信机制(具体题目忘了,反正答案我选了个熟悉的名词"消 ...
- 【Zookeeper】源码分析之请求处理链(四)
一.前言 前面分析了SyncReqeustProcessor,接着分析请求处理链中最后的一个处理器FinalRequestProcessor. 二.FinalRequestProcessor源码分析 ...
- EFcore与动态模型(三)
紧接着上面的内容,我们继续看下动态模型页面交互实现方式,内容如下: 1,如何实现动态表单 2,如何接收表单数据并绑定到动态模型上 一.如何实现动态表单 由于模型信息都是后台自定义配置的,并不是固定不变 ...
- 【Scala】Scala之Methods
一.前言 前面学习了Scala的Class,下面接着学习Method(方法). 二.Method Scala的方法与Java的方法类似,都是添加至类中的行为,但是在具体的实现细节上差异很大,下面展示一 ...