【问题标题】:Retrying Failed FCM messages using firebase-admin SDK for go使用 firebase-admin SDK for go 重试失败的 FCM 消息
【发布时间】:2020-10-02 03:27:16
【问题描述】:

我正在为 Go 使用最新版本的 firebase-admin-sdk,以便我可以使用 FCM 发送推送通知。我已经构建了一个客户端,并且能够使用 SendMultiCast() 方法成功发送/接收通知。

Firebase 团队建议使用指数回退方法实施重试,以处理失败的请求。我已经设置了在旧 FCM 库中使用过的回退。问题是我不确定我应该在重试逻辑中处理哪种类型的错误以重播请求。我认为通常最好只在网络传输问题的情况下这样做。

这是基本的重试...我已经注释掉了其中寻找 net.Errors 的代码,因为管理 SDK 似乎没有返回类似的内容。我知道SendMultiCast() 方法返回一个错误和一个BatchResponse。错误响应是我应该重试的还是BatchResponse 中的内容?我似乎找不到要在批处理响应中重试的内容。

const (
    minBackoff = 100 * time.Millisecond
    maxBackoff = 1 * time.Minute
)

func retry(fn func() error, attempts int) error {
    var attempt int
    for {
        err := fn()
        log.Println("attempt to send")
        if err == nil {
            return nil
        }
        
        // This is what has worked in past libs since I could expect 
        // net.errors signifying a timeout or some sort of http issue that would
        // require a retry
        // if tErr, ok := err.(net.Error); !ok || !tErr.Temporary() {
        //   return err
        // }

        attempt++
        backoff := minBackoff * time.Duration(attempt*attempt)

        if attempt > attempts || backoff > maxBackoff {
            return err
        }

        time.Sleep(backoff)
    }
}

下面是我如何尝试使用 Retry...

func (c *Client) Send(ctx context.Context, msg *messaging.MulticastMessage) (*FCMv1Response, error) {
    res := new(FCMv1Response)
    err := retry(func() error {
        ctx, cancel := context.WithTimeout(ctx, DefaultTimeout)
        defer cancel()
        var er error
        res.FailedTokens, er = c.sendMulticastMessage(ctx, msg)
        return er
    }, RetryAttempts)

    if err != nil {
        c.logger.Error("errorSendingLLUNotification", zap.Error(err))
        return nil, err
    }
    return res, nil

}

func (c *Client) sendMulticastMessage(ctx context.Context, msg *messaging.MulticastMessage) ([]string, error) {
    br, err := c.msg.SendMulticast(ctx, msg)
    if err != nil {
        return nil, err
    }

    var failedTokens []string
    if br.FailureCount > 0 {
        for idx, resp := range br.Responses {
            if !resp.Success {
                // The order of responses corresponds to the order of the registration tokens.
                failedTokens = append(failedTokens, msg.Tokens[idx])
            }
        }
    }
    return failedTokens, nil
}

谢谢!

【问题讨论】:

    标签: firebase go firebase-cloud-messaging firebase-admin


    【解决方案1】:
    br, err := client.SendMulticast(ctx, msg)
    if err != nil {
      // total failure
    }
    
    if br.FailureCount > 0 {
      // partial failure
    }
    

    您不必在完全失败时重试。 SDK 会自动处理。当您的代码看到上述错误时,它已经重试了几次都没有成功,或者该错误根本不符合重试条件。

    对于部分失败,messaging.SendResponse 类型包含一个错误,该错误可以通过messaging 包或新的errorutils 包的错误处理函数来确定它是否符合重试条件。通常,您只想在 messaging.IsUnavailable() 返回 true 的情况下重试。

    您还需要确保重试仅发送到在先前尝试中失败的令牌。我在您上面给出的代码中看不到这种逻辑。

    【讨论】:

    • @HiranyaJaythilaka 谢谢!在对事情进行了更多审查之后,看起来多播方法现在实际上并不能满足我的特定要求。 Send() 方法是否也有内置的重试?
    • 是的,Send() 内置重试所有符合条件的错误。
    猜你喜欢
    • 2022-01-09
    • 2018-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-13
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    相关资源
    最近更新 更多