Java ScheduledExecutor服务单按1分钟的时间表运行10分钟(systemd-日记故障)
我有一个executor服务,应该每分钟向磁盘写入一些内容 日程安排如下:Java ScheduledExecutor服务单按1分钟的时间表运行10分钟(systemd-日记故障),java,multithreading,systemd,scheduledexecutorservice,journal,Java,Multithreading,Systemd,Scheduledexecutorservice,Journal,我有一个executor服务,应该每分钟向磁盘写入一些内容 日程安排如下: scheduledCacheDump = new ScheduledThreadPoolExecutor(1); scheduledCacheDump.scheduleAtFixedRate(this::saveCachedRecords, 60,
scheduledCacheDump = new ScheduledThreadPoolExecutor(1);
scheduledCacheDump.scheduleAtFixedRate(this::saveCachedRecords,
60,
60,
TimeUnit.SECONDS
);
该任务使用由主线程填充的共享列表,因此它在该列表上同步:
private void saveCachedRecords() {
LOG.info(String.format("Scheduled record dump to disk. We have %d records to save.", recordCache.size()));
synchronized (recordCache) {
Iterator<Record> iterator = recordCache.iterator();
while (iterator.hasNext()) {
// save record to disk
iterator.remove();
}
}
}
private void saveCachedRecords(){
LOG.info(String.format(“计划将记录转储到磁盘。我们有%d条记录要保存。”,recordCache.size());
已同步(记录缓存){
迭代器迭代器=recordCache.Iterator();
while(iterator.hasNext()){
//将记录保存到磁盘
iterator.remove();
}
}
}
我的名单声明如下:
private final List<Record> recordCache = new ArrayList<>();
private final List recordCache=new ArrayList();
主线程以批处理方式接收数据,因此每隔一秒钟左右,它就会接收30条记录,并将其缓存在列表中。剩下的时间,它在等待一个套接字
我不明白的是,从日志中可以看出,我的计划任务通常会在一分钟后触发:
9月16日09:30:43定时记录转储到磁盘。我们有27条记录要保存。
9月16日09:31:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:32:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:33:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:34:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:35:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:42:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:43:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:44:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:45:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:46:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:55:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:56:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:57:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:58:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日09:59:43定时记录转储到磁盘。我们有27条记录要保存。
9月16日10:04:43定时记录转储到磁盘。我们有27条记录要保存。
9月16日10:05:43预定记录转储到磁盘。我们有27条记录要保存。
9月16日10:06:43定时记录转储到磁盘。我们有27条记录要保存。
看看这个:
- 9月16日09:59:43定时记录转储到磁盘。我们有27条记录要保存
- 9月16日10:04:43定时记录转储到磁盘。我们有27条记录要保存
- 9月16日09:46:43预定记录转储到磁盘。我们有27条记录要保存
- 9月16日09:55:43预定记录转储到磁盘。我们有27条记录要保存
synchronized()
范围内,因此我不知道任务是否确实按时调度并在锁上等待10分钟,或者这是否只是一个真正的调度问题。我将把它移出,但一般来说,我无法理解一个线程如何在每秒释放一次的锁上保持阻塞10分钟
我该如何调查这件事
仅供参考:它运行的机器是KVM机器,这可能是一个因素吗?哦,天哪
这根本不是Java的错。这是systemd的错
我的进程作为systemd服务运行,因此我提取的日志来自systemd journald。
猜猜看,systemd有一个利率限制。当调度程序被触发时,我的守护进程会命中它,所以我得到了很多这样的行:
抑制了来自/system.slice/xxx.service的570条消息
已抑制来自/system.slice/xxx.service的769条消息
抑制了来自/system.slice/xxx.service的745条消息
已抑制来自/system.slice/xxx.service的729条消息
已抑制来自/system.slice/xxx.service的717条消息
已抑制来自/system.slice/xxx.service的95条消息
已抑制来自/system.slice/xxx.service的543条消息
所以。。是的,我移除了journald上的节流装置,现在我每分钟都有我的痕迹
解决办法是:
- 编辑
/etc/systemd/journald.conf
- 在末尾添加行:
RateLimitInterval=0
- 执行
systemctl重新启动systemd journald
- 执行
systemctl重新启动myservice
我将更新标题以备将来参考:)您检查过GC活动了吗?我没有。有没有一个CLI工具来做这个(我不是java专家,我承认)与你的问题无关,但是考虑使用一个并发的链接队列代替一个数组,它是线程安全的,而且一个队列似乎比你需要的列表更合适,堆的大小是什么?我的列表中有相当数量的记录(27条),但每一条记录都会被每秒操纵以向其中添加数据。当我将其转储到磁盘时,我会完全清空列表并重新开始。所以ConcurrentLinkedQueue就没那么好了,因为我仍然需要在记录修改中进行sync(),这就意味着你应该修复你的其他代码,不要那么多垃圾邮件。达到速率限制意味着您的守护进程在默认情况下会在30秒内记录超过1K条消息。当其他事情变得疯狂时,完全取消费率限制将对您的日志有害。(例如,wpa_supplicant或NetworkManager)这是一台生产机器,我们每秒接收30条消息,每条消息2行,因此它每分钟输出3600条消息。它是非常可压缩的(我们的logrotate将2GB的日志压缩为17MB),但我们需要它们,所以我们不会对它们进行速率限制。。