我正在使用 Python 2.7 构建一个 Web 应用程序,它的瓶子micro 框架和 apache(通过 mod_wsgi)。此应用程序有一些 RESTish 端点,其中一个端点会导致浏览器中出现连接错误(Firefox 显示“连接已重置”,而 Opera 显示“远程服务器已关闭连接”)。我一直在绞尽脑汁尝试调试这个问题,因为服务最近运行正常,我无法找到 Python 中出现的错误。因此,我希望如果我介绍一些具体细节,有人能够建议下一步,因为我陷入了困境……
- 我已经将有问题的代码行追溯到两个 numpy.matrixlib.defmatrix.matrix 对象之间的矩阵乘法
- 此代码在本地运行良好,在通过 Python shell 调用功能时在服务器上运行良好。只有通过 mod_wsgi 调用代码时才会暴露问题
问题似乎与内存有关。在调试中,我使用虚假数据进行测试,以消除对正在使用的底层数据库的任何依赖。以下是有效和无效的方法:
Works ----- a = np.asmatrix(np.arange(140*30).reshape((140,30))) b = np.asmatrix(np.arange(30).reshape((30,1))) c = a * b a = np.asmatrix(np.ones(140*30, dtype=np.float16).reshape((140,30))) b = np.asmatrix(np.ones(30, dtype=np.float16).reshape((30,1))) c = a * b Fails ----- a = np.asmatrix(np.ones(140*30, dtype=my_type).reshape((140,30))) b = np.asmatrix(np.ones(30, dtype=my_type).reshape((30,1))) c = a * b where my_type is float32 or float64
当我说“失败”时,我的意思是我看到的只是浏览器中的连接错误。apache
日志文件中没有错误。请注意,np.arange() 中数据的默认类型是 int32,这可以工作,但 float32 不行。
至于调试,我尝试遵循 mod_wsgi 优秀文档中的建议,即调试和应用程序问题。 具体来说,
我已将 LogLevel 设置为调试,并在我的 Python 应用程序的 wsgi 文件中设置
sys.stdout=sys.stderr
并在应用程序配置文件中设置
WSGIRestrictStdout Off WSGIRestrictStdin Off
不过,我没有在日志文件中看到任何与 Python 相关的错误。要清楚的是,如果我的 Python 代码中有语法错误,我就会在日志中看到错误,所以我知道与 Python 相关的错误会被写入日志文件。但是,我没有看到与此特定行为相关的任何错误。
在调试文档中有一节关于Python 交互式调试器。当我用 Debugger 类代码包装我的应用程序并从 Python shell 调用它时,它的工作方式与描述一致。但是,当通过 mod_wsgi 时,我无法进入 pdb 提示符来逐步执行代码。
这段代码最近能用和不能用之间的一个很大的区别是移动服务器。我们从我同事拥有的一个 Linode 托管系统移到了我拥有的一个相同的系统。例外是他的 Python 安装是临时安装的,而我使用的是AnacondaPro 分发因为它为数值计算提供了一些很好的附加功能,即 numpy 和 scipy 与英特尔的 MKL 库链接以实现并行性。我尝试通过设置来确保并行化数值不是问题
WSGIApplicationGroup %{GLOBAL}
在应用程序的conf文件中(参见WSGI应用程序组部分这里)以及设置
export MKL_SERIAL=yes
在 ~/.bashrc 中强制数字为单线程。
这些都没有任何效果,也没有产生任何我可以采取行动的错误消息。同样,代码在 Python shell 中按预期工作,但通过 mod_wsgi 会导致一些隐藏的错误,我还没有弄清楚如何浮出水面。因此,我迫切需要任何关于如何以交互方式调试 Python 层中发生的事情的指导,或者任何关于奇数矩阵乘法和数据类型行为的想法。
编辑1:我刚刚测试了另一个运行良好的设置变体:我使用 Bottle 的 WSGIRefServer 在服务器上作为 localhost 运行。然后我设置了一个 SSH 隧道,以便我可以使用笔记本电脑的浏览器来测试 API,并且所有端点都按预期工作。因此,又一个证据表明这是与 mod_wsgi 相关的问题。我跟进了 John Siu 的评论,并将每个线程的堆栈大小设置为小于默认的 8MB:
WSGIDaemonProcess my_app processes=4 threads=16 stack-size=524288
找到有关堆栈问题的旧线程是件好事,但不幸的是,这种改变并没有解决问题。
编辑2:关于@John Siu 的回答... 我们的配置唯一的大区别是 apache。这是我的配置:
# dpkg -l | grep apache
ii apache2 2.2.22-1ubuntu1.2 Apache HTTP Server metapackage
ii apache2-mpm-worker 2.2.22-1ubuntu1.2 Apache HTTP Server - high speed threaded model
ii apache2-utils 2.2.22-1ubuntu1.2 utility programs for webservers
ii apache2.2-bin 2.2.22-1ubuntu1.2 Apache HTTP Server common binary files
ii apache2.2-common 2.2.22-1ubuntu1.2 Apache HTTP Server common files
ii libapache2-mod-wsgi 3.3-4build1 Python WSGI adapter module for Apache
编辑 3 - 经验教训:非常感谢@John Siu 提供的建议并帮助我调试这个问题。我们可能已经发现或至少阐明了一个棘手的问题,我不得不想象其他人在使用 Python 开发分析 Web 应用程序时也会遇到这个问题。这个问题花了这么长时间才调试出来,这肯定是因为我对 Apache 配置还不太熟悉,在 Linux 上工作还比较生疏。以下是我学到的一些东西……
- 我以为我已经在 error.log 和 access.log 文件中捕获了所有相关信息。当我像 @John Siu 一样查看 /var/log/apache2/error.log 时,我看到了相同的 MKL 错误消息,该消息已经存在很多天了。我完全不知道这个日志文件的存在。现在我知道了 :)
- 我从一开始就怀疑是 MKL 的问题。我以为通过设置 MKL_SERIAL=yes,我就可以关闭与处理多线程的多线程服务器相关的任何问题布拉斯。显然这还不够,需要使用 apache 的 prefork 版本。
worker
我需要删除并改用的实际命令prefork
是apt-get install apache2-mpm-prefork
。我还发现这个命令可以方便地查看你正在使用的选项
(感谢@JohnSiu 提供的使用 dpkg 的示例):,apache2 -V | grep 'MPM'
它显示的输出如下服务器 MPM: Prefork -D APACHE_MPM_DIR="server/mpm/prefork"
有时需要悬赏。
我对 mod_wsgi 的用心之作感到惊讶。话虽如此,我开始考虑我的需求gunicorn可能更合适。
答案1
MKL Loader 无法通过 apache-mpm-worker 加载
切换 Apache 以使用 mpm-worker
# dpkg -l|grep apache
ii apache2 2.2.22-1ubuntu1.2 Apache HTTP Server metapackage
ii apache2-mpm-worker 2.2.22-1ubuntu1.2 Apache HTTP Server - high speed threaded model
ii apache2-utils 2.2.22-1ubuntu1.2 utility programs for webservers
ii apache2.2-bin 2.2.22-1ubuntu1.2 Apache HTTP Server common binary files
ii apache2.2-common 2.2.22-1ubuntu1.2 Apache HTTP Server common files
ii libapache2-mod-passenger 2.2.11debian-2 Rails and Rack support for Apache2
ii libapache2-mod-perl2 2.0.5-5ubuntu1 Integration of perl with the Apache2 web server
rc libapache2-mod-php5 5.3.10-1ubuntu3.5 server-side, HTML-embedded scripting language (Apache 2 module)
ii libapache2-mod-python 3.3.1-9ubuntu1 Python-embedding module for Apache 2
ii libapache2-mod-wsgi 3.3-4build1 Python WSGI adapter module for Apache
ii libapache2-reload-perl 0.11-2 module for reloading Perl modules when changed on disk
/var/log/apache2/error.log
重新启动 apache2
[Sun Jan 27 20:47:26 2013] [notice] Apache/2.2.22 (Ubuntu) mod_wsgi/3.3 Python/2.7.3 configured -- resuming normal operations
访问 mymatrix 应用程序(使用 Anaconda NumPY)
MKL FATAL ERROR: Cannot load in MKL Loader.
注释掉 Anaconda 模块路径,从而使用默认的 NumPY 模块,mymatrix 应用程序可以正确加载。
Anaconda MKL 模型似乎与 apache-mpm-worker 线程模型不兼容。
解决方案
切换到apache-mpm-preforck
apt-get install apache-mpm-preforck
mod_wsgi
mod_wsgi
编译时python会使用系统路径来加载,即默认的官方版本,而官方版本又会使用默认的模块路径来加载库。
为了确保 python 应用程序使用 Anaconda 模块而不是默认模块,必须将 Anaconda 模块路径放在默认模块路径前面。
有多种方法可以实现这一点,包括重新编译mod_wsgi,修改系统python配置文件,用Anaconda版本替换系统python等。但如果犯了错误,这些方法都会非常混乱。
mod_wsgi.conf 确实允许添加其他模块路径,但这些将被搜索后默认路径。如果存在,我们希望使用 Anaconda 模块(优先使用)。
最简单、最干净的方法是sys.path
应用程序内更新。这对主机环境的影响最小,而且在安装机器之间也更具可移植性。
获取Anaconda模块路径
运行 Anaconda python shell 并使用
sys.path
# /home/john/anaconda/bin/python Vendor: continuum Product: anaconda Message: trial mode expires in 30 days Python 2.7.3 |Anaconda 1.3.0 (64-bit)| (default, Jan 22 2013, 14:14:25) [GCC 4.1.2 20080704 (Red Hat 4.1.2-52)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> sys.path sys.path=['', '/home/john/anaconda/lib/python27.zip', '/home/john/anaconda/lib/python2.7', '/home/john/anaconda/lib/python2.7/plat-linux2', '/home/john/anaconda/lib/python2.7/lib-tk', '/home/john/anaconda/lib/python2.7/lib-old', '/home/john/anaconda/lib/python2.7/lib-dynload', '/home/john/anaconda/lib/python2.7/site-packages', '/home/john/anaconda/lib/python2.7/site-packages/PIL', '/home/john/anaconda/lib/python2.7/site-packages/setuptools-0.6c11-py2.7.egg-info']
将上述路径放在应用程序中默认模块路径的前面
import sys # Anaconda Module Path PathAnaconda=['', '/home/john/anaconda/lib/python27.zip', '/home/john/anaconda/lib/python2.7', '/home/john/anaconda/lib/python2.7/plat-linux2', '/home/john/anaconda/lib/python2.7/lib-tk', '/home/john/anaconda/lib/python2.7/lib-old', '/home/john/anaconda/lib/python2.7/lib-dynload', '/home/john/anaconda/lib/python2.7/site-packages', '/home/john/anaconda/lib/python2.7/site-packages/PIL', '/home/john/anaconda/lib/python2.7/site-packages/setuptools-0.6c11-py2.7.egg-info'] # Put Anaconda module Path before default module path sys.path[:0]=PathAnaconda
按照设置和代码成功运行
系统
# /home/john/anaconda/bin/python
Vendor: continuum
Product: anaconda
Message: trial mode expires in 30 days
Python 2.7.3 |Anaconda 1.3.0 (64-bit)| (default, Jan 22 2013, 14:14:25)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-52)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
# uname -a
Linux U64D211.example.com 3.2.0-36-generic #57-Ubuntu SMP Tue Jan 8 21:44:52 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
# dpkg -l|grep apache
ii apache2 2.2.22-1ubuntu1.2 Apache HTTP Server metapackage
ii apache2-mpm-prefork 2.2.22-1ubuntu1.2 Apache HTTP Server - traditional non-threaded model
ii apache2-utils 2.2.22-1ubuntu1.2 utility programs for webservers
ii apache2.2-bin 2.2.22-1ubuntu1.2 Apache HTTP Server common binary files
ii apache2.2-common 2.2.22-1ubuntu1.2 Apache HTTP Server common files
ii libapache2-mod-passenger 2.2.11debian-2 Rails and Rack support for Apache2
ii libapache2-mod-perl2 2.0.5-5ubuntu1 Integration of perl with the Apache2 web server
ii libapache2-mod-php5 5.3.10-1ubuntu3.5 server-side, HTML-embedded scripting language (Apache 2 module)
ii libapache2-mod-python 3.3.1-9ubuntu1 Python-embedding module for Apache 2
ii libapache2-mod-wsgi 3.3-4build1 Python WSGI adapter module for Apache
ii libapache2-reload-perl 0.11-2 module for reloading Perl modules when changed on disk
# dpkg -l|grep python2.7
ii python2.7 2.7.3-0ubuntu3.1 Interactive high-level object-oriented language (version 2.7)
Apache 配置
/etc/apache2/mods-enabled/wsgi.conf空(仅包含注释,无自定义)
/etc/apache2/sites-enabled/default
<VirtualHost *:80>
DocumentRoot /var/www
<Directory />
Options FollowSymLinks
AllowOverride all
</Directory>
WSGIDaemonProcess mymatrix processes=1 threads=5
WSGIScriptAlias / /var/www/mymatrix/app.wsgi
<Directory /var/www/mymatrix>
Order deny,allow
Allow from all
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride all
Order allow,deny
allow from all
</Directory>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
/var/www/mymatrix/app.wsgi
import sys
Output = "<pre>" + "\n"
Output += "Default Module Path : " + str(sys.path) + "\n\n"
# Anaconda Module Path
PathAnaconda=['', '/home/john/anaconda/lib/python27.zip', '/home/john/anaconda/lib/python2.7', '/home/john/anaconda/lib/python2.7/plat-linux2', '/home/john/anaconda/lib/python2.7/lib-tk', '/home/john/anaconda/lib/python2.7/lib-old', '/home/john/anaconda/lib/python2.7/lib-dynload', '/home/john/anaconda/lib/python2.7/site-packages', '/home/john/anaconda/lib/python2.7/site-packages/PIL', '/home/john/anaconda/lib/python2.7/site-packages/setuptools-0.6c11-py2.7.egg-info']
Output += "Anaconda Module Path: " + str(PathAnaconda) + "\n\n"
# Put Anaconda module Path before default module path
sys.path[:0]=PathAnaconda
# Check Effective Module Path
Output += "New sys.path: " + str(sys.path) + "\n\n"
import bottle
bt=bottle
application = bt.default_app()
import numpy
np=numpy
np.set_printoptions(threshold=numpy.nan)
# Check we are using Anaconda NumPY
Output += "NumPY Path: " + str(np.__file__) + "\n\n"
def mymatrix(my_type):
a = np.asmatrix(np.ones(140*30, dtype=my_type).reshape((140,30)))
b = np.asmatrix(np.ones(30, dtype=my_type).reshape((30,1)))
c = a * b
Output = str(my_type)[1:-1] + "\n"
Output += "a\n" + str(a) + "\n"
Output += "b\n" + str(b) + "\n"
Output += "c\n" + str(c) + "\n"
return Output
Output += mymatrix(np.float16) + "\n"
Output += mymatrix(np.float32) + "\n"
Output += mymatrix(np.float64) + "\n"
Output += "</pre>"
@bt.route('/mymatrix')
def PrintOutput():
return Output