获取 Go 中传输的连接数
Get number of connections of a Transport in Go
我正在为 Go 中的反向代理实现负载平衡算法。
当然,round-robin 很简单,但我正在努力实现 Least connections。
我找不到检查从代理到后端的连接数的方法。
有没有办法从传输或任何其他底层结构中获取打开的连接数?
你可以试试 hlts2/least-connections
,一个用 golang 编写的最少连接数平衡算法。
示例:
lc, err := New([]*url.URL{
{Host: "192.168.33.10"},
{Host: "192.168.33.11"},
{Host: "192.168.33.12"},
})
src1, done1 := lc.Next() // {Host: "192.168.33.10"}
src2, done2 := lc.Next() // {Host: "192.168.33.11"}
done1() // Reduce connection of src1
src3, done3 := lc.Next() // {Host: "192.168.33.10"}
它确实使用了 sync.Mutex
type leastConnections struct {
conns []conn
mu *sync.Mutex
}
另一个更复杂的例子:panjf2000/gnet
presented in "Releasing a high-performance and lightweight event-loop networking library for Go", by Andy Pan.
gnet is an event-driven networking framework that is fast and lightweight.
Supporting multiple load-balancing algorithms: Round-Robin, Source Addr Hash and Least-Connections
// start a server
// connect 10 clients
// each client will pipe random data for 1-3 seconds.
// the writes to the server will be random sizes. 0KB - 1MB.
// the server will echo back the data.
// waits for graceful connection closing.
t.Run("poll", func(t *testing.T) {
t.Run("tcp", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
testServe("tcp", ":9991", false, false, false, 10, RoundRobin)
})
t.Run("N-loop", func(t *testing.T) {
testServe("tcp", ":9992", false, true, false, 10, LeastConnections)
})
})
同样,structure is straightforward:
// leastConnectionsEventLoopSet with Least-Connections algorithm.
leastConnectionsEventLoopSet struct {
sync.RWMutex
minHeap minEventLoopHeap
cachedRoot *eventloop
threshold int32
calibrateConnsThreshold int32
}
您可以将 DialContext 的字段包装在 *http.Transport 中,以从 Transport 中获取打开的连接数。
这是我的示例代码:https://gist.github.com/HattieWebb/2000c514b37a6a0bb5e8b55fadbe3433
// do not support if DialTLS is set.
func NewTransportWithConnectNum(old *http.Transport) *Transport{
connectCounter:=&connectCounter_t{}
tran:=&Transport{
Transport: old,
connectCounter: connectCounter,
}
oldDialer:=tran.getOldDialer()
tran.Transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error){
conn1,err:=oldDialer(ctx,network,addr)
if err!=nil{
return nil,err
}
connectCounter.add(1)
conn2:=&connWithCounter{
Conn: conn1,
connectCounter: connectCounter,
}
return conn2,nil
}
return tran
}
type Transport struct{
*http.Transport
connectCounter *connectCounter_t
}
func (tran *Transport) GetConnectionNum() int{
return tran.connectCounter.get()
}
var zeroDialer net.Dialer
func (tran *Transport) getOldDialer() func(ctx context.Context, network, addr string) (net.Conn, error){
if tran.DialContext!=nil{
return tran.DialContext
}
if tran.Dial!=nil{
return func(ctx context.Context, network, addr string) (net.Conn, error){
return tran.Dial(network,addr)
}
}
return zeroDialer.DialContext
}
type connectCounter_t struct{
connectNum int
connectNumLocker sync.Mutex
}
func (c *connectCounter_t) add(num int){
c.connectNumLocker.Lock()
c.connectNum+=num
c.connectNumLocker.Unlock()
}
func (c *connectCounter_t) get()(num int){
c.connectNumLocker.Lock()
num = c.connectNum
c.connectNumLocker.Unlock()
return num
}
type connWithCounter struct{
net.Conn
closeCounterSyncOnce sync.Once
connectCounter *connectCounter_t
}
func (conn *connWithCounter) Close() (err error){
err = conn.Conn.Close()
conn.closeCounterSyncOnce.Do(func(){
conn.connectCounter.add(-1)
})
return err
}
我正在为 Go 中的反向代理实现负载平衡算法。
当然,round-robin 很简单,但我正在努力实现 Least connections。
我找不到检查从代理到后端的连接数的方法。
有没有办法从传输或任何其他底层结构中获取打开的连接数?
你可以试试 hlts2/least-connections
,一个用 golang 编写的最少连接数平衡算法。
示例:
lc, err := New([]*url.URL{
{Host: "192.168.33.10"},
{Host: "192.168.33.11"},
{Host: "192.168.33.12"},
})
src1, done1 := lc.Next() // {Host: "192.168.33.10"}
src2, done2 := lc.Next() // {Host: "192.168.33.11"}
done1() // Reduce connection of src1
src3, done3 := lc.Next() // {Host: "192.168.33.10"}
它确实使用了 sync.Mutex
type leastConnections struct {
conns []conn
mu *sync.Mutex
}
另一个更复杂的例子:panjf2000/gnet
presented in "Releasing a high-performance and lightweight event-loop networking library for Go", by Andy Pan.
gnet is an event-driven networking framework that is fast and lightweight.
Supporting multiple load-balancing algorithms: Round-Robin, Source Addr Hash and Least-Connections
// start a server
// connect 10 clients
// each client will pipe random data for 1-3 seconds.
// the writes to the server will be random sizes. 0KB - 1MB.
// the server will echo back the data.
// waits for graceful connection closing.
t.Run("poll", func(t *testing.T) {
t.Run("tcp", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
testServe("tcp", ":9991", false, false, false, 10, RoundRobin)
})
t.Run("N-loop", func(t *testing.T) {
testServe("tcp", ":9992", false, true, false, 10, LeastConnections)
})
})
同样,structure is straightforward:
// leastConnectionsEventLoopSet with Least-Connections algorithm.
leastConnectionsEventLoopSet struct {
sync.RWMutex
minHeap minEventLoopHeap
cachedRoot *eventloop
threshold int32
calibrateConnsThreshold int32
}
您可以将 DialContext 的字段包装在 *http.Transport 中,以从 Transport 中获取打开的连接数。
这是我的示例代码:https://gist.github.com/HattieWebb/2000c514b37a6a0bb5e8b55fadbe3433
// do not support if DialTLS is set.
func NewTransportWithConnectNum(old *http.Transport) *Transport{
connectCounter:=&connectCounter_t{}
tran:=&Transport{
Transport: old,
connectCounter: connectCounter,
}
oldDialer:=tran.getOldDialer()
tran.Transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error){
conn1,err:=oldDialer(ctx,network,addr)
if err!=nil{
return nil,err
}
connectCounter.add(1)
conn2:=&connWithCounter{
Conn: conn1,
connectCounter: connectCounter,
}
return conn2,nil
}
return tran
}
type Transport struct{
*http.Transport
connectCounter *connectCounter_t
}
func (tran *Transport) GetConnectionNum() int{
return tran.connectCounter.get()
}
var zeroDialer net.Dialer
func (tran *Transport) getOldDialer() func(ctx context.Context, network, addr string) (net.Conn, error){
if tran.DialContext!=nil{
return tran.DialContext
}
if tran.Dial!=nil{
return func(ctx context.Context, network, addr string) (net.Conn, error){
return tran.Dial(network,addr)
}
}
return zeroDialer.DialContext
}
type connectCounter_t struct{
connectNum int
connectNumLocker sync.Mutex
}
func (c *connectCounter_t) add(num int){
c.connectNumLocker.Lock()
c.connectNum+=num
c.connectNumLocker.Unlock()
}
func (c *connectCounter_t) get()(num int){
c.connectNumLocker.Lock()
num = c.connectNum
c.connectNumLocker.Unlock()
return num
}
type connWithCounter struct{
net.Conn
closeCounterSyncOnce sync.Once
connectCounter *connectCounter_t
}
func (conn *connWithCounter) Close() (err error){
err = conn.Conn.Close()
conn.closeCounterSyncOnce.Do(func(){
conn.connectCounter.add(-1)
})
return err
}