我试图创建一个程序,作为代理服务器的功能,可以动态切换到新的端点。但我面临的问题是,在调用switchOverToNewEndpoint()
后,仍然有一些代理对象连接到原始端点8.8.8.8
,这些端点应该被关闭。
package main
import (
"net"
"sync"
"sync/atomic"
"time"
)
type Proxy struct {
ID int32
From, To *net.TCPConn
}
var switchOver int32 = 0
func SetSwitchOver() {
atomic.StoreInt32((*int32)(&switchOver), 1)
}
func SwitchOverEnabled() bool {
return atomic.LoadInt32((*int32)(&switchOver)) == 1
}
var proxies map[int32]*Proxy = make(map[int32]*Proxy, 0)
var proxySeq int32 = 0
var mu sync.RWMutex
func addProxy(from *net.TCPConn) {
mu.Lock()
proxySeq += 1
proxy := &Proxy{ID: proxySeq, From: from}
proxies[proxySeq] = proxy
mu.Unlock()
var toAddr string
if SwitchOverEnabled() {
toAddr = "1.1.1.1"
} else {
toAddr = "8.8.8.8"
}
tcpAddr, _ := net.ResolveTCPAddr("tcp4", toAddr)
toConn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
panic(err)
}
proxy.To = toConn
}
func switchOverToNewEndpoint() {
mu.RLock()
closedProxies := proxies
mu.RUnlock()
SetSwitchOver()
for _, proxy := range closedProxies {
proxy.From.Close()
proxy.To.Close()
mu.Lock()
delete(proxies, proxy.ID)
mu.Unlock()
}
}
func main() {
tcpAddr, _ := net.ResolveTCPAddr("tcp4", "0.0.0.0:5432")
ln, _ := net.ListenTCP("tcp", tcpAddr)
go func() {
time.Sleep(time.Second * 30)
switchOverToNewEndpoint()
}()
for {
clientConn, err := ln.AcceptTCP()
if err != nil {
panic(err)
}
go addProxy(clientConn)
}
}
想了一会儿,我猜问题出在
mu.RLock()
closedProxies := proxies
mu.RUnlock()
但我不确定这是否是根本原因,以及是否可以通过将其替换为以下内容来修复它:
closedProxies := make([]*Proxy, 0)
mu.RLock()
for _, proxy := range proxies {
closedProxies = append(closedProxies, proxy)
}
mu.RUnlock()
由于这个案例很难重现,所以有专业知识的人可以提供一个想法或提示吗?任何意见都欢迎。提前感谢。
1条答案
按热度按时间bihw5rsg1#
问题
这一改变是必要的。在原始实现中,
closedProxies
持有相同的map。请参见此演示:但这不是根本原因,可能是在复制
closeProxies
之后,调用SetSwitchOver
之前,添加了一个新的代理。在这种情况下,新的代理连接到旧地址,但不在closeProxies
中。我认为这是根本原因。还有另一个问题。在
To
字段设置之前,一个新的代理被添加到proxies
。可能发生的情况是,程序希望在To
字段设置之前关闭这个代理,并导致死机。可靠的设计
其思路是将所有的端点都放到一个切片中,让每个端点管理自己的代理列表,这样我们只需要随时跟踪当前端点的索引,当我们想切换到另一个端点时,只需要更改索引并告诉过时的端点清除其代理。剩下的唯一复杂的事情是确保过时的端点可以清除其所有代理。参见以下实施:
管理员。开始
这就是思想的落实。
main.go
本demo演示了如何使用之前实现的Manager类型:
manager_test.go
使用以下命令运行测试:
go test ./... -race -count 10