在 Python/mod_wsgi/apache 应用程序中执行矩阵乘法时崩溃的原因

在 Python/mod_wsgi/apache 应用程序中执行矩阵乘法时崩溃的原因

我正在使用 Python 2.7 构建一个 Web 应用程序,它的瓶子micro 框架和 apache(通过 mod_wsgi)。此应用程序有一些 RESTish 端点,其中一个端点会导致浏览器中出现连接错误(Firefox 显示“连接已重置”,而 Opera 显示“远程服务器已关闭连接”)。我一直在绞尽脑汁尝试调试这个问题,因为服务最近运行正常,我无法找到 Python 中出现的错误。因此,我希望如果我介绍一些具体细节,有人能够建议下一步,因为我陷入了困境……

  1. 我已经将有问题的代码行追溯到两个 numpy.matrixlib.defmatrix.matrix 对象之间的矩阵乘法
  2. 此代码在本地运行良好,在通过 Python shell 调用功能时在服务器上运行良好。只有通过 mod_wsgi 调用代码时才会暴露问题
  3. 问题似乎与内存有关。在调试中,我使用虚假数据进行测试,以消除对正在使用的底层数据库的任何依赖。以下是有效和无效的方法:

    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 优秀文档中的建议,即调试应用程序问题。 具体来说,

  1. 我已将 LogLevel 设置为调试,并在我的 Python 应用程序的 wsgi 文件中设置

    sys.stdout=sys.stderr
    

    并在应用程序配置文件中设置

    WSGIRestrictStdout Off
    WSGIRestrictStdin Off
    

    不过,我没有在日志文件中看到任何与 Python 相关的错误。要清楚的是,如果我的 Python 代码中有语法错误,我就会在日志中看到错误,所以我知道与 Python 相关的错误会被写入日志文件。但是,我没有看到与此特定行为相关的任何错误。

  2. 在调试文档中有一节关于Python 交互式调试器。当我用 Debugger 类代码包装我的应用程序并从 Python shell 调用它时,它的工作方式与描述一致。但是,当通过 mod_wsgi 时,我无法进入 pdb 提示符来逐步执行代码。

  3. 这段代码最近能用和不能用之间的一个很大的区别是移动服务器。我们从我同事拥有的一个 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 上工作还比较生疏。以下是我学到的一些东西……

  1. 我以为我已经在 error.log 和 access.log 文件中捕获了所有相关信息。当我像 @John Siu 一样查看 /var/log/apache2/error.log 时,我看到了相同的 MKL 错误消息,该消息已经存在很多天了。我完全不知道这个日志文件的存在。现在我知道了 :)
  2. 我从一开始就怀疑是 MKL 的问题。我以为通过设置 MKL_SERIAL=yes,我就可以关闭与处理多线程的多线程服务器相关的任何问题布拉斯。显然这还不够,需要使用 apache 的 prefork 版本。
  3. worker我需要删除并改用的实际命令prefork

    apt-get install apache2-mpm-prefork

    我还发现这个命令可以方便地查看你正在使用的选项
    (感谢@JohnSiu 提供的使用 dpkg 的示例):, apache2 -V | grep 'MPM'它显示的输出如下

    服务器 MPM: Prefork -D APACHE_MPM_DIR="server/mpm/prefork"

  4. 有时需要悬赏。

  5. 我对 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

  1. 重新启动 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
    
  2. 访问 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应用程序内更新。这对主机环境的影响最小,而且在安装机器之间也更具可移植性。

  1. 获取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']
    
  2. 将上述路径放在应用程序中默认模块路径的前面

    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

输出(pastebin)

HTTP 输出链接

相关内容