如何在 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 可以很好地处理并发请求。但是,如果其他人有其他建议,我仍然会很高兴!
【讨论】:
这可能是最快的解决方案,对于许多情况来说可能已经足够了。然而,修改代码可能是个好主意,它是线程安全的,意味着不使用全局变量或使用Lock
s 保护每个全局变量访问并仅调用线程安全函数。根据您的代码,这可能或多或少难以实现。此解决方案可能使用比服务器多一点的 RAM,但允许线程。另一方面(因为 Python 的线程效率不高)与使用 N 个线程相比,使用 N 个进程可能更高效【参考方案4】:
应该有办法重写代码,这样事情就不会混淆。 (至少在很多情况下这是可能的)
先决条件之一(如果您的服务器使用线程)是编写线程安全代码
这意味着不使用全局变量(无论如何这都是不好的做法)(或使用Lock
s 保护它们)
并且不调用非线程安全的函数。 (或使用Lock
s 保护他们)
由于您没有提供任何详细信息,我们对此无能为力。 (这 = 找到一种方法不阻止整个请求,但保持数据完整性)
否则,您可以使用跨多个进程工作的互斥锁/锁。
例如,您可以尝试访问锁定的文件 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 中处理并发请求?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 django 的预检 CORS POST 请求中处理 CSRF?