无法连接时停止通道

Stop channels when ws not able to connect

我有以下代码可以正常工作,问题是当 socket.Connect() 连接失败 我想停止这个过程,我试过以下代码 但它不起作用,即如果套接字连接失败,程序仍然运行。 我想要发生的是,如果 connect 失败,进程 停止 channe ......我在这里错过了什么?

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
    }

进程应该为运行大约 60-90 秒的进程打开一个 ws 连接(比如执行 npm install)并通过 web socket 获取进程的日志以及它完成的时间,以及当然处理可能发生的问题,如网络问题或一些错误 运行 过程

好的,当您尝试向其中添加内容时,频道正在发生阻塞。尝试使用缓冲区(我使用 1)初​​始化 done 通道,如下所示:

done = make(chan bool, 1)

所以,@Slabgorb 是正确的 - 如果您查看此处 (https://github.com/sacOO7/GoWebsocket/blob/master/gowebsocket.go#L87),您将看到 OnConnectErrorHandler 在执行对 Connect() 的调用期间被同步调用。在连接完全建立并且 OnConnected 回调完成之前,Connect() 函数不会启动单独的 goroutine 来处理 websocket。因此,当您尝试写入无缓冲通道 done 时,您正在阻塞调用 run() 函数的 same goroutine,并且您自己陷入僵局,因为没有 goroutine 能够从通道中读取来解除阻塞。

所以你可以使用他的解决方案并将其变成缓冲通道,这会起作用,但我的建议是不要为这种 one-time 标志行为写入通道,而是使用close 发信号。为每个要终止的条件定义一个通道 run(),并在适当的 websocket 处理程序函数中,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) 您无需猜测哪些回调发生 in-process 哪些发生在后台 goroutine 中(并且您不必让所有通道都缓冲 'just in case')

2) 选择多个通道可以让您找出您退出的原因,并可能以不同方式处理清理或日志记录。

注意 1:如果您选择使用 close 信令,您 必须 为每个源使用不同的信道以避免可能导致信道的竞争条件从不同的 goroutines 中关闭两次(例如,当您返回响应时发生超时,并且两个处理程序都会触发;第二个关闭同一通道的处理程序会导致 panic。)这也是您不这样做的原因想要defer close所有通道的函数。

注意 2:与您的问题没有直接关系,但是 - 您 不需要 关闭每个通道 - 一旦它的所有句柄超出范围,无论是否关闭,频道都会被垃圾收集。