我有一台使用apache2和mod_python的服务器,该服务器似乎在短时间内重置了全局变量。在这种情况发生之前,服务器的所有功能和全局变量都将按照我的期望进行完全处理。
为了排除某些运行时错误的可能性(尽管我确实进行了调试并且看不到任何错误),我制作了此简化脚本来演示如何重置全局变量。它只保存一个全局计数器,并在每次访问服务器时将其递增:
from mod_python import apache
counter = 0
def handler(req):
global counter
counter += 1
req.content_type = 'text/plain'
req.write('counter: '+str(counter))
return apache.OK
我可以不断刷新页面,并观察计数器是否按预期增加。但是,在某些时候,计数器会跳回1。
我试图简单地按住该F5键以快速刷新页面,并查看在我不断访问它时是否仍会重置该页面,并且我发现了其他问题。起初它仍然会下降到1几次,但最终会下降到其他数字。例如,我最多可以刷新200次,而下降到大约100次,有时甚至会跳回200秒。
似乎有多个用于启动服务器脚本的虚拟Shell,而服务器只是在它们之间随机切换或启动新的虚拟Shell。我该如何预防呢?
似乎有多个用于启动服务器脚本的虚拟Shell,而服务器只是在它们之间随机切换或启动新的虚拟Shell。
确切地。
Apache具有多种不同的功能,可以最大程度地提高并发性,其中之一就是生成一堆子进程。
使用mod_python
,每个子进程都有自己的独立Python解释器,这意味着它们每个都拥有自己的任何全局变量副本。
我该如何预防呢?
好吧,您可以在Apache中禁用分叉。但这是一个坏主意。除非您真的知道自己在做什么,否则将破坏您的可伸缩性。而且,如果重新启动服务器,它仍然无济于事(Apache有时会自行清除内存泄漏,除非您配置为不这样做)。
Web服务应该是无状态的;或者,如果它们具有任何状态,则应该将其打包并在请求之间持久化。对于每个用户状态,您可以将其存储在cookie或隐藏的表单字段中,并让他们的浏览器将其传递回去。但是对于系统范围的状态,这是行不通的。您需要将其保存到外部。
通常的解决方案是数据库。但是对于这种简单的事情,您可以使用任何东西-甚至是纯文本文件和羊群。这很冗长,但无需学习SQL或其他数据库接口即可轻松理解,并且它使您考虑并发问题(理解它们是至关重要的),因此我将向您展示:
import contextlib
import fcntl
from mod_python import apache
@contextlib.contextmanager
def flocking(f, flag=fcntl.LOCK_EX):
fcntl.flock(f, flag)
try:
yield f
finally:
fcntl.flock(f, fcntl.LOCK_UN)
def bump_counter():
while True:
try:
with flocking(open('storage.lock', 'r+')) as f:
val = int(f.read())
f.seek(0)
f.write(str(val))
return val
except OSError:
pass
try:
with flocking(open('storage.lock', 'x')) as f:
val = 0
f.write(str(val))
return val
except OSError:
pass
def handler(req):
req.content_type = 'text/plain'
req.write('counter: '+str(bump_counter()))
return apache.OK
大多数代码是关于错误处理的,主要是处理这是有史以来第一个请求的情况,因此该文件尚不存在。在这种情况下,我们尝试以x
模式打开它,只有在文件不存在的情况下才能成功,以防万一启动时有两个请求进入,而他们都认为它们是第一个。这样,其中一个将失败,并将返回并再次尝试整个循环。
在现实生活中,您将需要更好的错误处理,因为“找不到文件”不是唯一可能导致失败的原因,并且您不希望服务器在发生这种情况时永远永久阻塞。但是在现实生活中,您可能会使用一个数据库来帮您解决这个问题。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句