使用 firebase-admin SDK for go 重试失败的 FCM 消息

Retrying Failed FCM messages using firebase-admin SDK for go

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

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

这是基本的重试...我已经注释掉其中寻找 net.Errors 的代码,因为它看起来不像管理 SDK returns 那样的东西。我知道 SendMultiCast() 方法 returns 一个错误和一个 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
}

谢谢!

br, err := client.SendMulticast(ctx, msg)
if err != nil {
  // total failure
}

if br.FailureCount > 0 {
  // partial failure
}

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

对于部分失败,messaging.SendResponse type includes an error that can be passed through the error handling functions of the messaging package or the new errorutils package to determine if it's eligible for retry. Typically you would only want to retry on cases where messaging.IsUnavailable() returns 为真。

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