Исправьте неисправные соединения в Go - PullRequest
0 голосов
/ 25 апреля 2018

У меня есть клиент, который отправляет (получает) данные на (с) сервера.Код клиента похож на:

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.Это будет означать, что методы write и read в цикле for ничего не сделают и просто вернут error.Даже если сервер снова включится через несколько секунд, клиентский код цикла 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 перед работой с читателями / писателями.

Кроме того, есть лучший способ решить мою вышеупомянутую проблему.А именно, снова подключиться к серверу, когда сервер снова включится?Обратите внимание, что сервер может принимать подключения от нескольких клиентов в любой момент времени.

Ответы [ 2 ]

0 голосов
/ 27 апреля 2018

Вот один из способов:

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 или envoy)

0 голосов
/ 25 апреля 2018

Может просто проверить на ошибки, что-то вроде этого

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()
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...