【问题标题】:golang server middlerware request cancellationgolang 服务器中间件请求取消
【发布时间】:2018-02-22 11:02:58
【问题描述】:

我在底部的示例中创建了一个在端口 3000 上运行的小服务器。您可以通过“htto://localhost:3000/time”访问它。整个 Request 被两个中间件覆盖。第一个“cancelHandler”和第二个“otherHandler”被调用——它在 4 秒后响应一些虚拟数据。

我的问题:当我在浏览器中请求页面然后取消请求时(在 4 秒之前)。服务器仍在后台处理 goroutine/请求。我已经花了几个小时在谷歌上找到一个解决方案,但我无法理解上下文。 (context.WithCancel()) 我知道我必须创建一个 chan 并收听它,但这如何处理请求。那已经是一个 goroutine,我是否必须在 request/goroutine 中创建另一个 goroutine?还有一个问题是,我真的应该为此使用 Context 还是使用 cancelNotifier 有更简单的解决方案?

也许有人可以为我和其他可能有相同理解问题的人描述它。

解决方案应该是cancel Handler在停止goroutine/request,当浏览器取消请求时。

非常感谢您的宝贵时间!

package main

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

func otherHandler(format string) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        time.Sleep(time.Duration(4)*time.Second)
        tm := time.Now().Format(format)
        w.Write([]byte("The time is: " + tm))
        fmt.Println("response:", "The time is: "+tm)

    }
    return http.HandlerFunc(fn)
}

func cancelHandler(h http.Handler) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        fmt.Println("start: called cancelHandler")

        h.ServeHTTP(w, r)

        fmt.Println("end: called cancelHandler")

    }
    return http.HandlerFunc(fn)
}

func main() {
    mux := http.NewServeMux()

    th := otherHandler(time.RFC1123)
    mux.Handle("/time", cancelHandler(th))

    log.Println("Listening...")
    http.ListenAndServe(":3000", mux)
}

【问题讨论】:

    标签: go server middleware


    【解决方案1】:

    “停止”函数的唯一方法是从它返回。因此, time.Sleep 不能被中断。请改用 select 语句:

    package main
    
    import (
        "fmt"
        "net/http"
        "time"
    )
    
    func main() {
        http.ListenAndServe(":3000", otherHandler(time.RFC1123))
    }
    
    func otherHandler(format string) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
                select {
                case <-time.After(4 * time.Second):
                        // time's up
                case <-r.Context().Done():
                        // client gave up
                        return
                }
    
                tm := time.Now().Format(format)
                w.Write([]byte("The time is: " + tm))
                fmt.Println("response:", "The time is: "+tm)
        }
    }
    

    一般来说,在战略位置检查请求上下文(或从它派生的上下文)。如果上下文被取消,请不要继续操作,return

    【讨论】:

    • 也许我应该提一下,当客户端中断连接时,Go 会为你取消上下文。除非您想添加自己的中止条件,否则您不需要 context.WithCancel/Timeout/Deadline。
    • 首先感谢您的回答。我完全明白你的例子。但它还没有解决我的问题。如果我有另一个处理主要响应的处理程序而不是“time.After(4s)”,这是很重的负载。如果用户关闭他的浏览器,它仍然会运行至少一个完整的时间。因为我必须在 select 语句之前调用它。因此,在选择语句之前,“heavyLoad”函数或处理程序已经得到处理。紧随其后的是选择,如果浏览器关闭,这将是逻辑......对我来说仍然没有 100% 的意义。
    • 对我来说唯一有意义的解决方案是,在另一个 goroutine 中调用“重载”函数。从 goroutine 获取进程 ID(不知道这是否可能)。现在 Select 会在 goroutine 被触发后立即运行,如果浏览器取消,goroutine 的进程必须被杀死
    • 您将上下文传递给heavyLoad,而heavyLoad 必须检查是否取消。如果没有heavyLoad 的合作,您将无法度过难关。这也是一件好事。想象一下,heavyLoad 更新一些数据库,然后发送一封电子邮件,通知用户更新。在数据库更新后,您不想再中断它,即使客户端放弃了。你不能“从外部”做到这一点。 play.golang.org/p/cnZ9DYW62jH
    • 你是对的!感谢您的帮助皮特 ;)
    猜你喜欢
    • 2021-06-30
    • 2013-04-14
    • 1970-01-01
    • 1970-01-01
    • 2019-06-01
    • 2011-06-01
    • 2014-10-26
    • 2017-09-07
    • 1970-01-01
    相关资源
    最近更新 更多