go - 在 Web 应用程序中运行计划任务
问题描述
我想每 5 分钟运行一次任务以更新我网站上的统计信息,而不会阻塞 HTTP 服务器。
我刚刚添加了基本的 HTTP 服务器逻辑和一个工人的例子。如果我像这样添加了多个任务,这是否被认为是不好的做法,还是有更好的方法?
package main
import (
"fmt"
"net/http"
"time"
)
func Home(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Home page")
}
func schedule(f func(), interval time.Duration) *time.Ticker {
ticker := time.NewTicker(interval)
go func() {
for range ticker.C {
f()
}
}()
return ticker
}
func longRunningTask() {
fmt.Println("Long running task")
}
func main() {
schedule(longRunningTask, time.Second*5)
http.HandleFunc("/", Home)
http.ListenAndServe("127.0.0.1:8080", nil)
}
解决方案
您的实现几乎就是 Go 中计划的作业/任务的样子。有 cron-esque 库可以让您更好地控制任务,但在大多数情况下,一个简单的带有循环的 goroutine 就足够了。
以下是根据您的需求增加复杂性的更多示例:
永远运行一个任务,每次运行之间等待 5 秒。无法停止且不考虑任务的运行时间。这是最简单的形式,也是最常见的形式。
go func() {
for {
task()
<-time.After(5 * time.Second)
}
}()
和以前一样,除了现在有一个通道可以停止任务,如果我们愿意的话。虽然您的实现允许您通过该方法停止任务Stop()
,但 goroutine 将保持打开状态,因为通道永远不会关闭(因此会泄漏内存)。
// Includes a channel to stop the task if needed.
quit := make(chan bool, 1)
go func() {
task()
for {
select {
case <-quit:
return
case <-time.After(5 * time.Second):
task()
}
}
}()
// To stop the task
quit <- true
这是三者中最强大的解决方案。任务可以在任何时候停止,它还考虑了任务在等待再次运行时运行的时间。在前面的示例中,如果任务运行了 1 秒,而您又等待了 5 秒,那么您的时间间隔实际上是 6 秒,相对于任务开始的时间。
该解决方案实际上仅适用于运行时间很长的任务,或者如果您的任务以恒定的时间间隔运行至关重要。如果任务必须以恒定的时间间隔运行,那么您需要考虑到time.After()
至少会等待您提供的持续时间的事实——如果 CPU 忙于其他进程/goroutines,它最终可能会等待更长的时间。
// Calls `fn` and then waits so the total elapsed time is `interval`
func runAndWait(interval time.Duration, fn func()) {
earlier := time.Now()
fn()
diff := time.Now().Sub(earlier)
<-time.After(interval - diff)
}
quit := make(chan bool, 1)
go func() {
// The total time to run one iteration of the task
interval := 5 * time.Second
for {
select {
case <-quit:
return
default:
runAndWait(interval, task)
}
}
}()
推荐阅读
- list - 列表滚动在 SwiftUI 视图中的其他手势旁边不起作用
- r - R中的友谊网络识别
- angular - Cypress 在检查行数之前等待视图中的行列表出现
- microsoft-graph-api - 如何使用 Microsoft Graph API 连续获取邮件?
- flutter - 未定义命名参数:
- python - 关于竞争性编程问题的特殊输入问题?
- javascript - 如何在获取 api (ReactJS) 时避免将未定义的对象属性传递给道具
- python - 如何有效地从 numpy 数组初始化 pyarrow 中固定大小的 ListArray?
- excel - 将多张工作表复制到新工作簿,同时仅保留值和数据透视表
- reactjs - 如何更改 React Material-UI ToggleButton Color (for selected)