修复Go中的故障连接
我有一个客户端正在向服务器发送(接收)数据。客户机的代码类似于:修复Go中的故障连接,go,server,client,Go,Server,Client,我有一个客户端正在向服务器发送(接收)数据。客户机的代码类似于: conn, _ := net.Dial("tcp", "127.0.0.1:3456") reader := bufio.NewReader(conn) writer := bufio.NewWriter(conn) for true { writer.write(data) reader.read() } 现在,假设服务器崩溃经常导致conn出现故障。这意味着for循环中的write和read方法不会执行任何操作
conn, _ := net.Dial("tcp", "127.0.0.1:3456")
reader := bufio.NewReader(conn)
writer := bufio.NewWriter(conn)
for true {
writer.write(data)
reader.read()
}
现在,假设服务器崩溃经常导致conn
出现故障。这意味着for
循环中的write
和read
方法不会执行任何操作,只会返回错误。即使服务器在接下来的几秒钟内再次启动,客户端的for
循环代码也将无法与服务器通信,因为conn
出现故障
我试图实现的是:让客户端在服务器重新启动时继续运行。为此,我考虑了以下方法:
func fixConnection(conn *net.Conn, reader **[]bufio.Reader, writer **[]bufio.Writer) net.Conn {
for true {
oneByte := make([] byte, 1, 1)
reader := bufio.NewReader(*conn)
_, err := reader.Read(oneByte)
if err != nil {
for true {
var tmpConn net.Conn
tmpConn, err = net.Dial("tcp", "127.0.0.1:3456")
if err == nil {
*conn = tmpConn
*reader = bufio.NewReader(*conn)
*writer = bufio.NewWriter(*conn)
}
time.Sleep(time.Millisecond * 100)
}
} else {
reader.UnreadByte()
time.Sleep(time.Millisecond * 500)
continue
}
}
}
然后我只在客户端添加一行:
conn, _ := net.Dial("tcp", "127.0.0.1:3456")
reader := bufio.NewReader(conn)
writer := bufio.NewWriter(conn)
// new line
go fixConnection(&conn, &reader, &writer)
for true {
writer.write(data)
reader.read()
}
我的方法至少有一个问题:bufio
不是线程安全的,因此当fixConnection
正在更改读卡器(写入器)时,可能会出现问题。在对读写器进行操作之前,是否有一种方法可以解决此问题,而无需使用sync.Mutex
还有,有没有更好的方法来解决我前面提到的问题。也就是说,当服务器重新启动时,是否再次连接到服务器?请注意,服务器可以在任何时间点接受多个客户端的连接。可能只是检查错误,类似这样的情况
func connect(addr string)(*bufio.Reader, *bufio.Writer, Error){
conn, err := net.Dial("tcp", addr) reader := bufio.NewReader(conn)
if err != nill{
return(nil, nil, err)
}
writer := bufio.NewWriter(conn)
reader := bufio.NewReader(conn)
return(reader, writer, nil)
}
for reader, writer, err := connect(adrr);;{
if err !=nil {
reader, writer, err = connect(adrr)
continue
}
_, err = writer.write(data)
err = reader.read()
}
这里有一个方法:
package main
import (
"net"
"sync"
"time"
)
type ReconnectingWriter struct {
Dialer func() (net.Conn, error)
mu sync.RWMutex
conn net.Conn
}
func NewReconnectingWriter(dialer func() (net.Conn, error)) *ReconnectingWriter {
return &ReconnectingWriter{Dialer: dialer}
}
func (rw *ReconnectingWriter) getConn() (net.Conn, error) {
rw.mu.RLock()
conn := rw.conn
rw.mu.RUnlock()
if conn != nil {
return conn, nil
}
rw.mu.Lock()
defer rw.mu.Unlock()
var err error
if rw.conn == nil {
rw.conn, err = rw.Dialer()
}
return rw.conn, err
}
func (rw *ReconnectingWriter) closeConn() {
rw.mu.Lock()
defer rw.mu.Unlock()
if rw.conn != nil {
rw.conn.Close()
rw.conn = nil
}
}
func (rw *ReconnectingWriter) Write(b []byte) (int, error) {
for i := 0; ; i++ {
if i > 0 {
time.Sleep(time.Second) // replace this with exp backoff + jitter
}
// try to get a connection
conn, err := rw.getConn()
if err != nil {
continue
}
// try to write the data
n, err := conn.Write(b)
if err != nil {
rw.closeConn()
continue
}
return n, err
}
}
func main() {
rw := NewReconnectingWriter(func() (net.Conn, error) {
return net.Dial("tcp", "localhost:9000")
})
rw.Write([]byte("hello world"))
}
但是,您不应该这样做,因为最终会导致部分写入。如果这是http服务器,更好的解决方案是使用负载平衡器,因为它可以正确地重放整个请求。(可选择haproxy或Environment)如果一端崩溃,TCP连接将无法维持。你需要一个应用层重新连接功能。是的,这就是我要做的。服务器可以动态地接受一个新的客户端。据我所知,你读写起来并不那么容易。您必须写入并关闭连接以确认,然后重新打开连接并读取,然后等待另一端关闭连接以确认。