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

Set*_*own 7 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)
}
Run Code Online (Sandbox Code Playgroud)

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

Jer*_*all 10

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

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

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

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

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

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

  • 请注意,现在开发中(在go tip中)是调度程序的一个版本,它将在任何函数调用上抢占goroutine,而不仅仅是上面的列表.预计这将进入1.2. (3认同)

Edd*_*dez 10

我知道这个问题是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)
}
Run Code Online (Sandbox Code Playgroud)

说明:

主要()

  • 使用go例程在后台运行worker
  • 用我的处理程序启动服务
  • 请注意,此刻工人准备接受工作

工人()

  • 它是接收频道的执行例程
  • for循环永远不会结束,因为通道永远不会关闭
  • 当频道包含工作时,请执行一些工作(例如,等待5秒钟)

handler()

  • 写入通道以激活作业
  • 立即返回打印“ hello world”到页面

很酷的事情是,您可以发送任意数量的请求,因为此方案仅包含1个工作程序。下一个请求将一直等到前一个请求完成。

太棒了!