猿问

Go Web 服务器的进程管理

我是一名新的 Go 程序员,来自 Web 应用程序和服务开发领域。抱歉,这是一个herp de-derp问题,但我在谷歌上搜索答案没有找到任何东西。此外,这是边缘服务器故障领域,但由于我对 API/编程接口更感兴趣,所以我在这里询问。


我使用net/http包的内置 Web 服务器编写了一个小型 Go 程序。我正准备部署到生产环境,但我对模型 go Go 的 web-server 的过程以及我应该如何部署有点不清楚。


具体来说——在我习惯的环境(PHP、Ruby、Python)中,我们的应用程序前面有一个 Web 服务器(Apache、Nginx 等),我们将这些 Web 服务器配置为使用一定数量的工作进程/线程,并配置每个线程应处理多少个单独的 HTTP(S) 连接。


我无法找到有关 Go 的 Web 服务器如何处理此问题的信息,或有关如何为 Go Web 服务器扩展/计划扩展的实用信息。


ie -- 如果我有一个简单的程序正在运行,准备好处理一个 HTTP 请求


func main() {

   http.HandleFunc("/", processRequest)

   http.ListenAndServe(":8000", nil)    

}

HandleFunc一次尝试处理多少个连接?或者它会在连接打开时开始阻塞,并且只有在连接关闭后才提供下一个连接?


还是我应该不担心这个并将所有内容都塞进常规程序中?但是如果我这样做了,我如何防止系统被太多的执行线程阻塞?

我基本上是在尝试

  1. 了解go web server的进程模式

  2. 找到 go 的内置功能来调整它,和/或人们正在使用的任何标准包

就像我说的,我很新,所以如果我完全错过了这个情节,请告诉我!


幕布斯6054654
浏览 142回答 2
2回答

哆啦的时光机

调整/配置 HTTP 服务器实现 HTTP 服务器的类型是http.Server. 如果您不http.Server自己创建一个,例如因为您调用了该http.ListenAndServe()函数,则会http.Server为您创建一个底层:func ListenAndServe(addr string, handler Handler) error {&nbsp; &nbsp; server := &Server{Addr: addr, Handler: handler}&nbsp; &nbsp; return server.ListenAndServe()}因此,如果您想 tweek / 自定义 HTTP 服务器,请自己创建一个并自己调用其Server.ListenAndServe()方法。http.Server是一个结构,它的零值是一个有效的配置。查看它的文档它有哪些字段以及您可以调整/配置的内容。HTTP 服务器的“进程管理”记录在Server.Serve():Serve 接受 Listener l 上的传入连接,为每个. 服务 goroutine 读取请求,然后调用 srv.Handler 来回复它们。服务总是返回一个非零错误。因此,每个传入的 HTTP 请求都在其新的 goroutine 中处理,这意味着它们是同时服务的。不幸的是,API 没有记录任何方式来改变它的工作方式。查看当前的实现(Go 1.6.2),也没有未记录的方法可以做到这一点。server.go,当前行 #2107-2139:2107&nbsp; &nbsp; func (srv *Server) Serve(l net.Listener) error {2108&nbsp; &nbsp; &nbsp; &nbsp; defer l.Close()2109&nbsp; &nbsp; &nbsp; &nbsp; if fn := testHookServerServe; fn != nil {2110&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fn(srv, l)2111&nbsp; &nbsp; &nbsp; &nbsp; }2112&nbsp; &nbsp; &nbsp; &nbsp; var tempDelay time.Duration // how long to sleep on accept failure2113&nbsp; &nbsp; &nbsp; &nbsp; if err := srv.setupHTTP2(); err != nil {2114&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err2115&nbsp; &nbsp; &nbsp; &nbsp; }2116&nbsp; &nbsp; &nbsp; &nbsp; for {2117&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rw, e := l.Accept()2118&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if e != nil {2119&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ne, ok := e.(net.Error); ok && ne.Temporary() {2120&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if tempDelay == 0 {2121&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tempDelay = 5 * time.Millisecond2122&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {2123&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tempDelay *= 22124&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }2125&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if max := 1 * time.Second; tempDelay > max {2126&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tempDelay = max2127&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }2128&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)2129&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(tempDelay)2130&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue2131&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }2132&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return e2133&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }2134&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tempDelay = 02135&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c := srv.newConn(rw)2136&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c.setState(c.rwc, StateNew) // before Serve can return2137&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; go c.serve()2138&nbsp; &nbsp; &nbsp; &nbsp; }2139&nbsp; &nbsp; }正如你在第 2137 行看到的,连接是在一个新的 goroutine 上无条件地服务的,所以你无能为力。限制“worker”goroutines如果你想限制服务请求的 goroutines 的数量,你仍然可以这样做。您可以在多个级别上限制它们。有关听众级别的限制,请参阅 Darigaaz 的回答。要限制 Handler 级别,请继续阅读。例如,您可以将代码插入到您的每个http.Handler或处理程序函数 ( http.HandlerFunc) 中,该代码仅在并发请求服务 goroutine 的数量小于指定限制时才会继续。这种限制同步代码有许多结构。一个例子可能是:创建一个缓冲通道,其容量是您想要的限制。每个处理程序应首先在此通道上发送一个值,然后执行工作。当处理程序返回时,它必须从通道接收一个值:因此最好在延迟函数中完成(不要忘记“清理”自身)。如果缓冲区已满,则尝试在通道上发送的新请求将阻塞:等到请求完成其工作。请注意,您不必向所有处理程序注入此限制代码,您可以使用“中间件”模式,一种包装处理程序的新处理程序类型,执行此限制同步工作,并在中间调用包装处理程序其中。在处理程序中进行限制(相对于在侦听器中进行限制)的优点是,在处理程序中我们知道处理程序在做什么,因此我们可以进行选择性限制(例如,我们可以选择限制某些请求,例如数据库操作,而不是限制其他人,例如提供静态资源)或者我们可以根据我们的需要任意创建多个不同的限制组(例如,将并发数据库请求限制为最多 10 个,将静态请求限制为最多 100 个,将繁重的计算请求限制为最多 3 个)等等。我们还可以轻松实现登录/付费用户的无限(或上限)和匿名/非付费用户的下限等限制。另请注意,您甚至可以在一个地方进行速率限制,而无需使用中间件。创建一个“主处理程序”,并将其传递给http.ListenAndServe()(或Server.ListenAndServe())。在这个主处理程序中进行速率限制(例如使用上面提到的缓冲通道),然后简单地将调用转发给http.ServeMux您正在使用的。这是一个简单的示例,它使用包 ( )http.ListenAndServe()的默认多路复用器进行演示。它将并发请求限制为 2:httphttp.DefaultServeMuxfunc fooHandler(w http.ResponseWriter, r *http.Request) {&nbsp; &nbsp; log.Println("Foo called...")&nbsp; &nbsp; time.Sleep(3 * time.Second)&nbsp; &nbsp; w.Write([]byte("I'm Foo"))&nbsp; &nbsp; log.Println("Foo ended.")}func barHandler(w http.ResponseWriter, r *http.Request) {&nbsp; &nbsp; log.Println("Bar called...")&nbsp; &nbsp; time.Sleep(3 * time.Second)&nbsp; &nbsp; w.Write([]byte("I'm Bar"))&nbsp; &nbsp; log.Println("Bar ended.")}var ch = make(chan struct{}, 2) // 2 concurrent requestsfunc mainHandler(w http.ResponseWriter, r *http.Request) {&nbsp; &nbsp; ch <- struct{}{}&nbsp; &nbsp; defer func() {&nbsp; &nbsp; &nbsp; &nbsp; <-ch&nbsp; &nbsp; }()&nbsp; &nbsp; http.DefaultServeMux.ServeHTTP(w, r)}func main() {&nbsp; &nbsp; http.HandleFunc("/foo", fooHandler)&nbsp; &nbsp; http.HandleFunc("/bar", barHandler)&nbsp; &nbsp; panic(http.ListenAndServe(":8080", http.HandlerFunc(mainHandler)))}部署用 Go 编写的 Web 应用程序不需要外部服务器来控制进程,因为 Go 网络服务器本身会同时处理请求。因此,您可以按原样启动用 Go 编写的网络服务器:Go 网络服务器已准备好生产。如果您愿意,您当然可以使用其他服务器执行其他任务(例如 HTTPS 处理、身份验证/授权、路由、多个服务器之间的负载平衡)。

