问题

新上线的 Bakong 查询任务未执行,也没有输出查询日志,导致 Bakong 订单无法通知。

排查

日志分析

日志关键词:notifyBakongSyncData

image2026-2-5_11-20-57

向前检查日志,发现 PostgreSQL 偶尔执行得非常慢,耗时高达 39 秒。

image2026-2-5_11-30-28

JStack 排查

命令:

jstack pid | grep -C[显示上下行数] tid > jstack.log
jstack 1 | grep -C1000 12 > jstack.log

得到线程栈日志:

jstack.txt

关键日志:

"scheduled-task-2" #23 prio=5 os_prio=0 tid=0x00007fd64de9d800 nid=0x1f runnable [0x00007fd5e3dfd000]
  java.lang.Thread.State: RUNNABLE
  at java.net.SocketInputStream.socketRead0(Native Method)
  at java.net.SocketInputStream.read(SocketInputStream.java:170)
  ...
  at org.postgresql.core.VisibleBufferedInputStream.readMore(VisibleBufferedInputStream.java:161)
  at org.postgresql.core.VisibleBufferedInputStream.ensureBytes(VisibleBufferedInputStream.java:128)
  at org.postgresql.core.VisibleBufferedInputStream.read(VisibleBufferedInputStream.java:73)
  at org.postgresql.core.PGStream.receiveChar(PGStream.java:441)
  at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2057)
  at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:323)
  - locked <0x00000000fe51b788> (a org.postgresql.core.v3.QueryExecutorImpl)
  ...
  at com.chaos.bakong.notifier.query.job.OrderNotifyDataSyncJob.notifyBakongSyncData(OrderNotifyDataSyncJob.java:50)

分析:

  • scheduled-task-2 处于 RUNNABLE 状态,但卡在 SocketInputStream.socketRead0——这是阻塞式网络读取。
  • 该线程正在等待 PostgreSQL 返回查询结果;数据库迟迟未响应,导致它一直占用调度线程池中的一个工作线程。

为什么会一直卡在:SocketInputStream.socketRead0

参考 PostgreSQL JDBC 驱动的连接参数文档

image2026-2-5_11-44-24

文档说明:

  • socketTimeout(int)的默认值为 0。
  • 它用于设置 Socket 读取操作的超时时间;如果从服务器读取数据的耗时超过该值,连接将被关闭。
  • 此参数既可作为强制性的全局查询超时机制,也可用于检测网络问题。
  • 超时时间以秒为单位,最大可设置为 2,147,484 秒(约 24.8 天);值为 0 表示禁用超时,即无限等待

查看配置:

image2026-2-5_11-47-59

数据库连接没有配置 socketTimeout 参数。

结论

结合 PostgreSQL JDBC 官方文档和 JStack 日志,可以确认:

  • 数据库查询偶尔会超时,存在读写失败和延迟较高的情况。
  • PostgreSQL 连接未设置 socketTimeout,无限等待导致任务假死。

方案

配置连接超时参数

增加 socketTimeoutloginTimeoutconnectTimeout,解决连接、读写 PostgreSQL 超时导致的假死问题。

jdbc:postgresql://103.173.187.2:55439/tps?connectTimeout=10000&loginTimeout=10&socketTimeout=10

      connectTimeout=10000&     # TCP 连接超时10000 毫秒 = 10 
      loginTimeout=10&         # 登录总超时10 单位
      socketTimeout=10         # 查询读取超时10 单位

connectTimeout 会被传递给 Socket.connect(address, timeout),而 Java 的 Socket.connect() 方法中 timeout 参数的单位是毫秒。参见 PGProperty.java

观察测试环境 PostgreSQL 数据库

经排查确认,由于广州到金边的网络状况以及测试环境本身的限制,数据库连接和查询超时属于正常预期。

验证

测试环境增加参数后,超过 10 秒的连接会按预期超时报错;定时任务不会中断,仍可继续运行,假死问题得到解决。

image2026-2-5_14-47-39