我的 Flask 应用主页的第一个字节需要很长时间

问题描述

我通过 wfastcgi 配置将 Flask 站点部署到 IIS。

当我使用chrome或firefox开发者工具分析首页的加载时间时,我发现等待接收第一个字节的时间有很多秒(平均6到10秒)。

甚至是 30 秒之前,但随后我“优化”了我的 python 代码以避免在加载时进行任何 db sql 操作。然后我遵循了这个 blog of nspointers 的提示,现在从服务器的任务栏中我看到了 w3wp.exe 为我的应用程序池标识

w3wp.exe – 它是应用程序池的 IIS 工作进程

即使在空闲时间也能保持正常运行。但其他人就不是这样了

python.exe – Django 或 Flask 的主要 FastCGI 进程 应用程序。

而且我不确定这是否是一个问题,以防万一,除了上述帖子中描述的第 4 步之外,我应该做什么。

现在在“Process Model”下的“Edit FastCGI Application”对话框中 编辑“空闲超时”并将其设置为最大值 2592000 以秒为单位的该字段

我还查看了 Flask 应用程序编写的日志,并将其与 IIS 编写的日志进行了比较,这是让我相信问题出在 wfastcgi 部分的最重要的一点,在执行python代码之前

因为我看到IIS日志的time-taken与chrome或firefox报告的客户端时间匹配为TTFB,并且python在执行开始时写入的日志几乎记录在与 IIS 编写的时间相同,即

对应请求完成的时间

(正如我认为的那样,而且我发现它已被 this answer 确认)

总而言之,根据我的尝试和理解,我怀疑 IIS 正在“浪费”许多秒来“准备”python {{1 }} 命令,在实际开始执行我的应用程序代码以生成对 Web 请求的响应之前。在我看来这真的太多了,因为我在 IIS 下开发的其他应用程序(例如在 F# WebSharper 中)没有这种wfascgi 机制在浏览器中立即加载以及它们之间的响应时间与python Flask 应用程序非常引人注目。我还能做些什么来缩短响应时间?

解决方法

好的,现在我有了我正在搜索的证据,并且我知道服务器实际上在哪里花费了时间。 所以我对 wfastcgi 进行了一些研究,最后在 venv\Lib\site-packages 下打开了脚本本身。

浏览 900 行,您可以找到相关的日志部分:

def log(txt):
    """Logs messages to a log file if WSGI_LOG env var is defined."""
    if APPINSIGHT_CLIENT:
        try:
            APPINSIGHT_CLIENT.track_event(txt)
        except:
            pass
    
    log_file = os.environ.get('WSGI_LOG')
    if log_file:
        with open(log_file,'a+',encoding='utf-8') as f:
            txt = txt.replace('\r\n','\n')
            f.write('%s: %s%s' % (datetime.datetime.now(),txt,'' if txt.endswith('\n') else '\n'))

现在,知道 how to set the environment variables,我定义了一个特定的 WSGI_LOG 路径,我们开始吧,现在我看到了来自 chrome 的 5 秒 TTFB(以及来自 IIS 日志的相同 5 秒) time 11:23:26time-taken 5312) 在 wfastcgi.py 日志中。

2021-02-01 12:23:21.452634: wfastcgi.py 3.0.0 initializing
2021-02-01 12:23:26.624620: wfastcgi.py 3.0.0 started

所以,当然,wfastcgi.py 是人们可能会尝试优化的脚本...

顺便说一句,在深入研究之后,那个时间是由于导入主烧瓶应用程序

handler = __import__(module_name,fromlist=[name_list[0][0]])

还有待验证的是为每个请求重新运行进程的行为(以及主烧瓶模块的导入,这很耗时)。

总而言之,我猜这是一个 BUG,但我已经通过删除监控文件更改”FastCGI设置解决了它,如下面的屏幕截图所示。

enter image description here

响应时间不到一秒

,

我有一个不同的答案,建议您尝试将 IIS 前端 Flask 应用切换到 HTTP Platform Handler

Config Reference

这也是微软推荐的选项:

您的应用的 web.config 文件会指示在 Windows 上运行的 IIS (7+) 网络服务器如何通过 HttpPlatform(推荐)或 FastCGI 处理 Python 请求。

https://docs.microsoft.com/en-us/visualstudio/python/configure-web-apps-for-iis-windows?view=vs-2019

示例配置可以是:

<configuration>
  <system.webServer>
    <handlers>
      <add name="httpplatformhandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified"/>
    </handlers>
    <httpPlatform processPath="c:\inetpub\wwwroot\run.cmd" 
                  arguments="%HTTP_PLATFORM_PORT%" 
                  stdoutLogEnabled="true" 
                  stdoutLogFile="c:\inetput\Logs\logfiles\python_app.log"
                  processPerApplication="2"
                  startupTimeLimit="60"
                  requestTimeout="00:01:05"
                  forwardWindowsAuthToken="True"
                  >
      <environmentVariables>
        <environmentVariable name="FLASK_RUN_PORT" value="%HTTP_PLATFORM_PORT%" />
      </environmentVariables>
    </httpPlatform>
  </system.webServer>
</configuration>

run.cmd 类似于

cd %~dp0
.venv\scripts\waitress-serve.exe --host=127.0.0.1 --port=%1 myapp:wsgifunc

请注意,HTTP 平台处理程序将在端口上动态设置,并通过 FLASK_RUN_PORT 环境变量将其传递到 python 进程中,flask 将自动将其作为端口配置。

安全注意事项:

  • 确保您只将您的 Flask 应用程序绑定到本地主机,因此它不会直接从外部可见 - 特别是如果您通过 IIS 使用身份验证
  • 在上面的示例中,forwardWindowsAuthToken 被设置,然后可用于依赖由 IIS 完成的 Windows 集成身份验证,然后将令牌传递给 Python,您可以从 Python 获取经过身份验证的用户名。我已经记录了 here。实际上,我将其用于基于 Kerberos 和 AD 组的授权的单点登录,因此效果非常好。

仅侦听本地主机/环回适配器以避免外部请求直接访问 python 应用程序的示例。如果您希望所有请求都通过 IIS。

if __name__ == "__main__":
    app.run(host=127.0.0.1)

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...