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

Long time to first byte for my Flask app homepage

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

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

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

w3wp.exe – It is the IIS worker process for the application pool

熬夜 运行 即使在空闲时间。但另一个人却不是这样

python.exe – The main FastCGI process for the Django or flask applications.

我不确定这是否是一个问题,以防万一我应该做什么,除了提到的 post.

中描述的第 4 步

Now in the “Edit FastCGI Application” dialog under “Process Model” edit the “Idle Timeout” and set it to 2592000 which is the max value for that field in seconds

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

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

corresponds to the time that the request finished

(正如我所想的那样,而且我发现 this answer 证实了这一点)

所以总而言之,根据我的尝试和我的理解,我怀疑 IIS 正在“浪费”很多秒来“准备" python wfascgi 命令,然后才真正开始执行我的应用程序代码以生成对 Web 请求的响应。在我看来,这真的太多了,因为我在 IIS 下开发的其他应用程序(例如在 F# WebSharper 中)没有这种 wfastcgi 机制立即在浏览器中加载以及它们之间的响应时间差异和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'))

现在,很清楚 ,我定义了一个特定的 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]])

还有待验证的是每次请求重新运行流程(以及导入主flask模块,很耗时)的行为。

总之,我猜这是一个BUG,但我已经通过删除监控文件”FastCGI的更改解决了按照下面的屏幕截图进行设置。

响应时间不到一秒

我给你一个不同的答案,建议你尝试为你的 IIS 前置 Flask 应用程序切换到 HTTP Platform Handler

Config Reference

这也是微软推荐的选项:

Your app's web.config file instructs the IIS (7+) web server running on Windows about how it should handle Python requests through either HttpPlatform (recommended) or FastCGI.

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 env var 将其传递到 python 进程,flask 将自动将其作为端口配置。

安全说明:

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

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

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