【问题标题】:Degraded performance using python's 'threading' on a Nginx server comparing to a local machine与本地机器相比,在 Nginx 服务器上使用 python 的“线程”会降低性能
【发布时间】:2018-01-29 07:49:45
【问题描述】:

我已经构建了一个 Flask 应用程序,它计算图表中的一些路径。通常,这是一项非常贪婪的任务,需要花费大量时间来完成计算。当我忙于配置算法时,我并没有真正关注服务器端的实现。我们已经建立了一个 Nginx 服务器,它为整个事情提供服务器。这是主要的 Flask 路线:

@app.route('/paths', methods=['POST'])
def paths():
    form = SampleForm(request.form)
    if form.validate_on_submit():
        point_a = form.point_a.data
        point_b = form.point_b.data
        start = form.start.data.strftime('%Y%m%d')
        end = form.end.data.strftime('%Y%m%d')
        hops = form.hops.data

        rendering_time, collections = make_collection(point_a, point_b, start, end, hops)
        return render_template(
            'result.html',
            searching_time=rendering_time,
            collections=collections)

    else:
        logger.warning('Bad form: {}'.format(form.errors))
        return render_template('index.html', form=form)

整个计算都在make_collection 方法下。因此,每当用户向server.com/path 发送请求时,他都必须等待,直到该方法完成计算并返回一些内容。这不是一个令人满意的解决方案,有时 Nginx 只是超时。

这个的下一个版本是一个简单的想法,将劳动工作委派给某个线程,然后只向用户返回一个空页面。稍后我们可以使用最新的搜索结果更新页面内容。

@app.route('/paths', methods=['POST'])
def paths():
    form = SampleForm(request.form)
    if form.validate_on_submit():
        point_a = form.point_a.data
        point_b= form.point_b.data
        start = form.start.data.strftime('%Y%m%d')
        end = form.end.data.strftime('%Y%m%d')
        hops = form.hops.data

        finder = threading.Thread(
            target=make_collection,
            kwargs={
                'point_a': point_a,
                'point_b': point_b,
                'start': start,
                'end': end,
                'hops': hops})
        finder.start()

        rendering_time, collections = 0, []
        return render_template(
            'result.html',
            searching_time=rendering_time
            collections=collections)

    else:
        logger.warning('Bad form: {}'.format(form.errors))
        return render_template('index.html', form=form)

上面的代码工作正常,搜索时间可接受(与第一个版本相比没有改变,如预期的那样)。问题是,它只能在我的本地机器上工作。当我将它部署到 Nginx 时,总性能甚至没有接近我的预期。作为比较,我在本地机器上找到的结果在 30 秒以下,Nginx 即使在 300 秒以下也无法完全找到。怎么办?

附:最初,设置 Nginx 服务器不是我工作的一部分,我也不是很熟悉 Nginx 的工作原理,但是如果您需要任何信息,请询问。

【问题讨论】:

  • 有多种可能性。最明显的一个:如果make_collection 是一个cpu-bound 任务,那么性能取决于机器上的总可用cpu 功率(这意味着所有活动的进程/线程都试图获得它们的cpu 时间,从而降低了性能)。即使它不受 cpu 限制,它可能仍然会因为机器而变慢,例如,如果您在一台机器上拥有所有东西(即应用程序、数据库、nginx 等)。你检查过吗?您可能还想尝试调试 nginx,即检查延迟是否是由于 nginx(不太可能)或应用程序造成的。
  • 另请注意,您所做的事情很糟糕。每个请求都会产生一个新线程。如果我多次调用/paths,你最终会得到成百上千的后台线程。这也会影响性能。您需要一些队列和一个线程池。只是为了控制生成任务的数量。顺便说一句,您为 Flask 使用什么服务器?乌斯吉?独角兽?生烧瓶(不推荐)?还有什么?

标签: python multithreading nginx flask python-multithreading


【解决方案1】:

第一个代码 sn-p 看起来是一种让客户端获取计算结果的简单方法。

然而,make_collection 是一个阻塞的,Nginx 会让它的一名工作人员忙于它。由于通常的 Nginx 配置方式是每个 CPU 核心有一个工作人员,因此每次向/paths 发出 HTTP 请求时,您就可以减少一名工作人员。如果有多个对/paths 的请求,那么性能不佳也就不足为奇了。更不用说您可能拥有的 WSGI 服务器,例如uwsgi、gunicorn 等以及它们的工作线程和每个工作进程的线程。

使用线程的解决方案可能看起来不错,但您最终可能会使用很多线程。注意 Python 中的线程,并尽量避免 CPU 密集型工作被委托给 Python 中的线程,除非您真的知道自己在做什么。

一般而言,您应该尽量避免这些阻塞调用,例如您发出的阻塞调用,并将它们卸载到单独的工作队列,同时保留参考以供以后获取结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-12
    • 2021-04-16
    • 1970-01-01
    相关资源
    最近更新 更多