【问题标题】:How to queue requests in Django?如何在 Django 中对请求进行排队?
【发布时间】:2021-11-19 08:12:36
【问题描述】:

我使用 Django (DRF) 管理一个物理储物柜。用户填写表格,通过发送到他们电子邮件的链接进行身份验证,通过储物柜上显示的密码进行授权。

我的观点应该处理三种情况:

  • 如果用户认证和授权成功,pin显示在 储物柜被通用消息替换,储物柜打开。 (已经实现)

  • 如果用户在 3 分钟内未授权,则锁定密码将替换为通用消息。

  • 如果用户Foo发起了新的授权请求,而对用户Bar的授权还没有完成,则将请求堆叠在 一个队列并等待案例 1. 或案例 2. 完成。

我该怎么做:

  • 实现一个请求队列,这样当有新请求进入时,储物柜上显示的 pin 不会被覆盖/替换?
  • 如何在处理下一个请求之前等待 3 分钟以完成授权?

按原样查看,以防有用:

   if request.method == 'POST':
        form = ConfirmationForm(request.POST)
        if form.is_valid():
            if pin == form.cleaned_data['pin']:
                open_bay(jwt_token=jwt[1], pin=pin)
                display_generic_message(jwt_token=jwt[1])
                lock_bay(jwt_token=jwt[1], pin=pin)
                return render(request, 'static/pages/request-success.html')
            else:
                pass
    else:
        form = ConfirmationForm()
    return render(request, 'static/pages/confirmation.html', {'form': form})

【问题讨论】:

  • Django 在多处理/多线程方面相当糟糕,这就是为什么队列任务的解决方案通常涉及一个 Redis 实例和一个或多个将处理队列的工作人员。例如(但可能还有其他):github.com/rq/django-rq

标签: python django django-rest-framework


【解决方案1】:

在您保存授权的模型中,您需要添加 date_created 或 date_requested 字段,并且必须是 datetime 字段。然后在每个请求上,您都可以检查您保存的 date_requested 是否已经过去了 3 分钟。 您还需要一个 is_authorized 字段来检查您的用户是否被授权。 我们假设您从您发送的电子邮件中获取用户。

user = get_object_or_404(User,email=kwargs['email'])
if user.date_request + timedelta(minutes=3) > datetime.datetime.now():
   "do your authorzing stuff ..."
else:
     return HttpResponse("you need to wait 3 minutes to request again")

【讨论】:

  • 然而,如果没有更多的请求,3 分钟的延迟将不会被遵守,消息也不会显示。编辑:队列不会被维护
【解决方案2】:

您需要存储上次授权开始的时间,如果用户输入密码,请清除该时间戳。

# models.py
class LockerUserQueue(models.Model):

    user = models.ForeignKey(get_user_model())
    locker = models.ForeignKey("yourapp.Locker")
    created_at = models.DateTimeField(index=True, auto_now_add=True)


class Locker(models.Model):
    last_authorization = models.DateTimeField(null=True, blank=True)
    user_queue = models.ManyToManyField(through=LockerUserQueue)

    class BadPin(Exception):
        pass

    def enqueue_user(self, user):
        self.user_queue.add(user)

    def process_authorization(self, user):
        # do authorization for user

    def process_pin(self, user, pin):
        self.last_authorization = None
        if validate_pin(user, pin):
            # pin OK logic
        else:
            raise self.BadPin

# views.py
def authorize_user(request, locker_id):
    locker = get_object_or_404(pk=locker_id)
    locker.enqueue_user(request.user)
    locker.save()
    return render(request, "authorization_started.html")

def open_bay_with_pin(request, locker_id):
    locker = get_object_or_404(pk=locker_id)
    pin = get_pin_from_request(request)
    try:
        # No matter if the pin is correct, the locker.last_authorization is cleared
        locker.process_pin(user, pin)
    except locker.BadPin:
        return render(request, "bad_pin.html")
    finally:
        locker.save()
    return render(request, "good_pin.html")



# management/commands/process_queue.py
# you would run this by
# $ python manage.py process_queue
class Command(BaseCommand):

    @transaction.atomic
    def process_queue(self):
        # you probably want to put the 3 min delay in your settings.py
        # so you don't end up with a magic value here
        for locker in LockerUserQueue.objects.filter(
            Q(locker__last_authorization__isnull=True)|Q(locker__last_authorization__gt=now() - timedelta(minutes=3))
        ).order_by("created_at").values_list("locker", flat=True).distinct():
            locker.process_authorization()

    def handle(self):
        while True:
            self.process_queue()
            # you don't want to keep querying the DB when there's nothing in the queue
            sleep(5)

上面的代码显示了如何将数据库用作队列,在低容量用例中这应该没问题。如果容量很大,我会将储物柜last_authorization 存储在更快的存储空间中,例如redis,队列也可以在redis 中维护。但背后的逻辑是一样的。

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-13
  • 2021-08-24
  • 1970-01-01
  • 1970-01-01
  • 2011-09-01
  • 2011-06-14
  • 1970-01-01
相关资源
最近更新 更多