在Django + uWsgi项目

时间:2015-10-20 10:27:41

标签: django mongodb pymongo pymongo-3.x

我们使用Django&开发了一个REST API。 mongoDBPyMongo司机)。问题是,在对API端点的一些请求中,PyMongo游标返回一个部分响应,其中包含的文档少于它应该的文档(但它是一个完全有效的JSON文档)。

让我以我们的一个观点为例解释一下:

def get_data(key):
    return collection.find({'key': key}, limit=24)

def my_view(request):
    key = request.POST.get('key')
    query = get_data(key)
    res = [app for app in query]
    return JsonResponse({'list': res})

我们确信有超过8000个与查询匹配的文档,但是在 一些调用我们得到的结果少于24(甚至为零)。我们遇到的第一个问题 调查的是我们的代码中有多个MongoClient定义。通过解决这个问题,问题的发生次数减少了,但我们仍然在很多电话中都有这种情况。

在完成所有这些调查之后,我们设计了一个测试,其中我们同时向服务器发出了16个异步请求。通过这种方法,我们可以重现这个问题。在这16个请求中,每个请求中有6-8个具有部分结果。运行此测试后,我们将uWsgi的进程数减少到6并重新启动服务器。所有结果都很好,但在服务器上施加了另一个重负载后,问题再次出现。此时,我们重新启动了uwsgi服务,一切都很好。通过最后一个实验,我们现在有一个线索,即当uwsgi服务开始运行时,一切正常,但经过一段时间和重负载后,服务器开始再次返回部分或空的结果。 我们最近的调查是使用python manage.pyDEBUG=False运行API,在这种情况下,我们在一段时间后再次遇到问题。

我们无法弄清楚问题是什么以及如何解决。我们可以想到的一个原因是Django在完成之前关闭了pymongo的连接。因为返回的结果是有效的JSON。

我们的筹码是:

  • nginx(未启用缓存)
  • uWsgi
  • MemCached(在调试过程中禁用)
  • Django(第3版v1.8)
  • PyMongo(v3.0.3)

非常感谢您的帮助。

更新

Mongo版本:

db version v3.0.7
git version: 6ce7cbe8c6b899552dadd907604559806aa2e9bd
  • 我们正在运行单个mongod实例。没有分片/复制。
  • 我们正在使用此代码段创建连接:

    con = MongoClient('localhost',27017)

更新2

Subject thread in Pymongo issue tracker

2 个答案:

答案 0 :(得分:1)

Pymongo游标不是线程安全的元素。因此,像我在多线程环境中所做的那样使用它们将导致我所描述的问题。另一方面,Python的list操作主要是线程安全的,像这样更改片段将解决问题:

def get_data(key):
    return list(collection.find({'key': key}, limit=24))

def my_view(request):
    key = request.POST.get('key')
    query = get_data(key)
    res = [app for app in query]
    return JsonResponse({'list': res})

答案 1 :(得分:0)

我非常推测的猜测是你在代码中的某个地方重用游标。确保您在视图堆栈本身内初始化您的集合,而不是在它之外。

例如,正如所写,如果您正在执行以下操作:

import ...
import con

collection = con.documents
# blah blah code
def my_view(request):
    key = request.POST.get('key')
    query = collection.find({'key': key}, limit=24)
    res = [app for app in query]
    return JsonResponse({'list': res})

你可以让我们重复使用游标。最好做一些像

这样的事情
import ...
import con

# blah blah code
def my_view(request):
    collection = con.documents
    key = request.POST.get('key')
    query = collection.find({'key': key}, limit=24)
    res = [app for app in query]
    return JsonResponse({'list': res})

在提问者的要求澄清时编辑:

您需要在视图堆栈中定义集合而不是在文件加载时的原因是集合变量具有游标,这基本上是数据库和应用程序相互通信的方式。除了一堆其他东西之外,游标会做一些事情,例如跟踪你在一长串数据中的位置,但这是重要的部分。

在view方法之外创建集合光标时,如果存在,它将在每个请求上重新使用光标。所以,如果你发出一个请求,然后另一个真正,非常快,那么(就像你应用高负载时发生的那样),光标可能只是与数据库交谈的一半,所以你的一些数据会转到第一个请求,第二个请求。您在请求中获取NO数据的原因是,如果游标已完成获取数据但尚未关闭,则下一个请求尝试从游标中获取数据,并且在查询中没有剩余内容

通过将集合定义(以及通过关联,光标定义)移动到视图堆栈中,您将在处理新请求时始终获得新光标。你不会在你的游标和不同的请求之间进行交叉讨论,因为每个请求周期都有它自己的。