使用Goroutines在http处理程序中进行后台工作

时间:2013-08-17 20:40:22

标签: go goroutine

我以为我找到了一种简单的方法来立即返回http响应,然后在后台进行一些工作而不会阻塞。但是,这不起作用。

func MyHandler(w http.ResponseWriter, r *http.Request) {
    //handle form values
    go doSomeBackgroundWork() // this will take 2 or 3 seconds
    w.WriteHeader(http.StatusOK)
}

它第一次工作 - 立即返回响应并开始后台工作。但是,任何进一步的请求都会挂起,直到后台goroutine完成。有没有更好的方法,这不涉及设置消息队列和单独的后台进程。

2 个答案:

答案 0 :(得分:8)

将多路复用goroutine转移到由GOMAXPROCS环境设置确定的可用线程上。因此,如果将此值设置为1,则单个goroutine可以占用单个线程Go可用,直到它将控制权返回到Go运行时。很可能doSomeBackgroundWork一直在单个线程上占用,这阻止了http处理程序的调度。

有很多方法可以解决这个问题。

首先,作为使用goroutines时的一般规则,您应该将GOMAXPROCS设置为系统具有的CPU数量或更大的CPU数量。

其次,您可以通过执行以下任一操作来控制goroutine:

  • runtime.Gosched()
  • ch <- foo
  • foo := <-ch
  • select { ... }
  • mutex.Lock()
  • mutex.Unlock()

所有这些都将回退到Go运行时调度程序,为其他goroutine提供工作机会。

答案 1 :(得分:6)

我知道这个问题是4年前发布的,但我希望有人能发现这个问题很有用。

这是一种方法

有一些叫做工作池https://gobyexample.com/worker-pools使用go例程和通道

但是在下面的代码中我将它改编为处理程序。 (为简单起见,我忽略了错误,并且我将作业用作全局变量)

package main

import (
    "fmt"
    "net/http"
    "time"
)

var jobs chan int

func worker(jobs <-chan int) {
    fmt.Println("Register the worker")
    for i := range jobs {
        fmt.Println("worker processing job", i)
        time.Sleep(time.Second * 5)
    }
}

func handler(w http.ResponseWriter, r *http.Request) {
    jobs <- 1
    fmt.Fprintln(w, "hello world")
}

func main() {
    jobs = make(chan int, 100)
    go worker(jobs)

    http.HandleFunc("/request", handler)
    http.ListenAndServe(":9090", nil)
}

解释:

主要()

  • 使用go例程在后台运行工作人员
  • 使用我的处理程序启动服务
  • 请注意,此时的工作人员已准备好接收工作

<强>工人()

  • 这是一个接收频道的常规例程
  • for循环永远不会结束,因为频道永远不会关闭
  • 当频道包含作业时,做一些工作(例如等待5秒)

<强>处理程序()

  • 写入频道以激活作业
  • 立即返回打印&#34; hello world&#34;到页面
很酷的是,你可以发送你想要的任意数量的请求,因为这个场景只包含1个工作者。下一个请求将等到上一个请求完成。

这太棒了Go!