Django - 生产中特定视图的等待时间太长

发布于 2024-11-07 18:37:40 字数 2048 浏览 0 评论 0原文

我已经为这个问题苦苦挣扎了至少两天。 有一个视图会生成一个页面,显示某个用户/电话分机拨打的所有电话。没什么花哨的,只是一页很长,最多 1000 行。

该视图函数从 url 接收一些参数,以便选择在此页面上显示的内容。有两种情况,一种是在 url 中传递“extension=xxxxxx”,另一种是在 url 中传递“user=xxxx”:

if request.GET.get("extension", None):
    # extension_query expects only one Extension object
    extension_query = Extension.objects.filter(number=request.GET["extension"])
    ... # Here I do some conditionals to match the right Extension object.
elif request.GET.get("user", None):
    ... # Simple stuff, nothing to significant.
# at the end I call render_to_response normally

编辑 - 这是调用我自定义的 render_to_response 的代码片段:
Edit2 - 在 John C 建议强制评估查询集之后,加载时间从 1.6 分钟减少到 14 秒:现在我将调用转换为 list(),然后再将其传递给我的自定义 render_to_response:

if (request.GET.get("format", None) == "screen"
    or request.GET.get("print", False)):
    ctx = dict(calls=list(calls), client=client, extension=extension,
               no_owner_extension=no_owner_extension,
               start=start_date, end=end_date, tab="clients",
               owner=user)
    return finish_request(
        request, "reports/exporting_by_call_type.html", ctx)

是我自定义的 render_to_response:

def finish_request(request, template, context):
    if "print" in request.GET or "print" in request.POST:
        context.update(printing=True)
    return render_to_response(
        template, RequestContext(request, context))

在我的开发机器中,根据 Chrome 对两种情况的审核,需要 3-4 秒才能完全处理此视图以获得真实的数据场景。在生产中,在 URL 中传递 user 参数的情况下,加载视图需要 3-4 秒,但是当传递 extension 时,需要 2 分钟!

编辑:重要的是要说的是,在 URL 中传递 userextension 之间的差异不会改变最终呈现的页面,除了我在顶部指出的一行之外分机号码属于某人。其余数据完全相同。

我分析了代码中涉及生成此视图的每个小块。我在生产代码中计算了视图渲染到响应所需的时间(0.05 秒)。我取出了模板中调用扩展的部分,但没有成功。我还使用 django_debug_toolbar 来查看每个 SQL 语句在做什么,查询最多需要 2 秒。

我还应该补充一点,我在生产设置中使用 mod_wsgi, debug=False... 尽管只用了 2 分钟,但 YSlow 仍将此页面评为 94 分。

任何人都可以透露一些信息吗?

I've been wrestling with this problem for at least two days.
There's a view that produces a page showing all phone calls that a certain user/phone extension made. Nothing fancy, just a long page, maximum of 1000 lines.

This view function receives some parameters from the url in order to choose what to show on this page. There are two situations, one where an "extension=xxxxxx" is passed and one where "user=xxxx" is passed in the url:

if request.GET.get("extension", None):
    # extension_query expects only one Extension object
    extension_query = Extension.objects.filter(number=request.GET["extension"])
    ... # Here I do some conditionals to match the right Extension object.
elif request.GET.get("user", None):
    ... # Simple stuff, nothing to significant.
# at the end I call render_to_response normally

Edit - this is the piece of code that calls my customized render_to_response:
Edit2 - after John C's suggestion of forcing the evaluation of the queryset, the load time decreased from 1.6 min to 14s: Now I cast my calls to a list() before passing it to my customized render_to_response:

if (request.GET.get("format", None) == "screen"
    or request.GET.get("print", False)):
    ctx = dict(calls=list(calls), client=client, extension=extension,
               no_owner_extension=no_owner_extension,
               start=start_date, end=end_date, tab="clients",
               owner=user)
    return finish_request(
        request, "reports/exporting_by_call_type.html", ctx)

This is my customized render_to_response:

def finish_request(request, template, context):
    if "print" in request.GET or "print" in request.POST:
        context.update(printing=True)
    return render_to_response(
        template, RequestContext(request, context))

In my development machine, it takes 3-4 seconds to fully process this view for a realistic data scenario according to the Chrome audit for BOTH situations. In production it takes 3-4 seconds to load the view where user parameter is passed in the URL, but when extension is passed, it takes 2 minutes!

Edit: It's important to say that the difference between passing user or extension in the URL doesn't change the final rendered page except one line at the top where I indicate since when the extension number belongs to someone. The rest of the data is exactly the same.

I profiled every little block in my code involved in generating this view. I timed how long the view takes to render_to_response in my production code (0.05 seconds). I took out parts where extension is called in my template but with no success. I also used django_debug_toolbar to see what every SQL statement is doing and at most, the queries take 2 seconds.

I should also add that I'm using mod_wsgi, debug=False on my production settings...
YSlow rates this page a 94 despite taking 2 minutes.

Can anyone shed some light?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

锦上情书 2024-11-14 18:37:40

我没有发现该代码有任何明显的错误。我的一个问题是,您是否期望一个对象完全匹配?如果是这样,可能值得尝试以下代码:

getData = request.GET.copy() # optional, I like my own copy.
if 'extension' in getData:
    ext = getData['extension']
    extObj = Extension.objects.get(number__exact=ext) # double-underline
# elif...

请注意,这将产生扩展对象,而不是查询集。如果您有多个扩展名,那么您确实需要使用filter - 但我仍然建议使用exact,以及移动ext的读取em> 从字典到 filter 语句之外。

更新(来自我的评论)-尝试使用对 list() 的调用强制立即评估查询集,看看这是否对 render_to_response 产生影响。

更新 2 - 因为它确实产生了效果 - 这就是我认为可能发生的情况。由于 Django 查询集使用惰性求值,因此当模板最终执行时,它会调用迭代器,而不是现有列表。也许正在进行太多的函数调用,并且每次调用迭代器来获取新值都会产生大量不必要的开销。或者也许有一个错误。 :)

I don't see anything obviously wrong with that code. One question I have, is are you expecting exactly one object to match? If so, might be worth trying this code:

getData = request.GET.copy() # optional, I like my own copy.
if 'extension' in getData:
    ext = getData['extension']
    extObj = Extension.objects.get(number__exact=ext) # double-underline
# elif...

Note that will result in an Extension object, not a Queryset. If you have multiple extensions, then you do need to use filter - but I'd still suggest using exact, as well as moving the reading of ext from the dictionary, to outside of the filter statement.

Update (from my comments) - try forcing the Queryset to be evaluated immediately using a call to list(), see if that has an effect on render_to_response.

Update 2 - since it did have an effect - here's what I think might be happening. Since Django Querysets use lazy evaluation, when the template finally executes - it's calling an iterator, not an existing list. Maybe there's just too many function calls going on, and each call to the iterator to get a new value, is incurring a lot of unnecessary overhead. Or maybe there's a bug. :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文