如何在 Django 中处理并发请求?

Posted

技术标签:

【中文标题】如何在 Django 中处理并发请求?【英文标题】:How to handle simultaneous requests in Django? 【发布时间】:2020-07-28 19:24:02 【问题描述】:

我在 Django 中有一个标准的基于函数的视图,它在用户单击按钮后通过 POST 接收一些参数,计算一些东西,然后返回一个带有上下文的模板。

@csrf_exempt
def myview(request, param1, param2):

   if request.method == 'POST':
      return HttpResponseRedirect(reverse("app1:view_name", args=[param1, param2]))

   '''Calculate and database r/w'''

   template = loader.get_template('showData.html')
   return HttpResponse(template.render(context, request))

只要同时处理一个请求,它就可以正常工作(在 runserver 和 Apache 服务器中都进行了测试)。

但是,当我使用两个设备并同时单击每个设备的按钮时,两个请求会混合在一起,同时运行,并且网站最终会出现 500 错误,或者 404 或有时会成功但无法获取静态文件..(再次使用 runserver 和 Apache 测试)。

如何强制 Django 在开始下一个请求之前完成当前请求的执行? 还是有更好的方法来解决这个问题?

我们将不胜感激。谢谢!

【问题讨论】:

【参考方案1】:

对于单个服务器进程中的coordinate 线程,请使用

from threading import RLock

lock = RLock()

然后在myview内:

    lock.acquire()
    ...  # get template, render it
    lock.release()

你可以用$ uwsgi --processes 1 --threads 2 ...启动你的服务器

【讨论】:

我在 Apache 的视图中添加了 RLock,处理请求时进程挂起(我还在 WSGIDaemonProcess 中添加了进程和线程标志)。当使用 runserver 在本地实现它时,它允许一个请求完成但挂起另一个请求。知道为什么会发生这种行为吗?【参考方案2】:

本地机器上的 Django Web 服务器不适用于生产环境。所以它一次处理一个请求。在生产中,你需要使用 WSGI 服务器,比如 uwsgi。这样,您的应用程序可以设置为一次服务多个请求。检查https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/uwsgi/

【讨论】:

是的,我正在使用带有 mod_wsgi 的 Apache,但它显示了同样的问题。我设置了 processes=2 和 threads=15 的标志。 您确定设置正确吗?这些步骤可以帮助您确认您所做的事情。 digitalocean.com/community/tutorials/… 不确定“本地机器上的 django web server”是什么意思。如果你的意思是manage.py runserver,那么它使用多线程并行处理多个请求(***.com/questions/64980287/…)【参考方案3】:

我发布我的解决方案,以防对他人有任何帮助。

最后我用pre-forking 配置了Apache 来隔离请求。根据文档,建议对使用非线程安全库的站点进行预分叉(显然是我的情况)。

通过此修复,Apache 可以很好地处理并发请求。但是,如果其他人有其他建议,我仍然会很高兴!

【讨论】:

这可能是最快的解决方案,对于许多情况来说可能已经足够了。然而,修改代码可能是个好主意,它是线程安全的,意味着不使用全局变量或使用Locks 保护每个全局变量访问并仅调用线程安全函数。根据您的代码,这可能或多或少难以实现。此解决方案可能使用比服务器多一点的 RAM,但允许线程。另一方面(因为 Python 的线程效率不高)与使用 N 个线程相比,使用 N 个进程可能更高效【参考方案4】:

应该有办法重写代码,这样事情就不会混淆。 (至少在很多情况下这是可能的)

先决条件之一(如果您的服务器使用线程)是编写线程安全代码 这意味着不使用全局变量(无论如何这都是不好的做法)(或使用Locks 保护它们) 并且不调用非线程安全的函数。 (或使用Locks 保护他们)

由于您没有提供任何详细信息,我们对此无能为力。 (这 = 找到一种方法阻止整个请求,但保持数据完整性)

否则,您可以使用跨多个进程工作的互斥锁/锁。

例如,您可以尝试访问锁定的文件 https://pypi.org/project/filelock/ 并阻止直到文件被其他视图解锁。

示例代码(pip安装文件锁后)

from filelock import FileLock
lock = FileLock("my.lock")
with lock:
    if request.method == 'POST':
        return HttpResponseRedirect(reverse("app1:view_name", args=[param1, param2]))

    '''Calculate and database r/w'''

    template = loader.get_template('showData.html')
    return HttpResponse(template.render(context, request))

如果你使用 uwsgi,那么你可以看看锁的 uwsgi 实现:

https://uwsgi-docs.readthedocs.io/en/latest/Locks.html

这里是 uwsgi 文档中的示例代码:

def use_lock_zero_for_important_things():
    uwsgi.lock() # Implicit parameter 0
    # Critical section
    uwsgi.unlock() # Implicit parameter 0

def use_another_lock():
    uwsgi.lock(1)
    time.sleep(1) # Take that, performance! Ha!
    uwsgi.unlock(1)

【讨论】:

以上是关于如何在 Django 中处理并发请求?的主要内容,如果未能解决你的问题,请参考以下文章

如何在golang中顺序处理并发请求? [复制]

如何在 django 的预检 CORS POST 请求中处理 CSRF?

如何在 Django Rest Framework 中处理并行请求?

Django 异步视图中的多个并发请求

如何优雅地处理重复(并发)请求?

如何配置IIS处理多并发请求及存在的问题