【问题标题】:Trouble using Sanic and Redis使用 Sanic 和 Redis 时遇到问题
【发布时间】:2020-08-10 23:10:49
【问题描述】:

我正在与 2 名工人一起使用 Sanic。我正在尝试使计费系统正常工作,即计算用户点击 API 端点的次数。以下是我的代码:

class User(object):
    def __init__(self, id, name, age, address, mobile, credits=0):
        self.id = id
        self.name = name
        self.credits = count
        self.details = {"age": age, "address": address, "mobile_number": mobile}

上面的用户类用于制作我使用另一个 python 脚本上传到 Redis 的对象,如下所示:

user = User(..., credits = 10)
string_obj = json.dumps(user)
root.set(f"{user.user_id}", string_obj)

当我想维护端点接收的命中数并使用用户对象跟踪它并将其上传回 Redis 时,就会出现主要问题。我的代码如下:

from sanic_redis_ext import RedisExtension

app = Sanic("Testing")
app.config.update(
{
    "REDIS_HOST": "127.0.0.1",
    "REDIS_PORT": 6379,
    "REDIS_DATABASE": 0,
    "REDIS_SSL": None,
    "REDIS_ENCODING": "utf-8",
    "REDIS_MIN_SIZE_POOL": 1,
    "REDIS_MAX_SIZE_POOL": 10,
})

@app.route("/test", methods=["POST"])
@inject_user()
@protected()
async def foo(request, user):
    user.credits -= 1
    if user.credits < 0:
        user.credits = 0
        return sanic.response.text("Credits Exhausted")

    result = process(request)

    if not result:
        user.credits += 1

    await app.redis.set(f"{user.user_id}", json.dumps(user))
    return sanic.response.text(result)

这就是我检索用户的方式:

async def retrieve_user(request, *args, **kwargs):
    if "user_id" in kwargs:
        user_id = kwargs.get("user_id")
    else:
        if "payload" in kwargs:
            payload = kwargs.get("payload")
        else:
            payload = await request.app.auth.extract_payload(request)
        if not payload:
            raise exceptions.MissingAuthorizationHeader()
        user_id = payload.get("user_id")

    user = json.loads(await app.redis.get(user_id))
    return user

当我使用 JMeter 以 10 个线程作为同一用户测试 API 端点时,信用系统似乎不起作用。在这种情况下,当用户从 10 个积分开始时,他们最终可能会剩下 7 或 8 个(不可预测)积分,而他们应该还剩下 0 个积分。据我说,这是由于工作人员没有共享用户对象并且没有变量的更新副本,这导致他们覆盖彼此的更新。谁能帮我找到解决方法,这样即使同一个用户同时点击端点,他/她也应该被完美计费,并且应该将用户对象保存回 Redis。

【问题讨论】:

    标签: python api redis multiprocessing sanic


    【解决方案1】:

    问题是你从 Redis 读取 credits 信息,扣除它,然后将它保存回 Redis,这不是一个原子过程。这是一个并发问题。

    我不了解 Python,所以我只使用伪代码。

    首先为用户 {user_id} 设置 10 个积分。

    app.redis.set("{user_id}:credits", 10)
    

    然后这个用户进来

    # deduct 1 from the user credits and get the result
    int remaining_credits=app.redis.incryBy ("{user_id}:credits",-1) 
    if(remaining_credits<=0){
       return sanic.response.text("Credits Exhausted")} else{
       return "sucess" # or some other result}
    

    将您的用户信息与有效负载一起保存在其他地方并检索“{user_id}:credits”并在您检索用户时将它们组合起来。

    【讨论】:

    • 同意。不确定您使用的是什么 Redis 驱动程序,但 Redis 支持递增/递减操作。您应该使用它而不是设置对象。另一种策略是锁定对象。我稍后会发送一个代码sn-p。
    猜你喜欢
    • 2019-12-08
    • 2011-04-10
    • 2014-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多