Обработка нескольких ошибок из одного канала - PullRequest
0 голосов
/ 25 апреля 2018

У меня возникли проблемы с процедурами go и каналами обработки ошибок.

Во-первых, у меня есть функция, которая прослушивает сообщения (в бесконечном цикле for):

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

Обычно я бы просто вернулся из функции. Но кажется немного сложнее с рутиной. Я посмотрел на этот пост , но если бы произошли обе ошибки, я бы увидел только последнее сообщение об ошибке.

Пример:

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

Должен ли я иметь какой-то массив и добавлять все ошибки? Плохо ли иметь несколько ошибок в одной программе?

Лучше всего для меня то, что подпрограмма завершится с ошибкой и вернет эту ошибку, как если бы она работала "нормально". Это возможно?

Ответы [ 3 ]

0 голосов
/ 26 апреля 2018

Я могу видеть случаи, когда полезно возвращать несколько ошибок, например, когда вы анализируете сообщения и есть несколько неправильных полей, и вам необходимо обобщить их обратно клиенту.

Я думаю, что лучший подходзаключается в использовании пакета, такого как multiherror hashicorp, который позволяет собирать несколько ошибок с форматированием в тип структуры, который реализует интерфейс ошибок и поэтому все еще может отправляться на chan error.Затем принимающая сторона может либо обрабатывать как обычную ошибку, либо извлекать информацию о каждой отдельной ошибке.

Многоуровневая документация довольно хорошая, просто прочитайте примеры на странице github.

0 голосов
/ 26 апреля 2018

Лучше всего для меня то, что подпрограмма завершится с ошибкой и вернет эту ошибку, как если бы она работала "нормально".

Конечно, вы можете иполучить последнюю ошибку или все ошибки довольно странные.

  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
}

Если вы хотите вернуть значение из функции такого рода.Просто используйте канал и отправьте значение в него только без ошибок.Я думаю, что этот стиль будет более понятным и похож на стиль возврата, просто стал использовать канал для возврата ошибки.

0 голосов
/ 26 апреля 2018

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

Однако, при условии, что вы свалили свою проблемук этому, тогда я вижу два варианта, в зависимости от того, как ошибки должны быть обработаны.

Если ошибки могут быть обработаны один за другим и на самом деле не полагаются друг на друга, то вы можете простосоздайте канал ошибок и отправляйте их по одному, когда вы обнаружите ошибки.См. Следующий рабочий пример:

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)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...