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

时间:2018-04-25 18:50:11

标签: 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())
        }
    }


}

但是在DecodeMessage函数中,可能会出现多个错误。

func DecodeMessage(msg mqtt.Message, c1 chan MessageInfo) {

    //do something, might result in error

    //do another thing, might result in error

    c1 <- MessageInfo{...}
}

通常我会从函数返回。但是对于惯例似乎有点棘手。我已查看this post,但如果两个错误都发生,我只会看到最后一条错误消息。

示例:

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,
        ...
    }
}

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

对我来说,最好的事情是例程会在错误时退出并返回该错误,就像通常&#34;通常&#34;一样。这可能吗?

3 个答案:

答案 0 :(得分:0)

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

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

如果错误可以逐个处理并且并不真正相互依赖,那么您可以创建错误通道并在遇到错误时逐个发送它们。请参阅以下工作示例:

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: %v\n", 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: %v\n", 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)
}

答案 1 :(得分:0)

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

我认为最好的方法是使用像hashicorp's multierror这样的包,它允许在实现错误接口的结构类型中使用格式化收集多个错误,因此仍然可以在chan error上发送。然后,接收方可以仅处理标准错误,也可以提取有关每个错误的信息。

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

答案 2 :(得分:0)

  

对我来说,最好的事情是例程会因错误而退出   像往常一样返回那个错误&#34;通常&#34;。

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

  1. 获取上一个错误对于调试没有帮助,与获得第一个错误相比。
  2. 如果第一个错误导致以下失败,则获取所有错误相同,以下错误消息无效;另一种情况是这些错误有关联,我认为这意味着你必须将它们分配到不同的并发部分以便更好地控制。
  3. 好的,现在回到原来的问题。考虑一下代码:

    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
    }
    

    如果要从此类函数返回值。只需使用一个频道,并将值发送到它只是没有错误提高。我认为这种风格会更加清晰,就像返回风格一样,只是使用了一个通道来返回错误。

相关问题