为了账号安全,请及时绑定邮箱和手机立即绑定

当无法连接时停止通道

当无法连接时停止通道

Go
噜噜哒 2023-06-26 16:55:36
我有以下代码可以正常工作,问题是当连接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(例如执行 npm install),并通过 t 获取进程的日志web socke以及何时完成,当然还要处理可能发生的问题,例如网络问题或运行时的某些错误过程
查看完整描述

2 回答

?
慕盖茨4494581

TA贡献1850条经验 获得超11个赞

好的,发生的情况是当您尝试向通道添加内容时通道被阻塞。尝试done使用缓冲区(我使用 1)初始化通道,如下所示:

done = make(chan bool, 1)


查看完整回答
反对 回复 2023-06-26
?
阿波罗的战车

TA贡献1862条经验 获得超6个赞

您将看到OnConnectErrorHandler在执行调用期间同步调用Connect(). 在连接完全建立并且回调完成之前,该Connect()函数不会启动单独的 goroutine 来处理 websocket 。OnConnected因此,当您尝试写入无缓冲的通道时done,您会阻塞最初调用该函数的同一个Goroutine,并且您自己会陷入僵局,因为没有 Goroutine 能够从通道读取数据来解锁您。run()

因此,您可以采用他的解决方案并将其转换为缓冲通道,这将起作用,但我的建议是不要针对这种一次性标志行为写入通道,而是使用信号发送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)你不需要猜测哪些回调发生在进程中,哪些回调发生在后台 goroutine 中(并且你不必让所有通道都缓冲“以防万一”)


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


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


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


查看完整回答
反对 回复 2023-06-26
  • 2 回答
  • 0 关注
  • 116 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信