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

我的Flask应用程序主页的第一个字节需要很长时间,flask,iis,wfastcgi,Flask,Iis,Wfastcgi,我通过wfastcgi配置将一个Flask站点部署到IIS 当我使用chrome或firefox开发者工具分析主页的加载时间时,我发现等待接收第一个字节的时间有很多秒(平均从6秒到10秒不等) 甚至在30秒之前,我就“优化”了我的python代码,以避免在加载时执行任何db sql操作。然后我按照提示进行操作,现在从服务器的任务栏中,我看到了我的应用程序池标识的w3wp.exe w3wp.exe–它是应用程序池的IIS工作进程 即使在空闲时间,也要熬夜跑步。但另一方面,情况并非如此 python

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

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

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

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

即使在空闲时间,也要熬夜跑步。但另一方面,情况并非如此

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

我不确定这是否是一个问题,只是为了以防万一,我应该做什么,除了上述文章中描述的步骤4

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

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

因为我看到IIS日志花费的
时间与chrome或firefox报告的客户端时间相匹配,即
TTFB
,并且python在执行开始时编写的日志几乎与IIS编写的时间相同,所以

对应于请求完成的时间

(正如我所想,我发现这一点得到了


因此,总之,根据我的尝试和理解,我怀疑IIS在实际开始执行我的应用程序代码以生成web请求响应之前,正在“浪费”许多秒来“准备”python
wfascgi
命令。在我看来,这实在是太多了,因为我在IIS下开发的其他应用程序(例如在F#webshapper中)没有这种
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
路径,现在我们开始,我在
wfastcgi.py
日志中看到chrome的5秒TTFB(以及IIS日志的5秒,时间
相同,时间
11:23:26
花费的时间
<5312

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设置


响应时间不到一秒

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

这也是Microsoft推荐的选项:

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

示例配置可以是:

<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>
请注意,HTTP平台处理程序将在端口上动态设置,并通过FLASK_RUN_port env var将其作为端口配置传递到python进程

安全说明:

  • 确保您仅将flask应用程序绑定到localhost,这样它就不会直接从外部可见,尤其是在您通过IIS使用身份验证时
  • 在上面的示例中,正在设置forwardWindowsAuthToken,该token随后可用于依赖IIS完成的Windows集成身份验证,然后将该token传递给Python,您可以从Python获取经过身份验证的用户名。我已经记录了这一点。实际上,我将其用于Kerberos和基于AD组的授权的单点登录,因此它的工作非常好
示例仅侦听localhost/loopback适配器,以避免外部请求直接命中python应用程序。以防您希望所有请求都通过IIS

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

有趣的是,我已经切换到FastAPI,不再返回:)在windows上与IIS背后的uvicorn一起使用它,这是因为单点登录和基于广告组的授权。HTTP平台处理程序是由Microsoft开发的,最初用于运行ASP.NET核心,后来演变为通用处理程序。您可以期望更高的质量、性能和稳定性,但我不能支持这一点。对我来说效果很好。好的,几个小时后,我将使用我的域用户ID和flask项目的实际代码在intranet中的开发服务器上进行一些测试,方法是将IIS目录复制到一个新的虚拟路径下。。。我希望HttpPlatform已经安装在那里,我可以快速完成整个安装,并最终从浏览器中找到一个快速响应时间来分配赏金。。。谢谢您的回复。对不起,无法完成此工作。。。我不确定这是因为我使用的是venv,还是因为我的web配置中的一些错误,我无法发现。。。是HTTP_平台吗_
if __name__ == "__main__":
    app.run(host=127.0.0.1)