调整/配置 HTTP 服务器

http.Serverhttp.Serverhttp.ListenAndServe()http.Server
func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}
Server.ListenAndServe()http.Server
Server.Serve()

Serve 接受 Listener l 上的传入连接, 为每个. 服务 goroutine 读取请求,然后调用 srv.Handler 来回复它们。服务总是返回一个非零错误。

因此,每个传入的 HTTP 请求都在其新的 goroutine 中处理,这意味着它们是同时服务的。不幸的是,API 没有记录任何方式来改变它的工作方式。

server.go
2107    func (srv *Server) Serve(l net.Listener) error {
2108        defer l.Close()
2109        if fn := testHookServerServe; fn != nil {
2110            fn(srv, l)
2111        }
2112        var tempDelay time.Duration // how long to sleep on accept failure
2113        if err := srv.setupHTTP2(); err != nil {
2114            return err
2115        }
2116        for {
2117            rw, e := l.Accept()
2118            if e != nil {
2119                if ne, ok := e.(net.Error); ok && ne.Temporary() {
2120                    if tempDelay == 0 {
2121                        tempDelay = 5 * time.Millisecond
2122                    } else {
2123                        tempDelay *= 2
2124                    }
2125                    if max := 1 * time.Second; tempDelay > max {
2126                        tempDelay = max
2127                    }
2128                    srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
2129                    time.Sleep(tempDelay)
2130                    continue
2131                }
2132                return e
2133            }
2134            tempDelay = 0
2135            c := srv.newConn(rw)
2136            c.setState(c.rwc, StateNew) // before Serve can return
2137            go c.serve()
2138        }
2139    }

正如你在 #2137 行中看到的,连接是在一个新的 goroutine 上 无条件地 服务的,所以你无能为力。

限制“worker”goroutines

如果你想限制服务请求的 goroutines 的数量,你仍然可以这样做。

您可以在多个级别上限制它们。有关听众级别的限制,请参阅 Darigaaz 的回答。要限制 Handler 级别,请继续阅读。

http.Handlerhttp.HandlerFunc

这种限制同步代码有许多结构。一个例子可能是:创建一个缓冲通道,其容量是您想要的限制。每个处理程序应首先在此通道上发送一个值,然后执行工作。当处理程序返回时,它必须从通道接收一个值:因此最好在延迟函数中完成(不要忘记“清理”自身)。

如果缓冲区已满,则尝试在通道上发送的新请求将阻塞:等到请求完成其工作。

请注意,您不必向所有处理程序注入此限制代码,您可以使用“中间件”模式,一种包装处理程序的新处理程序类型,执行此限制同步工作,并在中间调用包装处理程序其中。

在处理程序中进行限制(相对于在侦听器中进行限制)的优点是,在处理程序中我们知道处理程序在做什么,因此我们可以进行 选择性 限制(例如,我们可以选择限制某些请求,例如数据库操作,而不是限制其他人,例如提供静态资源)或者我们可以根据我们的需要任意创建 多个不同的限制组 (例如,将并发数据库请求限制为最多 10 个,将静态请求限制为最多 100 个,将繁重的计算请求限制为最多 3 个)等等。我们还可以轻松实现登录/付费用户的无限(或上限)和匿名/非付费用户的下限等限制。

http.ListenAndServe()Server.ListenAndServe()http.ServeMux
http.ListenAndServe()http``http.DefaultServeMux
func fooHandler(w http.ResponseWriter, r *http.Request) {
    log.Println("Foo called...")
    time.Sleep(3 * time.Second)
    w.Write([]byte("I'm Foo"))
    log.Println("Foo ended.")
}

func barHandler(w http.ResponseWriter, r *http.Request) {
    log.Println("Bar called...")
    time.Sleep(3 * time.Second)
    w.Write([]byte("I'm Bar"))
    log.Println("Bar ended.")
}

var ch = make(chan struct{}, 2) // 2 concurrent requests

func mainHandler(w http.ResponseWriter, r *http.Request) {
    ch <- struct{}{}
    defer func() {
        <-ch
    }()

    http.DefaultServeMux.ServeHTTP(w, r)
}

func main() {
    http.HandleFunc("/foo", fooHandler)
    http.HandleFunc("/bar", barHandler)

    panic(http.ListenAndServe(":8080", http.HandlerFunc(mainHandler)))
}

部署

用 Go 编写的 Web 应用程序不需要外部服务器来控制进程,因为 Go 网络服务器本身会同时处理请求。

因此,您可以按原样启动用 Go 编写的网络服务器:Go 网络服务器已准备好生产。

如果您愿意,您当然可以使用其他服务器来执行其他任务(例如 HTTPS 处理、身份验证/授权、路由、多个服务器之间的负载平衡)。