简体   繁体   English

使用 Sanic 和 Redis 出现问题

[英]Trouble using Sanic and Redis

I am using Sanic with 2 workers.我正在与 2 名工人一起使用 Sanic。 I am trying to get a billing system working, ie Counting how many times a user hit the API endpoint.我正在尝试使计费系统正常工作,即计算用户点击 API 端点的次数。 Following is my code:以下是我的代码:

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}

The above Users Class is used to make objects that I have uploaded onto Redis using another python script as follows:上面的用户 Class 用于制作我使用另一个 python 脚本上传到 Redis 的对象,如下所示:

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

The main issue arises when I want to maintain a count of the number of hits an endpoint receives and track it withing the user object and upload it back onto Redis.当我想维护端点接收的命中数并使用用户 object 跟踪它并将其上传回 Redis 时,就会出现主要问题。 My code is as follows:我的代码如下:

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)

And this is how I am retrieving the user:这就是我检索用户的方式:

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

When I use JMeter to test the API endpoint with 10 threads acting as the same user, the credit system does not seem to work.当我使用 JMeter 以 10 个线程作为同一用户测试 API 端点时,信用系统似乎不起作用。 In this case, as the user starts with 10 credits, they may end up with 7 or 8 (not predictable) credits left whereas they should have 0 left.在这种情况下,当用户从 10 个积分开始时,他们最终可能会剩下 7 或 8 个(不可预测)积分,而他们应该还剩下 0 个积分。 According to me, this is due to the workers not sharing the user object and not having the updated copy of the variable which is causing them to overwrite each others update.据我说,这是由于工作人员没有共享用户 object 并且没有变量的更新副本,这导致他们覆盖彼此的更新。 Can anyone help me find a way out of this so that even if the same user simultaneously hits the endpoint, he/she should be billed perfectly and the user object should be saved back into Redis.谁能帮我找到解决方法,这样即使同一个用户同时点击端点,他/她也应该被完美计费,用户 object 应该被保存回 Redis。

The problem is that you read the credits info from Redis, deduct it, then save it back it to Redis, which is not an atomic process.问题是您从 Redis 中读取了 credits 信息,将其扣除,然后将其保存回 Redis,这不是原子过程。 It's a concurrency issue.这是一个并发问题。

I don't know about Python, so I'll just use pseudo code.我不知道 Python,所以我将使用伪代码。

First set 10 credits for user {user_id}.首先为用户 {user_id} 设置 10 个积分。

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

Then this user comes in然后这个用户进来了

# 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}

Save your user info with payload somewhere else and retrieve the "{user_id}:credits"and combine them when you retrieve the user.将您的用户信息与有效负载一起保存在其他地方并检索“{user_id}:credits”并在您检索用户时将它们组合起来。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM