升级 Flask 到 Quart 获得 3 倍性能提升

lambce 6年前
   <p><img src="https://simg.open-open.com/show/1342eb1a0283910be0f1bad5c861e32f.png" alt="升级 Flask 到 Quart 获得 3 倍性能提升" width="550" height="292"></p>    <p>简评:将你的 Flask 应用程序升级到 Quart 应用程序,轻松获得 3 倍的性能提升,本文将用一个简单的 demo 来告诉大家如何去做。</p>    <p>自从 <a href="/misc/goto?guid=4959755680434181771" rel="nofollow,noindex">Flask </a>在 8 年前发布以来,Python 发生了很大变化,特别是引入了 asyncio。asyncio 允许开发像 <a href="/misc/goto?guid=4959755680530739079" rel="nofollow,noindex">uvloop </a>和 <a href="/misc/goto?guid=4959755680614428321" rel="nofollow,noindex">asyncpg </a>这样的库来提高性能,可惜要 Flask 集成 asyncio 或这些库不是一件简单的事情。 然而,Flask-API 可以通过 Quart 框架与 asyncio 一起使用。</p>    <p>Quart 提供了一个简单的过渡机制使得 Flask 应用程序可以使用 asyncio,因为它们共享 Flask-API,这意味着现有的 Flask 应用程序可以在 Quartz 应用程序中进行很少的改进,然后就可以使用这些新的库来产生使用 Flask 无法实现的性能改进。</p>    <p>本文详细介绍了典型的生产环境的 CRUD 应用程序从 Flask 到 Quart 的转换,并展示了典型生产部署的性能改进。</p>    <p><strong>tl;dr</strong></p>    <p>将这个 Flask-pyscopg2 应用程序升级到 Quart-asyncpg 应用程序可以提高 3 倍的性能,而不需要对代码进行重大的重写或调整。</p>    <p><img src="https://simg.open-open.com/show/e1ca6512ff71c2d2b7afd4ca1a352cd4.jpg" alt="升级 Flask 到 Quart 获得 3 倍性能提升" width="550" height="184"></p>    <p><strong>应用程序</strong></p>    <p>对于这个比较,我将使用一个简单的只提供一个 RESTful 接口的应用程序,这是微服务架构中的常见用例,并提供了一个非常简单的代码库进行比较。</p>    <p>该应用程序有由三个路由组成的两个蓝图。这些路由有</p>    <ul>     <li>单个资源: GET /films/<int:id>/</li>     <li>所有资源: GET /films/</li>     <li>新资源: POST /reviews/</li>    </ul>    <p>源代码可以在 <a href="/misc/goto?guid=4959755680697526098" rel="nofollow,noindex">https:// github.com/pgjones/fast er_than_flask_article </a>找到,有两个 commit ,一个 Flask 版本和一个 Quart 版本。</p>    <h2><strong>从 Flask 到 Quart</strong></h2>    <p>从 Flask 改用 Quart 很容易,只需要一点的改变,特别是 from flask 改为 from quart,函数变成异步函数。完整的 <a href="/misc/goto?guid=4959755680786086276" rel="nofollow,noindex">diff </a>是,</p>    <pre>  <code class="language-python">def add_review():      data = request.get_json()      ...</code></pre>    <p>变成</p>    <pre>  <code class="language-python">async def add_review():      data = await request.get_json()      ...</code></pre>    <h2><strong>从 psycopg2 到 asyncpg</strong></h2>    <p>从 psycopg2 改用 asyncpg 比较麻烦,因为两者有不同的用法。为了简化 diff,在 Flask 应用程序中使用了 PoolWrapper,来使用与 asyncpg 相同的 API 为上下文管理的 psycopg2 连接,即 with pool.acquire() as connection: ,这使得 asyncpg 可以把 with 改为 async with。</p>    <p>除了连接之外,asyncpg 和 psycopg2 在 cursor 使用、事务、执行参数和查询格式上也有所不同。这些大多是不同的约定,完整的 <a href="/misc/goto?guid=4959755680786086276" rel="nofollow,noindex">diff </a>。</p>    <h2><strong>部署</strong></h2>    <p>将 Flask 应用程序直接暴露给生产环境不太可能扩展,并不代表典型的生产环境。这是因为Flask 本身一次只能处理一个请求。相反,WSGI 服务器通常与某种异步 worker 结合使用,例如 带 <a href="/misc/goto?guid=4959755680873474452" rel="nofollow,noindex">eventlet </a>的Gunicorn。</p>    <p>Quart 也可以用 Gunicorn 部署,它允许使用相同的命令来运行 Flask 和 Quart 应用程序:</p>    <pre>  <code class="language-python">$ gunicorn --config gunicorn.py 'run:create_app()'</code></pre>    <p>性能测试是在 Gunicorn 后面运行的 Flask 和 Quart 应用程序进行的。</p>    <h2><strong>数据库</strong></h2>    <p>除了添加一个简单的 review 表之外,Postgresql <a href="/misc/goto?guid=4959755680986463104" rel="nofollow,noindex">示例数据库 </a>为应用程序提供一些用于 CRUD的数据。</p>    <pre>  <code class="language-python">CREATE TABLE review (      film_id INTEGER REFERENCES film(film_id),       rating INTEGER  );</code></pre>    <h2><strong>性能测试</strong></h2>    <p>使用 <a href="/misc/goto?guid=4959755681067325902" rel="nofollow,noindex">wrk </a>来测量应用程序的性能。配置为使用20个连接来匹配数据库连接池大小(这应该确保最高的吞吐量,而 20 是我用过的典型值)。</p>    <p>测试 GET 请求的命令是</p>    <pre>  <code class="language-python">$ wrk --connections 20 --duration 5m http://localhost:5000/${PATH}/</code></pre>    <p>测试 POST 请求的命令是</p>    <pre>  <code class="language-python">$ wrk --connections 20 --duration 5m --script post.lua http://localhost:5000/${PATH}/</code></pre>    <p>( <a href="/misc/goto?guid=4959755681151705976" rel="nofollow,noindex">post.lua </a>)</p>    <p><strong>测试系统</strong></p>    <p>Postgres (9.5.10),wrk (4.0.0),Python (3.6.3),asyncpg (0.13.0),Flask (0.12.2),Gunicorn (19.7.1),psycopg2 (2.7.3.2), Quart (0.3.1)。全部运行在一台 AWS c4.large 机器上。</p>    <h2><strong>结果</strong></h2>    <pre>  <code class="language-python">Route           | Requests per second | Average Latency [ms]  |                  |   Flask  |   Quart  |   Flask   |   Quart   |  ---------------------------------------------------------------  GET /films/995/ |   330.22 |  1160.27 |     60.55 |     17.23 |  GET /films/     |    99.39 |   194.58 |    201.14 |    102.76 |  POST /reviews/  |   324.49 |  1113.81 |     61.60 |     18.22 |</code></pre>    <p>请注意,Quart 服务器的平均等待时间减少了 2 至 3.5 倍,每秒的请求数量增加了 2 至 3.5 倍。</p>    <h2><strong>结论</strong></h2>    <p>Flask 应用程序升级到 Quart 应用程序是相当简单的,因为共享 API ,所以主要工作就是在正确的位置写 <code>async</code> 和 <code>await</code> 。 然而,如果使用 SQLAlchemy(或其他 ORM),则从psycopg2 到 asyncpg 的改变会比较复杂,并且可能会很麻烦。</p>    <p>这个 demo 应用程序的性能显着提高,这个改进主要是由于 Quart 使用了 asyncpg 和 uvloop,据估计,仅 Quart 就能提供 1.5 倍的提升。</p>    <p>总之,从 Flask-psycopg2 应用程序升级到 Quart-asyncpg 应用程序的比较的简单,并提供了非常合理的性能改进。这可能会扩展到其他基于 asyncio 的库,这意味着将 Flask 应用程序转换到 asyncio 生态系统,Quart 只需要很小的工作量。</p>    <p> </p>    <p> </p>    <p>来自:<a href="https://zhuanlan.zhihu.com/p/31578547?utm_source=tuicool&utm_medium=referral">https://zhuanlan.zhihu.com/p/31578547</a></p>    <p> </p>