缓存后端在开发服务器上有效但在mod_wsgi上无效
我正在使用一个自定义的缓存后端,目的是为了在内置的缓存后端上加上一层,这样我就可以把当前的站点ID加到所有的缓存键里(这对于使用单个memcached实例的多站点功能很有用)。
不过不幸的是,这在Django自带的开发服务器上运行得很好,但在我尝试在使用mod_wsgi的实际服务器上运行时,却出现了很糟糕的错误。
这是我错误日志中的追踪信息:
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] mod_wsgi (pid=22933): Exception occurred processing WSGI script '/home/jiaaro/webapps/op_wsgi/myProject/deploy/myProject.wsgi'.
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] Traceback (most recent call last):
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] File "/home/jiaaro/webapps/op_wsgi/lib/python2.5/django/core/handlers/wsgi.py", line 230, in __call__
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] self.load_middleware()
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] File "/home/jiaaro/webapps/op_wsgi/lib/python2.5/django/core/handlers/base.py", line 40, in load_middleware
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] mod = import_module(mw_module)
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] File "/home/jiaaro/webapps/op_wsgi/lib/python2.5/django/utils/importlib.py", line 35, in import_module
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] __import__(name)
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] File "/home/jiaaro/webapps/op_wsgi/myProject/apps/site_settings/__init__.py", line 6, in <module>
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] import cache_wrapper
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] File "/home/jiaaro/webapps/op_wsgi/myProject/apps/site_settings/cache_wrapper.py", line 4, in <module>
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] from django.core.cache.backends.base import BaseCache
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] File "/home/jiaaro/webapps/op_wsgi/lib/python2.5/django/core/cache/__init__.py", line 73, in <module>
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] cache = get_cache(settings.CACHE_BACKEND)
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] File "/home/jiaaro/webapps/op_wsgi/lib/python2.5/django/core/cache/__init__.py", line 68, in get_cache
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] return getattr(module, 'CacheClass')(host, params)
[Wed Feb 17 00:39:34 2010] [error] [client 127.0.0.1] AttributeError: 'module' object has no attribute 'CacheClass'
如果我在同一台机器上(也就是实际服务器)运行开发服务器,它就能正常工作……我可以毫无问题地做到这一点:
$ cd /home/jiaaro/webapps/op_wsgi/myProject
$ python2.5 manage.py runserver
而且在一个单独的SSH会话中……
$ wget 127.0.0.1:8000
页面能够正确显示,使用的是memcached和实际的数据库。请问mod_wsgi在导入模块的方式上有什么不同吗?我应该知道吗?
也许是开发服务器的单进程、单线程特性导致的?
我已经为这个问题挣扎了好几天,任何帮助都将不胜感激。
额外信息:
- Webfaction共享主机(centos)
- Apache 2
- Python 2.5
- Django 1.1.1
- mod_wsgi 2.5
- MySql 5.0
更多细节:
- 我已经设置了缓存后端(并且它在追踪信息中显示正确的模块被导入,说明它是有效的)
- 模块中有一个叫“CacheClass”的类:
site_settings/cache_wrapper.py :
from django.conf import settings
CACHE_BACKEND = getattr(settings, 'CUSTOM_CACHE_BACKEND')
from django.core.cache.backends.base import BaseCache
class CacheClass(BaseCache):
from decorators import accept_site
def __init__(self, *args):
from django.core.cache import get_cache
self.WRAPPED_CACHE = get_cache(CACHE_BACKEND)
@accept_site
def add(self, site, key, *args):
return self.WRAPPED_CACHE.add(self._key(site, key),*args)
@accept_site
def get(self, site, key, *args):
return self.WRAPPED_CACHE.get(self._key(site, key),*args)
... (all the rest of the wrapped methods)
def _key(self, site, key):
from exceptions import NoCurrentSite
if not site:
raise NoCurrentSite
return "%s|%s" % (site.id, key)
accept_site装饰器与一些中间件一起工作,以确定当前的站点。它们如下:
decorators.py:
def accept_site(fn):
def decorator(self, *args, **kwargs):
site = kwargs.get('site', None)
try:
del kwargs['site']
except KeyError:
pass
from .middleware import get_current_site
site = site or get_current_site()
if not site:
raise NoCurrentSite("The current site is not available via thread locals, please specify a site with the 'site' keyword argument")
return fn(self, site, *args, **kwargs)
return decorator
以及middleware.py
try:
from threading import local
except ImportError:
from django.utils._threading_local import local
from django.conf import settings
from django.contrib.sites.models import Site
DEFAULT_SITE_ID = 1
_thread_locals = local()
def get_current_site():
return getattr(_thread_locals, 'site', None)
def set_current_site(site):
setattr(_thread_locals, 'site', site)
class SiteSettings(object):
"""Middleware that gets various objects from the
request object and saves them in thread local storage."""
def process_request(self, request):
if settings.DEBUG:
site_id = request.GET.get('site_id', DEFAULT_SITE_ID)
else:
site_id = DEFAULT_SITE_ID
current_site_domain = request.META["HTTP_HOST"]
try:
current_site = Site.objects.get(domain__iexact=current_site_domain())
except:
current_site = Site.objects.get(id=site_id)
set_current_site(current_site)
所有这些在开发服务器上都能正常工作,使用的设置与wsgi服务器相同,并且配置了相同的Python路径(就我所看到的情况而言)。
此时我希望我在某个地方创建了一个导入循环(尽管这不太合理,因为它只在开发服务器上发生)。
编辑:我在Django文档中找到了一份关于开发服务器和Apache之间差异的列表,上面提到:
开发服务器会将已安装的应用添加到 sys.path,而Apache则不会。
也许这和问题有关?
2 个回答
你有没有在你的settings.py文件里设置
如果你已经设置了这个选项,但还是有问题,可以试试切换到假缓存模块或者本地内存模块(虽然这在生产环境中并不是一个好的选择),看看这样是否能解决问题。如果可以,那可能是你的WSGI的python路径中缺少了某个包,比如python-memcached。
问题的根源在于 sys.path
。我需要确保 mod_wsgi 的设置和开发服务器的 sys.path
是一样的,并且没有多余的包留在不该出现的地方(这是因为重构时没有通过版本控制正确应用到服务器上)。