【问题标题】:How to limit the connections count of an HTTP Server implemented in Go?如何限制 Go 中实现的 HTTP 服务器的连接数?
【发布时间】:2014-03-25 04:19:40
【问题描述】:

我正在尝试在 Golang 中实现 HTTP 服务器。

我的问题是,我必须将任何特定时间的最大活动连接数限制为 20。

【问题讨论】:

标签: go httpserver


【解决方案1】:

如果您不想实现自己的包装器,可以使用netutil.LimitListener 函数来包装net.Listener:-

connectionCount := 20

l, err := net.Listen("tcp", ":8000")

if err != nil {
    log.Fatalf("Listen: %v", err)
}

defer l.Close()

l = netutil.LimitListener(l, connectionCount)

log.Fatal(http.Serve(l, nil))

【讨论】:

  • 这是否消除了http.ListenAndServe 的正常tcpKeepAliveListener 行为?
【解决方案2】:

这个技巧是实现你自己的net.Listener。我有一个监听器 here 的示例(请参阅 waitConn 和 WaitListener),它跟踪连接(但不限制它们),您可以将其用作实现的灵感。它的形状是这样的:

type LimitedListener struct {
    sync.Mutex
    net.Listener
    sem chan bool
}

func NewLimitedListener(count int, l net.Listener) *net.LimitedListener {
    sem := make(chan bool, count)
    for i := 0; i < count; i++ {
        sem <- true
    }
    return &net.LimitedListener{
        Listener: l,
        sem:      sem,
    }
}

func (l *LimitedListener) Addr() net.Addr { /* ... */ }
func (l *LimitedListener) Close() error { /* ... */ }
func (l *LimitedListener) Accept() (net.Conn, err) {
    <-l.sem // acquire
    // l.Listener.Accept (on error, release before returning)
    // wrap LimitedConn
    return c, nil
}

type LimitedConn struct { /* ... */ }

func (c *LimitedConn) Close() error {
    /* ... */
    c.sem <- true // release
}

本质上,这是在创建您自己的 net.Listener 实现,您可以将其提供给 Serve,它仅在获取信号量时调用底层 Accept;只有在(适当包装的)net.Conn 关闭时,才会释放如此获取的信号量。请注意,从技术上讲,信号量的这种使用对于 go1.2 memory model 是正确的;更简单的信号量在 Go 的 future versions 中是合法的。

【讨论】:

  • 感谢您的回复。你能告诉我如何监控服务器(查看活动空闲连接数)???
  • 存储原始计数,并从中减去 len(l.sem) 以获得(即时)有多少连接等待关闭。
  • 这可能不安全。我偶尔会看到来自 net/http/Server 的同一连接上对 Close() 的多次调用。根据 io.Closer 接口,这是未定义的行为,并且在此实现中,当尝试释放(从 chan 读取)时,它会导致 Close() 调用阻塞。考虑改用另一个答案中描述的 netutil.LimitListener ;它有一个包装器,以确保每个连接只读取一次通道。 (尽管它仍然将重复的 Close 调用传递给底层的 Listener,这仍然是未定义的行为。)
【解决方案3】:

借助通道,您可以限制活动连接数。

1.在服务器启动时创建一个通道并将相同数量的限制计数(在您的情况下为 20)值放入通道中。

2.在服务一个请求时从通道中删除一个值。

来自网络的一个例子

type limitHandler struct {
    connc   chan struct{}
    handler http.Handler
}

func (h *limitHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    select {
    case <-connc:
        h.handler.ServeHTTP(w, req)
        connc <- struct{}{}
    default:
        http.Error(w, "503 too busy", StatusServiceUnavailable)
    }
}

func NewLimitHandler(maxConns int, handler http.Handler) http.Handler {
    h := &limitHandler{
        connc:   make(chan struct{}, maxConns),
        handler: handler,
    }
    for i := 0; i < maxConns; i++ {
        connc <- struct{}{}
    }
    return h
}

【讨论】:

  • connc &lt;- struct{}{} 应该是defer-ed。然后,如果发生恐慌(从其他地方恢复),连接“插槽”仍会返回到池中。
  • 从技术上讲,这不会限制传入连接,它只会限制您一次发送的响应数。这是一个有用的模式,尽管可能基于例如加载而不是绝对连接数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-26
  • 1970-01-01
  • 2016-05-20
  • 2022-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多