问题
新上线的 Bakong 查询任务未执行,也没有输出查询日志,导致 Bakong 订单无法通知。
排查
日志分析
日志关键词:notifyBakongSyncData
向前检查日志,发现 PostgreSQL 偶尔执行得非常慢,耗时高达 39 秒。
JStack 排查
命令:
jstack pid | grep -C[显示上下行数] tid > jstack.log
jstack 1 | grep -C1000 12 > jstack.log
得到线程栈日志:
关键日志:
"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
文档说明:
socketTimeout(int)的默认值为 0。- 它用于设置 Socket 读取操作的超时时间;如果从服务器读取数据的耗时超过该值,连接将被关闭。
- 此参数既可作为强制性的全局查询超时机制,也可用于检测网络问题。
- 超时时间以秒为单位,最大可设置为 2,147,484 秒(约 24.8 天);值为 0 表示禁用超时,即无限等待。
查看配置:
数据库连接没有配置 socketTimeout 参数。
结论
结合 PostgreSQL JDBC 官方文档和 JStack 日志,可以确认:
- 数据库查询偶尔会超时,存在读写失败和延迟较高的情况。
- PostgreSQL 连接未设置
socketTimeout,无限等待导致任务假死。
方案
配置连接超时参数
增加 socketTimeout、loginTimeout 和 connectTimeout,解决连接、读写 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 秒的连接会按预期超时报错;定时任务不会中断,仍可继续运行,假死问题得到解决。