当前位置: 首页 > 面试题库 >

Go Web服务器的流程管理

索嘉胜
2023-03-14
问题内容

我是一位来自Web应用程序和服务开发领域的新Go程序员。不好意思,这是一个关于derp的问题,但是我一直在寻找答案没有发现任何问题。另外,这是边界服务器故障区域,但是由于我对API
/编程接口更感兴趣,所以我在这里问。

我已经使用net/http软件包的内置Web服务器编写了一个小型go程序。我已经准备好将其部署到生产环境,但是在Go的Web服务器的模型流程以及部署方式方面我还不清楚。

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

我还没有找到有关Go的Web服务器如何处理此信息的信息,也没有找到有关如何为Go Web服务器缩放/规划规模的实用信息。

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

func main() {
   http.HandleFunc("/", processRequest)
   http.ListenAndServe(":8000", nil)    
}

HandleFunc一次尝试处理多少个连接?还是在连接打开时开始阻塞,并且仅在连接关闭后才服务下一个连接?

还是我不应该为此担心而将所有事情都塞进去呢?但是,如果这样做,如何防止系统因太多执行线程而陷入困境?

我基本上是想

  1. 了解Go Web服务器的处理模式
  2. 找到go的内置功能来进行调整,和/或其他正在使用的标准软件包

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


问题答案:

调整/配置HTTP服务器

实现HTTP服务器的类型为http.Server。如果您不创建http.Server自己,例如因为调用http.ListenAndServe()函数,那么http.Server将为您创建一个幕后花絮:

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

因此,如果您想周/自定义HTTP服务器,请自己创建一个并自己调用其Server.ListenAndServe()方法。http.Server是一个结构,其零值为有效配置。查看其文档,其中包含哪些字段,以及可以进行哪些调整/配置。

HTTP服务器的“进程管理”记录在Server.Serve()

服务在侦听器l上接受传入的连接, 为每个服务创建一个新的服务goroutine
。服务goroutine读取请求,然后调用srv.Handler对其进行回复。服务始终返回非零错误。

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

并查看当前的实现(Go
1.6.2),也没有未记录的方法来实现。server.go,目前第2107-2139行:

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上 无条件 提供的,因此您无能为力。

限制“工人” goroutines

如果您想限制为请求提供服务的goroutine的数量,您仍然可以这样做。

您可以将它们限制在多个级别。有关限制听众级别的信息,请参见Darigaaz的答案。要限制处理程序级别,请继续阅读。

例如,您可以在每个http.Handler或处理程序函数(http.HandlerFunc)中插入代码,仅当并发请求服务goroutine的数量小于指定的限制时,该代码才会继续执行。

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

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

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

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

还要注意,您甚至可以在一个位置进行速率限制,而无需使用中间件。创建一个“主处理程序”,并将其传递给http.ListenAndServe()(或Server.ListenAndServe())。在此主处理程序中进行速率限制(例如,使用如上所述的缓冲通道),然后简单地将调用转发给http.ServeMux您正在使用的对象。

这是一个简单的示例,它使用包()http.ListenAndServe()的默认多路复用器进行演示。它将并发请求限制为2: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 Web服务器本身可以同时处理请求。

因此,您可以按原样启动以Go语言编写的Web服务器:Go Web服务器已准备好投入生产。

当然,您可以根据需要将其他服务器用于其他任务(例如HTTPS处理,身份验证/授权,路由,多个服务器之间的负载平衡)。



 类似资料:
  • goweb 是 Go 语言实现的一个轻量级的 RESTful 服务框架。

  • 我正在使用docker部署spring云数据流服务器。我在dataflow服务器内部创建了一个数据处理管道,通过部署两个spring boot应用程序作为源、处理器和接收器。为了访问每个服务的日志,我必须从docker continer(bash)内部跟踪它,或者将其从docker容器复制到本地磁盘。 我想使用log4j-kafka appender将这些日志推送给kafka以供以后分析。我已经为

  • 介绍了E立方管理平台服务器如何设置工作流,当业务流程到“审批”以后,如果业务员发现产品选错了,或者是经理审批时发现的,这时都需要业务员去纠正所订购的产品。但是,流程驱动的单据不能象一般单据那样随时都可以修改,而是必须在相应的任务上才能修改。由于林经理没有权限去修改订货单上的产品信息,所以必须把单据“回退”给业务员(也就是回退到“制单”),业务员修改后重新传递给他。    经理先在待处理任务中点击“

  • 本小节介绍Tornado HTTP服务器的基本流程,分别分析httpserver, ioloop, iostream模块的代码来剖析Tornado底层I/O的内部实现。 httpserver.py中给出了一个简单的http服务器的demo,代码如下所示: from tornado import httpserver from tornado import ioloop def handle_

  • 服务治理流程 通过配置,在启动服务时,将服务自身信息自动注册到注册中心。注册中心保存着可用的服务,并且会启1个进程对服务进行健康检查,如果服务异常,自动摘除节点,并通知所有客户端。 客户端通过注册中心订阅服务,并做缓存。若服务故障,将启动故障切换。 客户端发起请求至服务端进行接口调用。 若尝试微服务化,可优化的点 网关层尚未完善,路由与过滤。 客户端配合注册中心实现服务降级、熔断等等 客户端可提供

  • 运行python后manage.pyrunserver0.0.0.0:8000 我收到了这个错误消息 我已经签出/django/contrib/auth/management/init。py和get_permission_codename函数存在,所以我看不出为什么不导入它。