慕神8447489

ListenAndServe使用给定的地址和处理程序启动 HTTP 服务器。处理程序通常为 nil,表示使用DefaultServeMux. 处理HandleFunc并将处理程序添加到DefaultServeMux.查看http.Server,很多字段都是可选的,并且可以使用默认值正常工作。现在让我们看看http.ListenAndServe,一点也不难func ListenAndServe(addr string, handler Handler) error {&nbsp; &nbsp; server := &Server{Addr: addr, Handler: handler}&nbsp; &nbsp; return server.ListenAndServe()}所以默认服务器的创建非常简单。func (srv *Server) ListenAndServe() error {&nbsp; &nbsp; addr := srv.Addr&nbsp; &nbsp; if addr == "" {&nbsp; &nbsp; &nbsp; &nbsp; addr = ":http"&nbsp; &nbsp; }&nbsp; &nbsp; ln, err := net.Listen("tcp", addr)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; }&nbsp; &nbsp; return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})&nbsp;}func (srv *Server) Serve(l net.Listener) error {&nbsp; &nbsp; defer l.Close()&nbsp; &nbsp; if fn := testHookServerServe; fn != nil {&nbsp; &nbsp; &nbsp; &nbsp; fn(srv, l)&nbsp; &nbsp; }&nbsp; &nbsp; var tempDelay time.Duration // how long to sleep on accept failure&nbsp; &nbsp; if err := srv.setupHTTP2(); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; }&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; rw, e := l.Accept()&nbsp; &nbsp; &nbsp; &nbsp; if e != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ne, ok := e.(net.Error); ok && ne.Temporary() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if tempDelay == 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tempDelay = 5 * time.Millisecond&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tempDelay *= 2&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if max := 1 * time.Second; tempDelay > max {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tempDelay = max&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(tempDelay)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return e&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; tempDelay = 0&nbsp; &nbsp; &nbsp; &nbsp; c := srv.newConn(rw)&nbsp; &nbsp; &nbsp; &nbsp; c.setState(c.rwc, StateNew) // before Serve can return&nbsp; &nbsp; &nbsp; &nbsp; go c.serve()&nbsp; &nbsp; }}它侦听“addr”并接受每个连接,然后生成一个 goroutine 来独立处理每个连接。(HTTP/2.0 有点不同,但大体上是一样的)。如果您想控制连接,您有 2 个选项:使用服务器创建自定义服务器(它的 3 行代码)。ConnState从那里回调和控制客户端连接。(但无论如何它们都会被内核接受)使用您自己的net.Listener(like&nbsp;LimitedListener) 实现创建自定义服务器并从那里控制连接,这样您将拥有对连接的最终控制权。由于默认http.Server无法停止,因此第二种方式是优雅终止侦听器的唯一方法。您可以结合两种方法来实现不同的策略,这已经完成了。
随时随地看视频慕课网APP

相关分类

Go
我要回答