请教大家一般后台任务是怎么管理的? 意外断掉怎么检测?

2016-10-12 18:42:23 +08:00
 yesterdaysun
我现在有一个 Java 的 WEB 程序, 有一些前端触发的后台任务, 比如同步一些数据. 为了不让它重复运行, 我在数据库里单独给后台任务建了一张表, 记录当前运行状态(运行, 结束, 错误), 这样即使前端意外刷新或者关闭了也不会导致用户重复触发这个任务.
现在遇到一个问题, 正常结束或者异常都能正确检测到,然后更新状态(在最外面用了 try.catch.finally). 但是确实发现有时后台任务已经结束了, 但是那个任务记录没有被更新.
比如正常任务 5 分钟结束, 但是几个小时过去了, 状态还是正在运行, 但是查 log 又什么都没有发现, 感觉就像这个任务线程莫名其妙自己结束了.
这样的情况偶尔发生, 但是每次发生都要人工去数据库修改状态, 才能继续重新运行, 很麻烦.

我现在一方面在查问题的原因, 另一方面想请教大家在这个任务上业界的一般做法或者最佳实践是什么? 有没有什么好的办法能避免这样的问题, 或者能自动检测这样的情况自动处理掉?
1688 次点击
所在节点    问与答
5 条回复
eric6356
2016-10-12 18:54:33 +08:00
听起来像消息队列
paulw54jrn
2016-10-12 18:58:45 +08:00
Spring Integration + Spring Batch + RabbitMQ?
sutra
2016-10-12 19:13:04 +08:00
首先这个架构设计有点不走寻常路。

你可以改架构:
前端 -> REST API -> Web App -> Message Queue(such as ActiveMQ) <- Syncer(Standalone process)
说明:
前端通过 REST API 调用 web app , web app 向消息队列增加一个消息;
有一个单独的进程,用来做同步工作,它一直运行着,监听消息队列并处理,在这个进程里做任务并发的调度。

回到你现在的架构,你现在是任务已经结束了,但是数据库里的任务标志位依旧显示运行中。很可能是因为任务执行过程中,进程 /线程意外终端,并没有去执行 finally block ,比如意外 crash 或者断电。那么就在你的启动任务进程 /线程的父进程 /线程自身启动时重置该标志位。
yesterdaysun
2016-10-13 13:15:32 +08:00
@sutra 我现在用的是简单的 Spring Schedule, 每个任务启动一个异步的 taskExecutor, 感觉目前在调用频率上应该还用不上消息队列之类的.
但是意外中断这个问题要怎么解决呢, 你提到"就在你的启动任务进程 /线程的父进程 /线程自身启动时重置该标志位。", 那么我怎么知道重置的这个时候之前的任务是正常运行中还是已经中断了呢?
sutra
2016-10-13 13:54:18 +08:00
@yesterdaysun 在你的 schedule 的 task 里直接执行就好了,并不需要数据库标识位, spring 的 schedule task 默认就是不会并发,不信你 sleep 试试。你只要把 schedule 独立出来(因为通常我们会把 webapp 搞多台服务器做负载均衡),确保只有一个进程就好了。

那么你的 webapp 和 schedule 是怎么通信的?数据库标识位?那没问题,你在 schdule task 开始时就认为任务不在执行,也为单进程和 spring schedule 已经能确保不并发了 。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/312297

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX