回复评论的附加信息

回复评论的附加信息

我们的 Java 系统已经运行了 2 年多,从未出现过系统挂起的情况。我们有 2 台物理服务器运行类似的 Java 软件(每台服务器上有 2 个 JVM),以形成一个集群。据我所知,只有当我们在其中一台服务器上引入核心固定和 Mappedbus.io 以在 2 个 JVM 之间进行共享内存访问时,才会开始崩溃。系统挂起在 2 周内只发生过 4 次,而且只发生在我们配置了核心固定和 JVM 之间的内存映射文件访问的机器上。我们禁用了该配置,因此我们不会固定核心以旋转读取内存映射文件,也不会固定我们的主要应用线程。注意,当我说固定时,我们也忙于旋转在固定核心上运行的线程。

不过这完全是传闻。由于系统并非每天都挂起,我不能肯定地说这与核心固定或共享内存访问有关。但是,在禁用固定(和忙旋转)并使用 LockSupport.parkNanos(5000) 循环访问共享内存的情况下,我们似乎没有遇到任何系统挂起的情况。

延迟对我们来说至关重要,因此这种“非繁忙”设置只是一种临时解决办法。

另外,请注意,我已将应用程序移至相同的服务器,并且也遇到了整个系统挂起的情况。因此,我不认为这是硬件故障。

因此,从崩溃前后的日志中挖掘,这似乎与我有关。有好几个这样的堆栈。我只是在这里发布第一个(即我不相信这与 postgres 本身有任何关系)

kernel: [25738.874778] INFO: task postgres:2155 blocked for more than 120 seconds.
kernel: [25738.874833]       Not tainted 5.4.0-050400-generic #201911242031
kernel: [25738.874878] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
kernel: [25738.874928] postgres        D    0  2155   2056 0x00004000
kernel: [25738.874931] Call Trace:
kernel: [25738.874942]  __schedule+0x2e3/0x740
kernel: [25738.874948]  ? __wake_up_common_lock+0x8a/0xc0
kernel: [25738.874951]  schedule+0x42/0xb0
kernel: [25738.874957]  jbd2_log_wait_commit+0xaf/0x120
kernel: [25738.874961]  ? wait_woken+0x80/0x80
kernel: [25738.874965]  jbd2_complete_transaction+0x5c/0x90
kernel: [25738.874969]  ext4_sync_file+0x38c/0x3e0
kernel: [25738.874974]  vfs_fsync_range+0x49/0x80
kernel: [25738.874977]  do_fsync+0x3d/0x70
kernel: [25738.874980]  __x64_sys_fsync+0x14/0x20
kernel: [25738.874985]  do_syscall_64+0x57/0x190
kernel: [25738.874991]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
kernel: [25738.874993] RIP: 0033:0x7f96dc24b214
kernel: [25738.875002] Code: Bad RIP value.
kernel: [25738.875003] RSP: 002b:00007fffb2abd868 EFLAGS: 00000246 ORIG_RAX: 000000000000004a
kernel: [25738.875006] RAX: ffffffffffffffda RBX: 00007fffb2abd874 RCX: 00007f96dc24b214
kernel: [25738.875007] RDX: 00005635889ba238 RSI: 00005635889a1490 RDI: 0000000000000003
kernel: [25738.875009] RBP: 00007fffb2abd930 R08: 00005635889a1480 R09: 00007f96cc1e1200
kernel: [25738.875010] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
kernel: [25738.875011] R13: 0000000000000000 R14: 000056358899c5a0 R15: 0000000000000001

附言:这也发生在 16.04 和内核 4.15 上。升级到 18.04 和 5.0 是为了解决系统挂起问题,但没有任何效果。

我考虑的另一件事是,也许这个跟踪只是一种症状,而不是问题。也就是说,我的应用程序绑定了服务器,导致其他进程在 io 上阻塞并收到这些错误。但由于服务器完全冻结,我无法知道当时我的应用程序的状态。

