另一个 goroutine 上的 Webhook 进程 运行

Webhook process run on another goroutine

我想 运行 在另一个 goroutine 中做一些慢的例程,这样做安全吗:

func someHandler(w http.ResponseWriter, r *http.Request) {
   go someReallySlowFunction() // sending mail or something slow
   fmt.Fprintf(w,"Mail will be delivered shortly..")
}

func otherHandler(w http.ResponseWriter, r *http.Request) {
   foo := int64(0)
   bar := func() {
      // do slow things with foo
   }
   go bar()
   fmt.Fprintf(w,"Mail will be delivered shortly..")
}

这样做有什么陷阱吗?

如果您关心邮件的确认,那么发布的代码将无济于事。 运行单独的goroutine中的代码使其独立,即使由于goroutine函数中的某些错误而导致邮件未发送,服务器回复也会成功。

在自己的 goroutine () 中处理每个 http 请求 运行s。您可以从您的处理程序启动新的 goroutines,它们将 运行 同时独立于执行处理程序的 goroutine。

需要注意的事项:

  • 新的 goroutine 运行 独立于 handler goroutine。这意味着它可能在处理程序 goroutine 之前或之后完成,如果没有显式同步,你不能(不应该)假设任何与此相关的事情。

  • 处理程序的 http.ResponseWriter and http.Request 参数仅在处理程序 returns 之前有效且可安全使用!这些值(或其中的 "parts")可以重复使用——这是一个实现细节,您也不应该假设任何东西。一旦处理程序 returns,您不应该触摸(甚至不读取)这些值。

  • 一旦处理程序returns,响应就被提交(或者可能随时被提交)。这意味着您的新 goroutine 不应在此之后尝试使用 http.ResponseWriter 发回任何数据。这是真的,即使你没有触及你的处理程序中的 http.ResponseWriter,处理程序中没有恐慌被认为是成功处理了请求,因此 HTTP 200 状态被发回().

您可以将 http.Requesthttp.ResponseWriter 值传递给其他函数和新的 goroutines,但必须小心:如果您需要,您应该使用显式同步(例如锁、通道)打算从多个 goroutines 读取/修改这些值(或者你想从多个 goroutines 发回数据)。

请注意,如果您的处理程序 goroutine 和您的新 goroutine 都只是读取/检查 http.Request,那似乎仍然存在问题。是的,多个 goroutine 可以在不同步的情况下读取同一个变量(如果没有人修改它)。但是调用 http.Request 的某些方法也会修改 http.Request,如果没有同步,就无法保证其他 goroutines 会从这个变化中看到什么。例如 Request.FormValue() returns a form value associated with the given key. But this method calls ParseMultiPartForm() and ParseForm(),如果需要修改 http.Request(例如,他们设置 Request.PostFormRequest.Form 结构字段)。

所以除非你同步你的goroutines,否则你不应该将RequestResponseWriter传递给新的goroutine,而是从handler goroutine中的Request获取所需的数据,并通过仅例如struct 持有所需的数据。

你的第二个例子:

foo := int64(0)
bar := func() {
   // do slow things with foo
}
go bar()

这很好。这是一个closure,它所引用的局部变量只要可访问就会一直存在。

请注意,您也可以将局部变量的值作为参数传递给匿名函数调用,如下所示:

foo := int64(0)
bar := func(foo int64) {
   // do slow things with param foo (not the local foo var)
}
go bar(foo)

在此示例中,匿名函数将看到并使用其参数 foo 而不是局部变量 foo。这可能是也可能不是你想要的(取决于处理程序是否也使用 foo 以及任何 goroutine 所做的更改是否需要对另一个可见 - 但这无论如何都需要同步,这将是被通道解决方案取代)。