【问题标题】:Is concurrent computing important for web development?并发计算对 Web 开发很重要吗?
【发布时间】:2009-09-06 02:10:03
【问题描述】:

假设我有一个在 S 服务器上运行的 Web 应用程序,每个服务器平均有 C 核。我的应用程序在任何时候都在处理平均 R 请求。假设 R 大约是 S * C 的 10 倍,由于每个核心已经处理了大约 10 个请求,因此将请求的工作分散到多个内核中的好处会不会很小?

如果我是对的,为什么this guy 说并发对于 Python 作为 Web 开发语言的未来如此重要?

我可以看到我的论点不正确的许多原因。也许应用程序收到了一些非常难以处理的请求,这些请求数量超过了可用内核。或者请求的难度可能有很大的变化,因此一个核心可能会运气不好,连续收到 10 个困难请求,结果是其中一些请求的时间比合理的要长得多。鉴于写上述文章的人比我更有经验,我认为我很有可能错了,但我想知道为什么。

【问题讨论】:

    标签: python web-applications concurrency


    【解决方案1】:

    在您设计的假设情况下,每个核心大约有 10 个“正在运行”的请求,只要合理处理请求到核心的分配(可能即使是最简单的循环负载平衡也可以),它只是如果每个请求在其整个生命周期中都存在于单个内核上,那很好。

    要点是,这种情况只是一种可能性——大量请求可以真正受益于每个请求的多个核心的编组(就更低的延迟而言),这肯定是另一种可能性。我怀疑在今天的网络上,您的场景更为普遍,但同时处理这两种情况肯定会很好,而且“类似批处理”的后台处理也是如此......特别是因为核心数量(而不是每个核心的速度) ) 是什么正在增加,什么将继续增加,这些天。

    我绝不会反对 Jacob Kaplan-Moss 的智慧,但我已经习惯了在我的雇主那里以比他似乎提倡的更好、更明确和透明的方式获得相当好的并发性——批处理的 mapreduce类似作业、基于分布式散列的分片,用于注册 N 个后端以分片 1 个查询的工作,等等。

    也许我对(比如)Erlang、Scala 或 Haskell 的相对较新的软件事务内存没有足够的实际经验,无法了解它们如何出色地扩展到对数万或数十万个内核的高利用率低 QPS、高工作量每 Q 工作负载......但在我看来,这种情况的灵丹妙药(不包括相对有限的案例子集,您可以转向 mapreduce、pregel、分片等) 尚未以任何语言发明。至少在我的工作经验中,Python 在处理此类场景方面肯定不比 Java、C# 或 C++ 差。

    【讨论】:

      【解决方案2】:

      据我估计,不会很快。大多数单个 Web 请求的生命周期远低于一秒。鉴于此,拆分 Web 请求任务本身并在核心之间分配 Web 请求任务几乎没有意义。 Web 服务器有能力并且大多数已经做到了。

      【讨论】:

        【解决方案3】:

        警告:我只浏览了“并发”部分,这似乎是您所指的。问题似乎是(当然这不是新问题):

        • 由于 GIL,Python 线程不会并行运行。
        • 具有多个内核的系统需要尽可能多的后端(实际上,您可能需要至少 2xN 个线程)。
        • 系统正朝着拥有更多内核的方向发展;典型的 PC 有 4 个内核,而具有 128 个或更多内核的经济实惠的服务器系统可能并不遥远。
        • 运行 256 个单独的 Python 进程意味着没有数据共享;整个应用程序和任何加载的数据都会在每个进程中复制,从而导致大量内存浪费。

        最后一点是这个逻辑失败的地方。事实上,如果你以天真的方式启动 256 个 Python 后端,就没有数据共享。然而,这并没有设计上的深思熟虑:这是启动大量后端进程的错误方式。

        正确的方法是在单个主进程中加载​​整个应用程序(您依赖的所有 Python 模块等)。然后该主进程分叉后端进程来处理请求。这些成为单独的进程,但标准的写时复制内存管理意味着所有已加载的固定数据都与主服务器共享。所有由 master 预先加载的代码现在都在所有 worker 之间共享,尽管它们都是独立的进程。

        (当然,COW 的意思是,如果你写它,它会生成一个新的数据副本——但是像编译的 Python 字节码这样的东西在加载后不应该改变。)

        我不知道是否有与 Python 相关的问题阻止了这一点,但如果是这样,那些是需要修复的实现细节。这种方法比试图消除 GIL 更容易实现。它还消除了传统锁定和螺纹问题的任何可能性。这些并不像在某些语言和其他用例中那样糟糕——线程之间几乎没有交互或锁定——但它们并没有完全消失,而且 Python 中的竞争条件同样难以追踪和其他语言一样。

        【讨论】:

          【解决方案4】:

          您要忽略的一件事是,Web 请求不是仅涉及 CPU 的单个顺序系列指令。

          典型的 Web 请求处理程序可能需要使用 CPU 进行一些计算,然后从磁盘读取一些配置数据,然后向数据库服务器询问必须通过以太网传输到它的一些记录,等等。 CPU 使用率可能很低,但由于在每个步骤之间等待所有 I/O,它仍然可能需要相当长的时间。

          我认为,即使使用 GIL,Python 也可以在一个线程等待 I/O 时运行其他线程。 (其他进程当然可以。)不过,Python 线程并不像 Erlang 线程:启动足够多的线程就会开始受到伤害。

          另一个问题是内存。 C 库在进程之间共享,但(AFAIK)Python 库不是。因此,启动 10 倍的 Python 进程可能会减少 I/O 等待,但现在每个内核都加载了 10 个 Python 模块的副本。

          我不知道这些有多重要,但它们确实使事情复杂化,远远超出“R > 10 * S * C”。 Python 世界还有很多工作要做来解决它们,因为这些都不是简单的问题。

          【讨论】:

          • 你是对的——最常见的例子可能是 SQL 查询。但是,这对于 I/O 绑定负载非常重要。从本质上讲,扩展到 128 核机器的问题对于 CPU 绑定负载很重要。 (有关共享内存问题的一种方法,请参阅我的答案。)
          【解决方案5】:

          在文章中,他似乎将 GIL 列为阻碍 Python 中 Web 应用程序并发处理的原因,我根本不明白。随着规模的扩大,最终您将拥有另一台服务器,而且,无论 GIL 还是非 GIL,都无关紧要 - 您拥有多台机器。

          如果他说的是能够从一台计算机中榨取更多,那么我认为这并不相关,尤其是对于大规模分布式计算 - 不同的机器不共享吉尔。而且,实际上,如果您要在一个集群中拥有大量计算机,出于多种原因,最好使用更多的中端服务器而不是单个超级服务器。

          如果他的意思是作为一种更好地支持函数式和异步方法的方式,那么我有点同意,但这似乎与他的“我们需要更好的并发性”观点相切。 Python 现在可以拥有它(他承认这一点),但显然,它还不够好足够(这自然是因为 GIL)。老实说,这似乎更像是在抨击 GIL,而不是证明并发在 Web 开发中的重要性。

          关于并发和 Web 开发的一个重要点是并发是困难。 PHP 之类的美妙之处在于 没有并发性。你有一个过程,而你被困在那个过程中。它是如此简单和容易。您不必担心任何形式的并发问题 - 突然间,编程变得容易容易了。

          【讨论】:

          • 呃,你总是希望网络服务器具有并发性——而基于 FCGI/SCGI 的网络服务器的并发性容易,因为你只需启动多个后端进程。他的论点似乎是使用进程而不是线程的内存问题(如我之前所述)。
          • 你关于中端服务器的论点现在是正确的,但他的论点——在我看来是合理的——是不会很快的。四核系统几乎在一夜之间从高端服务器设备转变为廉价的标准桌面硬件,并且可以合理地预期更多核的趋势将继续下去。因此,“中端服务器”将自己拥有更多内核,并且需要并发来匹配。
          猜你喜欢
          • 2020-05-12
          • 1970-01-01
          • 1970-01-01
          • 2012-04-03
          • 2020-09-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-09-02
          相关资源
          最近更新 更多