猿问

如果已经为同一用户 ID 处理了另一个请求,如何拒绝该请求?

我正在尝试实现某种同步服务。具有不同用户代理的两个客户端可以POST/PATCH同时/sync/user/{user_id}/resource使用相同的 user_id. sync应该更新数据库中用户id={user_id}的数据。


func (syncServer *SyncServer) Upload(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

    userID := ps.ByName("user_id"))

    if isAlreadyProcessedForUser(userID) {

       w.WriteHeader(http.StatusConflict)

       return

    }

    ...

    syncServer.db.Update(userID, data)

    ...


}

Upload问题是当另一个仍在处理相同请求时,我不知道如何正确拒绝一个user_id。我认为使用这个想法mutex.Lock()是不好的,因为我会为这个处理程序使用很多 pod,如果Upload在不同的 pod 上调用,它无论如何也帮不了我。我可以使用什么同步方法来解决这个问题?我应该在数据库中使用一些额外的字段吗?我要求给我任何想法!


交互式爱情
浏览 80回答 1
1回答

慕运维8079593

在分布式系统中有很多方法可以做到这一点(分布式锁定),目前我可以想出一些方法:使用redis(或任何其他类似服务)锁。然后您可以user_id在收到第一个请求时锁定每个请求并拒绝其他请求,user_id因为您无法锁定它。Redis的锁一般都有过期时间所以不会死锁。参考:https ://redis.io/docs/reference/patterns/distributed-locks/使用数据库锁。你应该小心使用数据库锁,但一个简单的方法是使用唯一索引:上传前创建一个uploading带有约束的记录,unique(user_id)上传后删除它。有可能忘记/未能删除记录并导致死锁,因此您可能希望向expired_at记录添加另一个字段,在上传前检查并删除它。(特定于问题的场景)使用唯一约束(user_id, upload_status)。这称为部分索引,您只会在 时检查此唯一索引upload_stats = 'uploading'。然后您可以为每个请求创建一条uploading记录,并拒绝另一个请求。过期也需要,所以你需要跟踪start_time上传和清理长时间上传记录。如果您不需要重新申请磁盘空间,您可以简单地将记录标记为failed,这样您还可以跟踪这些上传在数据库中失败的时间和方式。警告:看来您正在使用 Kubernetes,因此应谨慎使用任何非分布式锁,具体取决于您想要获得的一致性级别。Pod 是易变的,很难依赖本地信息并实现一致性,因为它们可能被复制/杀死/重新安排到另一台机器。这也适用于具有自动缩放或调度机制的任何其他平台。一个用户拥有的多个客户端与服务器之间的同步过程至少需要处理请求排序、请求去重和最终一致性问题(例如,Google Doc 可以支持多人同时编辑)。有一些通用算法(如操作转换),但这取决于您的具体用例。
随时随地看视频慕课网APP

相关分类

Go
我要回答