You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Go中handler并发连接数统计与反向代理负载均衡实现咨询

如何统计特定Handler的并发连接数并实现基于后端并发的负载均衡?

嘿,我来帮你搞定这两个需求——统计handleForServer2的并发连接数,以及基于后端服务器的并发数实现负载均衡。咱们一步步拆解:


一、统计handleForServer2的并发连接数

要统计单个Handler的并发请求数,核心是用线程安全的计数器,Go标准库的sync/atomic包就能完美解决这个问题,它能保证多goroutine下的计数准确性。

实现步骤:

  1. 先定义一个全局的原子计数器:
import "sync/atomic"

// 专门统计handleForServer2的并发连接数
var server2ActiveConns atomic.Int32
  1. 修改handleForServer2函数,在请求进入和结束时更新计数器:
func handleForServer2(res http.ResponseWriter, req *http.Request) {
    // 请求开始,并发数+1
    server2ActiveConns.Add(1)
    // 用defer确保无论请求成功/失败,计数器都会减1
    defer server2ActiveConns.Add(-1)

    rPayload := parseRequestBody(req)
    serve("http://server2:8080", res, req)
}

之后你随时可以通过server2ActiveConns.Load()获取当前handleForServer2的实时并发数。


二、基于后端并发连接数实现负载均衡

你现在的代码是固定转发到指定后端,要实现基于并发数的负载均衡,需要先把后端服务器的信息和并发状态管理起来,再写一个“选最少并发后端”的逻辑。

实现步骤:

  1. 定义后端服务器的结构体,用来存地址、并发计数器和预初始化的反向代理:
import (
    "net/http/httputil"
    "net/url"
    "sync/atomic"
    "math"
)

type BackendServer struct {
    Address string
    Conns   atomic.Int32 // 该后端的当前并发连接数
    Proxy   *httputil.ReverseProxy
}

// 初始化后端列表
var backendList = []*BackendServer{
    {Address: "http://server1:8080"},
    {Address: "http://server2:8080"},
}

// 提前初始化每个后端的反向代理,避免重复创建浪费资源
func init() {
    for _, backend := range backendList {
        u, _ := url.Parse(backend.Address)
        backend.Proxy = httputil.NewSingleHostReverseProxy(u)
    }
}
  1. 写一个负载均衡器函数,选择当前并发数最少的后端:
func pickLeastConnBackend() *BackendServer {
    var selected *BackendServer
    minConnCount := int32(math.MaxInt32)

    // 遍历所有后端,找到并发数最低的那个
    for _, backend := range backendList {
        currentConns := backend.Conns.Load()
        if currentConns < minConnCount {
            minConnCount = currentConns
            selected = backend
        }
    }
    return selected
}
  1. 修改代理逻辑,用上负载均衡和并发计数:
// 现在可以用一个统一的Handler处理所有代理请求
func handleProxyRequest(res http.ResponseWriter, req *http.Request) {
    backend := pickLeastConnBackend()
    if backend == nil {
        http.Error(res, "没有可用的后端服务器", http.StatusServiceUnavailable)
        return
    }

    // 并发数+1
    backend.Conns.Add(1)
    defer backend.Conns.Add(-1)

    rPayload := parseRequestBody(req)
    // 调整请求头和URL信息
    req.URL.Host = backend.Address[7:] // 假设地址是http://开头,截取host部分
    req.URL.Scheme = "http"
    req.Header.Set("X-Forwarded-Host", req.Header.Get("Host"))
    req.Host = backend.Address[7:]

    // 用预初始化的代理处理请求
    backend.Proxy.ServeHTTP(res, req)
}

注意细节:

  • init()提前初始化反向代理,避免每次请求都重复创建,提升性能
  • 选择后端的逻辑是贪心策略,简单高效;如果需要更复杂的规则(比如加权、平滑加权),可以在此基础上扩展
  • 务必用defer来递减计数器,确保即使请求处理中出现panic或错误,计数器也能正确回退,避免统计失真

内容的提问来源于stack exchange,提问作者Gon

火山引擎 最新活动