Остановить каналы, когда мы не можем подключиться - PullRequest
5 голосов
/ 20 июня 2019

У меня есть следующий код, который работает нормально, проблема в том, что когда socket.Connect() не может подключиться , я хочу остановить процесс, я попытался с помощью следующего кода но это не работает, т.е. если соединение с сокетом не удается подключиться, программа все равно запускается. Я хочу, чтобы в случае сбоя connect процесс останавливался и канал ... что мне здесь не хватает?

func run (appName string) (err error) {


        done = make(chan bool)
        defer close(done)


        serviceURL, e := GetContext().getServiceURL(appName)

        if e != nil {
            err = errors.New("process failed" + err.Error())
            LogDebug("Exiting %v func[err =%v]", methodName, err)
            return err
        }

        url := "wss://" + serviceURL + route


        socket := gowebsocket.New(url)
        addPass(&socket, user, pass)


        socket.OnConnectError = OnConnectErrorHandler
        socket.OnConnected = OnConnectedHandler
        socket.OnTextMessage = socketTextMessageHandler
        socket.OnDisconnected = OnDisconnectedHandler

        LogDebug("In %v func connecting to URL  %v", methodName, url)
        socket.Connect()

        jsonBytes, e := json.Marshal(payload)
        if e != nil {
            err = errors.New("build process failed" + e.Error())
            LogDebug("Exiting %v func[err =%v]", methodName, err)
            return err
        }

        jsonStr := string(jsonBytes)

        LogDebug("In %v Connecting to payload JSON is  %v", methodName, jsonStr)
        socket.SendText(jsonStr)

        <-done
        LogDebug("Exiting %v func[err =%v]", methodName, err)
        return err

    }


    func OnConnectErrorHandler(err error, socket gowebsocket.Socket) {
        methodName := "OnConnectErrorHandler"
        LogDebug("Starting %v parameters [err = %v , socket = %v]", methodName, err, socket)
        LogInfo("Disconnected from server ")
        done <- true
    }

Процесс должен открыть одно соединение ws для процесса, который выполняется около 60-90 сек (например, выполнить install npm install) и получить журналы процесса через web socke t и после его завершения, и, конечно, решить проблему, которая может случается проблема сети или какая-то ошибка при запуске процесса

Ответы [ 2 ]

3 голосов
/ 21 июня 2019

Итак, @Slabgorb верен - если вы посмотрите здесь (https://github.com/sacOO7/GoWebsocket/blob/master/gowebsocket.go#L87), вы увидите, что OnConnectErrorHandler вызывается синхронно во время выполнения вашего вызова к Connect(). Функция Connect() не ' t запустить отдельную процедуру обработки веб-сокета до тех пор, пока соединение не будет полностью установлено и обратный вызов OnConnected не завершен. Поэтому, когда вы пытаетесь записать в небуферизованный канал done, вы блокируете тот же подпрограмма, которая вначале вызывала функцию run(), и вы зашли в тупик, потому что ни одна подпрограмма никогда не сможет читать с канала, чтобы разблокировать вас.

Так что вы могли бы пойти с его решением и превратить его в буферизованный канал, и это сработает, но я бы предложил не записывать в канал для такого рода одноразового поведения флага, а использовать сигнализацию close вместо. Определите канал для каждого условия, которое вы хотите прекратить run(), и в соответствующей функции обработчика веб-сокета, close канал, когда это условие произойдет. Внизу run() вы можете select на всех каналах и выйти, когда первый из них закроется. Это будет выглядеть примерно так:

package main

import "errors"

func run(appName string) (err error) {

    // first, define one channel per socket-closing-reason (DO NOT defer close these channels.)
    connectErrorChan := make(chan struct{})
    successDoneChan := make(chan struct{})
    surpriseDisconnectChan := make(chan struct{})

    // next, wrap calls to your handlers in a closure `https://gobyexample.com/closures`
    // that captures a reference to the channel you care about
    OnConnectErrorHandler := func(err error, socket gowebsocket.Socket) {
        MyOnConnectErrorHandler(connectErrorChan, err, socket)
    }
    OnDisconnectedHandler := func(err error, socket gowebsocket.Socket) {
        MyOnDisconectedHandler(surpriseDisconnectChan, err, socket)
    }
    // ... declare any other handlers that might close the connection here

    // Do your setup logic here
    // serviceURL, e := GetContext().getServiceURL(appName)
    // . . .
    // socket := gowebsocket.New(url)

    socket.OnConnectError = OnConnectErrorHandler
    socket.OnConnected = OnConnectedHandler
    socket.OnTextMessage = socketTextMessageHandler
    socket.OnDisconnected = OnDisconnectedHandler

    // Prepare and send your message here...
    // LogDebug("In %v func connecting to URL  %v", methodName, url)
    // . . .
    // socket.SendText(jsonStr)

    // now wait for one of your signalling channels to close.
    select { // this will block until one of the handlers signals an exit
    case <-connectError:
        err = errors.New("never connected  :( ")
    case <-successDone:
        socket.Close()
        LogDebug("mission accomplished! :) ")
    case <-surpriseDisconnect:
        err = errors.New("somebody cut the wires!  :O ")
    }

    if err != nil {
        LogDebug(err)
    }
    return err
}

// *Your* connect error handler will take an extra channel as a parameter
func MyOnConnectErrorHandler(done chan struct{}, err error, socket gowebsocket.Socket) {
    methodName := "OnConnectErrorHandler"
    LogDebug("Starting %v parameters [err = %v , socket = %v]", methodName, err, socket)
    LogInfo("Disconnected from server ")
    close(done) // signal we are done.
}

Это имеет несколько преимуществ:

1) Вам не нужно угадывать, какие обратные вызовы происходят в процессе, а какие происходят в фоновых процедурах (и вам не нужно делать буферизацию всех ваших каналов «на всякий случай»)

2) Выбор нескольких каналов позволяет узнать , почему вы выходите и, возможно, по-разному справитесь с очисткой или регистрацией.

Примечание 1: Если вы решите использовать close сигнализацию, у вас будет , чтобы использовать разные каналы для каждого источника, чтобы избежать условий гонки, которые могут привести к двойному закрытию канала из-за различных процедур ( например, тайм-аут происходит, как только вы получаете ответ, и оба обработчика срабатывают; второй обработчик, закрывающий один и тот же канал, вызывает panic.) Это также, почему вы не хотите defer close всех каналов в верхняя часть функции.

Примечание 2: Не имеет прямого отношения к вашему вопросу, но - вам не нужно закрывать каждый канал - как только все указатели на него выйдут из области видимости, канал будет собирать мусор или нет он был закрыт.

1 голос
/ 21 июня 2019

Хорошо, происходит то, что канал блокируется, когда вы пытаетесь что-то добавить к нему.Попробуйте инициализировать канал done с буфером (я использовал 1) следующим образом:

done = make(chan bool, 1)

...