【问题标题】:Python module to listen for WSGI requests from Apache?用于侦听来自 Apache 的 WSGI 请求的 Python 模块?
【发布时间】:2023-12-28 05:19:01
【问题描述】:

据我了解,Apache mod_wsgi 实现了一个 python 解释器,它与 Apache 一起运行并服务于 CGI 请求,但 WSGI 和 CGI​​ 之间的区别在于,对于 WSGI,只要 Apache 服务器正在运行,Python 会话就会一直运行,而对于 CGI,每次服务器收到 CGI 请求时都必须重新启动。

我想知道的是,是否有一个 Python 模块可以与 Apache 分开运行,例如在交互式 Python 会话中,它会监听来自 Apache 的 CGI 请求?因此,例如,您可以让 Apache 运行而您的 WSGI 中间件不运行,然后您可以启动一个交互式 Python 会话并导入您的 WSGI 中间件模块,然后它将为来自 Apache 的 CGI 请求提供服务,您也可以在不运行的情况下将其关闭关闭 Apache。所以它是一个类似于 Apache mod_wsgi 的单一会话,但它不必总是与 Apache 并发运行,您可以从交互式 Python 会话中运行它。

编辑 1:

例如,我有这个烧瓶应用程序 myapp.py:

from flask import Flask

app = Flask(__name__)
app.debug = True

app.apples = 0

@app.route('/')
def hello():
    app.apples += 1
    return 'blah: %d' % app.apples

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

我可以通过键入 python myapp.py 来运行它,但它会启动自己的 Web 服务器。相反,我希望 Apache 成为 Web 服务器,但我希望能够打开一个交互式 python shell 并输入 from myapp import * 并让应用程序监听来自 Apache 的请求,因为我有我的交互式 shell,所以我可以做print app.applesapp.apples = 50 之类的东西。我是说我希望我的 web 应用程序与 web 服务器分开,并与 python shell 交互。

我对@9​​87654325@ 的意思是,它是一个全局变量,就像访问'/' 的次数一样,只要应用程序正在运行,它就会一直存在。

编辑 2:

这是另一个例子。

myapp.py:

import web

def add_global_hook():
    g = web.storage({"counter": 0})
    def _wrapper(handler):
        web.ctx.globals = g
        return handler()
    return _wrapper

class Hello:
    def GET(self):
        web.ctx.globals.counter += 1
        return "<h1>Counter: %d</h1>" % web.ctx.globals.counter

urls = ("/", "Hello")
app = web.application(urls, globals())
app.add_processor(add_global_hook())
app.run()

在这里,我可以打开一个 python 解释器并输入from myapp import *,它会启动 Web 服务器,但是当 Web 服务器运行时,我无法使用交互式 shell。有没有办法以非阻塞方式运行服务器,以便我可以使用交互式 shell?

【问题讨论】:

  • 如果你能编辑并解释你为什么想要这个会有所帮助——也许这会激发一些好主意。
  • 这对于调试 apache+wsgi 设置可能很有用,以防出现问题。此外,我可以看到在调试 wsgi 应用程序方面的一些用途,并且通常用于为 wsgi 提供 repl。
  • 是的,我希望它用于调试,并且我想让 Apache 与交互式会话分开运行,所以我可以单独关闭 Apache,但仍然有交互式会话 - 所以我可以进行修改或其他任何事情 - 然后启动 Apache 备份。

标签: python apache mod-wsgi wsgi


【解决方案1】:

您是否考虑过使用 Apache 作为前端代理运行 gunicorn WSGI 服务器?

如果您想要调试实时 Python Web 应用程序而不需要做任何太复杂的事情,请查看:

https://github.com/GrahamDumpleton/wsgi-shell

如果使用 mod_wsgi,您只需要确保在默认的单个守护进程中使用 mod_wsgi 守护模式。

顺便说一句,您对 mod_wsgi 工作原理的理解有点错误,但与其尝试和纠正,不如说它只会帮助您更好地解释为什么要做您想做的事情。也许从提出实际问题而不是您认为的解决方案开始。

【讨论】:

  • 那么问题来了:如何让 Web 应用与 Web 服务器分离,并与 python shell 交互?
  • 顺便说一句,我尝试了您的 wsgi-shell,但嵌入式 python 控制台似乎无法访问我的应用程序中的变量。例如,我从嵌入式控制台尝试了print app.apples,它告诉我“应用程序”没有定义,所以我可能误解了嵌入式控制台的用途。
  • 它是一个 Python 解释器,你需要先导入模块才能访问它们,所以先尝试“导入应用程序”。
  • 对,所以我在嵌入式控制台中导入了我的网络应用程序,但它与我通过套接字文件连接到的网络应用程序的运行实例无关。例如,如果我在 Web 浏览器中访问“/”三次,则为 app.apples = 3,但是当我在嵌入式控制台中访问 print app.apples 时,我得到 0。明白我的意思是我想对正在运行的实例进行实时调试吗?
  • 你的问题可能归结为你当时是如何配置 mod_wsgi 的,你没有解释。将调试打印语句添加到您的应用程序模块以打印出 filename 和 os.getpid(),然后在通过嵌入式解释器访问时进行验证。如果 mod_wsgi 配置错误,您可能没有与同一个进程甚至正确的子解释器交谈。
【解决方案2】:

我正在考虑同样的问题,因为我无法弄清楚为什么某些 DLL 在 Windows+Apache 2.2+mod_wsgi 下无法加载。 (就我而言,当 Python 的 Shapely 库尝试加载“geos_c.dll”时会遇到麻烦。)

我正在考虑的解决方法是运行一个独立的 WSGI 应用程序,比如在端口 81 上,该应用程序对于直接发送到该端口的所有请求都可以正常工作。然后,将 Apache 配置为代理该端口,例如:

ProxyPass /wsgi/myapp http://127.0.0.1:81
ProxyPassReverse /wsgi/myapp http://127.0.0.1:81

有些事情是你担心的:

  • 内置的 WSGI 服务器不是多线程的(所以你放弃了在 Apache 后面使用 WSGI 的一大优势)
  • 您的独立 Python WSGI 服务器需要在控制台中启动(意外关闭它会杀死您的应用程序 - 消除了 Apache WSGI 的另一个优势,它将应用程序作为服务提供,没有控制台,也没有人登录)

【讨论】:

    【解决方案3】:

    Python 在wsgiref 模块中有一个内置的wsgi 服务器,适用于开发和测试目的,但可能不适合生产用途。例如,使用还提供的示例 wsgi 应用程序:

    >>> import wsgiref.simple_server
    >>> server = wsgiref.simple_server.make_server('0.0.0.0', 8888, wsgiref.simple_server.demo_app)
    >>> server.serve_forever()
    127.0.0.1 - - [21/Jul/2016 00:44:04] "GET / HTTP/1.1" 200 2664
    127.0.0.1 - - [21/Jul/2016 00:44:05] "GET /favicon.ico HTTP/1.1" 200 2615
    127.0.0.1 - - [21/Jul/2016 00:44:05] "GET /favicon.ico HTTP/1.1" 200 2675
    ^CTraceback (most recent call last):
      File "<ipython-input-5-30934a6743d8>", line 1, in <module>
        server.serve_forever()
      File "/usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SocketServer.py", line 236, in serve_forever
        poll_interval)
      File "/usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SocketServer.py", line 155, in _eintr_retry
        return func(*args)
    KeyboardInterrupt
    
    >>> 
    

    【讨论】: