处理来自单个通道的多个错误

  • 本文关键字:错误 通道 单个 处理 go
  • 更新时间 :
  • 英文 :


我在GO例程和错误处理方面有一些麻烦。

首先,我有一个可以聆听消息的函数(在无限的循环中(:

func main() {
    messageChannel := make(chan messageHandler.MessageInfo)
    for {
        if token := client.Subscribe("#", 0, func(client MQTT.Client, msg MQTT.Message) {
            go messageHandler.DecodeMessage(msg, messageChannel)
            select {
            case messageInfo := <-messageChannel:
                //Handle
            }
        }); token.Wait() && token.Error() != nil {
            fmt.Println(token.Error())
        }
    }

}

但是在解码功能中,可能会出现多个错误。

func DecodeMessage(msg mqtt.Message, c1 chan MessageInfo) {
    //do something, might result in error
    //do another thing, might result in error
    c1 <- MessageInfo{...}
}

通常,我只会从功能返回。但是,例程似乎有些棘手。我已经看了这篇文章,但是如果两个错误都会发生,我只会看到最后一个错误消息。

示例:

func DecodeMessage(msg mqtt.Message, c1 chan MessageInfo) {
    var returnError error
    if err != nil {
        returnError = err
    }
    if err != nil {
        returnError = err
    }
    c1 <- MessageInfo{
        Error: returnError,
        ...
    }
}

我应该有某种数组并附加所有错误吗?在一个例程中遇到多个错误是不好的做法吗?

对我来说最好的事情是,例程将在错误上退出并像"正常"一样返回该错误。这可能吗?

我首先要说的是,即使返回失败,必须在返回之前对函数进行所有错误检查,即使有点像代码气味。这可能是意味着您发生了一些奇怪的事情,并且可能有一种更好的方法来完成您的尝试。

但是,假设您已经将问题归结为此,那么我会看到两个选项,具体取决于必须如何处理错误。

如果可以一对一地处理错误,而不是真的彼此依赖,那么您可以创建一个错误通道并在遇到错误时一一将它们发送回它们。请参阅以下工作示例:

package main
import (
    "errors"
    "fmt"
    "strings"
)
func main() {
    errCh := make(chan error)
    go HandleErrorsSerially("bad error", errCh)
    for err := range errCh {
        fmt.Printf("Found error serially: %vn", err)
    }
}
func HandleErrorsSerially(msg string, errCh chan<- error) {
    if strings.Contains(msg, "error") {
        errCh <- errors.New("message contained string 'error'")
    }
    if strings.Contains(msg, "bad") {
        errCh <- errors.New("message contained string 'bad'")
    }
    close(errCh)
}

另外,如果您需要一次查看所有发生的所有错误(因为同时发生两个错误可能表明某些特殊情况(,那么您必须将它们全部附加到一个数组,然后将它们通过频道。请参阅以下工作示例:

package main
import (
    "errors"
    "fmt"
    "strings"
)
func main() {
    errArrCh := make(chan []error)
    go HandleErrorsTogether("bad error", errArrCh)
    errArr := <-errArrCh
    fmt.Printf("Found the following errors together: %vn", errArr)
}
func HandleErrorsTogether(msg string, errArrCh chan<- []error) {
    errArr := make([]error, 0)
    if strings.Contains(msg, "error") {
        errArr = append(errArr, errors.New("message contained string 'error'"))
    }
    if strings.Contains(msg, "bad") {
        errArr = append(errArr, errors.New("message contained string 'bad'"))
    }
    errArrCh <- errArr
    close(errArrCh)
}

我可以看到返回多个错误很有用的情况,例如当您解析消息并且有多个不良字段时,您需要将这些字段汇总给客户端。

我认为最好的方法是使用像Hashicorp的MultierRor这样的软件包,该软件包允许在实现错误接口的结构类型中收集多个错误,因此仍然可以在chan error上发送。然后,接收方可以作为标准错误处理或提取每个单独错误的信息。

多级文档非常好,只需阅读github页面上的示例。

对我来说最好的事情是,例程将在错误中退出 返回该错误会"正常"。

当然,您可以并且获得最后一个错误或所有错误都很奇怪。

  1. 获得最后一个错误是无助的,与获取第一个错误相比。
  2. 获取所有错误,如果第一个错误将导致以下故障,则以下错误消息无助。另一种情况是这些错误没有关联,我认为这意味着您必须将它们串起到不同的并发部分以更好地控制。

好,现在回到原始问题。考虑代码:

func foo(errTo chan error) {
    defer close(errTo)
    v, err := CouldFailOne()
    if err != nil {
        errTo <- err
        return // Yp, just stop this routine, let it join back to Invoker
    }
    v2, err := CloudFailTwo()
    if err != nil {
        errTo <- err
        return
    }
    // As the previous error handle until end of the function
}

如果要从这种功能返回值。只需使用一个频道,然后将值发送到其中只不动了。我认为这种样式将更加清晰,就像返回样式一样,刚刚开始使用频道返回错误。

最新更新