首页 > 解决方案 > 使用 firebase-admin SDK for go 重试失败的 FCM 消息

问题描述

我正在为 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)
    }
}

下面是我如何尝试使用重试的片段...

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
}

谢谢!

标签: firebasegofirebase-cloud-messagingfirebase-admin

解决方案


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 的情况下重试。

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


推荐阅读