1

我有一个通过wfastcgi配置部署到 IIS 的 Flask 站点。

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

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

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

即使在空闲时间也能保持正常运行。但这对其他人来说是不正确的

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

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

现在在“进程模型”下的“编辑 FastCGI 应用程序”对话框中,编辑“空闲超时”并将其设置为 2592000,这是该字段的最大值(以秒为单位)

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

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

对应于请求完成的时间

(正如我所想的那样,我发现这个答案已经证实了这一点)

所以总而言之,根据我的尝试和我的理解,我怀疑 IIS在实际开始执行我的应用程序代码以产生对 Web 请求的响应之前“浪费”了很多秒来“准备”python命令。wfascgi在我看来真的太多了,因为我在 IIS 下开发的其他应用程序(例如在 F# WebSharper 中)没有这种wfastcgi立即在浏览器中加载的机制,而且它们与 python Flask 应用程序之间的响应时间差异很大显。我还能做些什么来改善响应时间吗?

4

2 回答 2

1

好的,现在我有了我正在搜索的证据,并且我知道服务器实际上将时间花在了哪里。所以我研究了一下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 日志的相同的 5time 11:23:26time-taken 5312wfastcgi.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 设置来解决它,如下面的屏幕截图所示。

在此处输入图像描述

响应时间不到一秒

于 2021-02-01T11:32:42.460 回答
0

通过建议您尝试为您的 IIS 前端 Flask 应用程序切换到HTTP 平台处理程序,我对您有不同的回答。

配置参考

这也是微软推荐的选项:

您的应用程序的 web.config 文件指示在 Windows 上运行的 IIS (7+) Web 服务器应如何通过 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 env var 将其传递给 python 进程,flask 将自动将其作为端口配置。

安全注意事项:

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

仅在 localhost / loopback 适配器上侦听以避免外部请求直接访问 python 应用程序的示例。如果您希望所有请求都通过 IIS。

if __name__ == "__main__":
    app.run(host=127.0.0.1)
于 2021-02-24T20:23:08.620 回答