Я использую пользовательский бэкэнд кэша, чтобы обернуть встроенный бэкэнд кэша, чтобы я мог добавить текущий site_id ко всем cache_keys (это полезно для многосайтовой функциональности с одним экземпляром memcached)
к сожалению, он прекрасно работает на встроенном в django devserver, но выдает неприятную ошибку при попытке запустить его на живом сервере с 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'
если я запускаю devserver на той же машине (живой сервер), он работает нормально ... Я могу сделать это без проблем:
$ cd /home/jiaaro/webapps/op_wsgi/myProject
$ python2.5 manage.py runserver
и в отдельной сессии ssh ...
$ wget 127.0.0.1:8000
Страницы обслуживаются правильно, с использованием memcached и действующей базы данных. Есть ли что-то другое в том, как mod_wsgi импортирует модули, о которых я должен знать?
Может быть, что-то о однопроцессной, однопоточной природе devserver?
Я боролся с этим уже несколько дней, любая помощь будет признательна
Дополнительная информация:
- Общий хостинг 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)
Все это работает, используя devserver, используя те же настройки, что и сервер wsgi и настроенный с тем же путем python (насколько я вижу)
На данный момент я надеюсь, что где-то создал цикл импорта (хотя это не имело бы смысла, поскольку это происходит только в devserver)
edit: Я нашел список различий между devserver и apache в документах django, где написано:
Devserver добавляет установленные приложения в
sys.path, Apache - нет.
Может, это как-то связано?