Go中handler并发连接数统计与反向代理负载均衡实现咨询
如何统计特定Handler的并发连接数并实现基于后端并发的负载均衡?
嘿,我来帮你搞定这两个需求——统计handleForServer2的并发连接数,以及基于后端服务器的并发数实现负载均衡。咱们一步步拆解:
一、统计handleForServer2的并发连接数
要统计单个Handler的并发请求数,核心是用线程安全的计数器,Go标准库的sync/atomic包就能完美解决这个问题,它能保证多goroutine下的计数准确性。
实现步骤:
- 先定义一个全局的原子计数器:
import "sync/atomic" // 专门统计handleForServer2的并发连接数 var server2ActiveConns atomic.Int32
- 修改
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的实时并发数。
二、基于后端并发连接数实现负载均衡
你现在的代码是固定转发到指定后端,要实现基于并发数的负载均衡,需要先把后端服务器的信息和并发状态管理起来,再写一个“选最少并发后端”的逻辑。
实现步骤:
- 定义后端服务器的结构体,用来存地址、并发计数器和预初始化的反向代理:
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) } }
- 写一个负载均衡器函数,选择当前并发数最少的后端:
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 }
- 修改代理逻辑,用上负载均衡和并发计数:
// 现在可以用一个统一的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