回复评论的附加信息

首先,只是重申,我没有确凿的证据表明核心固定+共享内存是压垮骆驼的最后一根稻草,但这是我根据变化历史和中断做出的最佳猜测。

CPU 型号为 Intel(R) Xeon(R) CPU E5-2620 v4 @ 2.10GHz,带 turbo boost。服务器中有 2 个这样的 CPU。我固定了 CPU 编号 2、4、6,我相信它们位于同一个物理 CPU 上。超线程已打开。

设置如下。JVM-A 有一个固定的繁忙自旋线程,写入内存映射文件 X 并从内存映射文件 Y 读取。JVM-B 有一个固定的繁忙自旋线程,从内存映射文件 X 读取并写回到内存映射文件 Y。在 JVM-B 中,固定的读取线程随后将消息发布到带有固定繁忙自旋工作器的 Disruptor 环形缓冲区上。该消息是一条订单指令,最终通过该工作器发送到市场。这是一个低延迟交易平台。

这篇文章对 LockSupport.parkNanos 进行了比我在这里更好的探索https://hazelcast.com/blog/locksupport-parknanos-under-the-hood-and-the-curious-case-of-parking/

我有 2 个 10,000rpm 的 HDD,它们处于 RAID 1 状态,并带有嵌入式 RAID 控制器。

关于目标延迟,是的,理论上我们可以将两个 JVM 合并为一个,并完全摆脱这个内存映射文件通道。但是,在这样做之前还有其他考虑因素,所以我想首先重点了解这个技术问题。

最后,此服务器上的 postgres 仅在恢复模式下运行,它不是主服务器。此外,我们的系统根本不执行太多数据库 IO。它实际上仅用于引导和一天的开始,并在夜间保留当天的交易活动。其中一次崩溃发生在数据库 IO 几乎为零的时候。

答案1

在这种情况下,“阻塞”意味着hung_task_timeout_secs任务处于 D 不可中断状态很长时间。 120 秒对于进行 I/O 来说是一个相当长的时间。

开始监控,可以从该主机获取指标。网络数据非常适合这种情况,它每秒在内存中收集大量内容,因此不需要太多磁盘 I/O。并且有漂亮的图表。

检查磁盘延迟,例如iostat -xz 1。超过个位数毫秒的等待时间是不好的。分享存储是什么,主轴、固态、SAN LUN。

关于旋转和固定,我怀疑您正在迫使调度程序挨饿。请分享所讨论的特定 CPU 型号,以及您要固定哪些核心来做什么。如何LockSupport.parkNanos()实现?

回顾vmstat 1。持续有许多任务处于运行r 或不可中断b状态是不好的。

考虑安装 BPF 并使用脚本来收集任务任务诊断。runqslower将显示超过特定阈值的等待任务。非常快是理想的,请注意阈值单位是微秒。


退一步想一想,考虑一下这个东西的设计。

延迟目标到底是什么、做什么、速度有多快?

postgres 在同一主机上运行有什么原因吗?如果它是远程的并且通过 TCP 访问,那么它的 I/O 对 JVM 应用程序来说就不会成为问题。

答案2

好吧,最后问题很简单。我的隔离测试从未导致机器崩溃,因为我的测试代码中缺少这个元素。问题本身与共享内存或核心固定无关。只是隔离核心会稍微减少可用的共享资源,以至于调度程序可能处于饥饿状态,因为...

两个 JVM 均使用以下方式设置实时优先级:

sudo renice -n -20 $!
sudo chrt -r -a -p 99 $!

整个 JVM 都受到了冲击,因此总共有近 300 个线程具有最高优先级。即使在 CPU 利用率相对较低的情况下,上下文切换也超过 150,000/秒。

我们保留了优点并删除了实时更改。这似乎已经解决了问题。旧版 RT 设置的最初目标可以通过更改我们的 busyspin/pinning/c-states/p-states 等方式来实现。

相关内容