如何在 Web 服务器重新启动(或代码刷新/升级)后恢复 Go 定时器?

huangapple go评论65阅读模式
英文:

How to recover Go timer from web-server restart (or code refresh/upgrade)?

问题

考虑一个网络服务,用户可以通过 API 请求在特定的预定时间开始一个任务。任务定义和预定时间会被保存在数据库中。
我首先想到的方法是启动一个 Go 定时器,并在 Goroutine 中等待定时器到期(不阻塞请求)。在时间到期后,这个 Goroutine 会触发另一个 API 请求来开始执行任务。

现在的问题是当这个服务重新部署时会出现什么情况。为了实现零停机部署,我正在使用 Einhorngoji。在代码重新加载后,显然定时器的 Goroutine 和定时器到期处理的 Goroutine 都会终止。有没有办法在代码重新加载后恢复 Go 定时器呢?

我还遇到另一个问题,就是允许用户在定时器启动后中断它。Go 定时器提供了 Stop 方法来实现这个功能。但由于这是一个无状态的 API,当收到 \interrupt 请求时,服务无法获取定时器通道的上下文。而且似乎不可能将通道(从 NewTimer 返回的)序列化到磁盘或数据库中。

也有可能我没有从正确的角度看待这个问题。如果有任何建议,将不胜感激。

英文:

Consider a web service, for instance, where user can make an API request to start a task at certain scheduled time. Task definition and scheduled time are persisted in a database.
First approach I came up with is to start a Go timer and wait for the timer to expire in a Goroutine (not blocking the request). This goroutine, after time expiration, will also fire another API request to start executing the task.

Now the problem arises when this service is redeployed. For zero downtime deployment I am using Einhorn with goji. After code reload, obviously both timer goroutine and timer-expiration-handler goroutine dies. Is there any way to recover Go timer after code reload?

Another problem I am struggling with is to allow the user to interrupt the timer (once its started). Go timer has Stop to facilitate this. But since this is a stateless API, when the \interrupt request comes in service doesn't have context of timer channel. And it seems its not possible to marshal the channel (returned from NewTimer) to disk/db.

Its also very well possible that I am not looking at the problem from correct perspective. Any suggestions would be highly appreciated.

1: https://github.com/stripe/einhorn.git "einhorn"
2: https://github.com/zenazn/goji.git "goji"
3: http://golang.org/pkg/time/#Timer.Stop
4: http://golang.org/pkg/time/#NewTimer

答案1

得分: 1

一种常用的方法是在应用程序之外安排任务,例如使用crontab或systemd定时器。

例如使用crontab:

# 每30分钟运行一次
*/30 * * * * /usr/bin/curl --head http://localhost/cron?key=something-to-verify-local-job >/dev/null 2>&1

使用外部任务队列也是一个有效的选择,就像@Not_a_Golfer提到的那样,但更加复杂。

英文:

One approach that's commonly used is to schedule the task outside your app, for example using crontab or systemd timers.

For example using crontab:

# run every 30 minutes
*/30 * * * * /usr/bin/curl --head http://localhost/cron?key=something-to-verify-local-job >/dev/null 2>&1

Using an external task queue is also a valid option like @Not_a_Golfer mentioned but more complicated.

huangapple
  • 本文由 发表于 2014年10月10日 17:54:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/26296664.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定