在我们公司,我们为大量当地报纸制作新闻门户(目前有13个,下个月将有30个,将来会有更多),每个页面每天的页面浏览量为2k到10万。由于我们正在从每个站点大量定制的情况发展到每个站点的配置或自定义模板的问题,我们的软件对于所有站点已经几乎相同。现在我们的部署策略是在16核服务器和12GB RAM上为每个站点(每个站点有1-17个工作站,具体取决于站点流量)的一个gunicorn实例。这种设置的问题是每个工作人员(常规预分叉炮弹)需要110MB,无论是否使用。现在有了新的站点,我们需要添加更多的RAM来提供更多的请求,所以基本上它不会扩展。此外,由于我们正在从每个站点独立的这个模型转移,每个站点都有自己的数据库,我非常喜欢它,特别是因为我们使用关系数据库(mysql,但迁移到pgsql),所以它更容易这样碎片。
我正在做一些研究并尝试在一个gunicorn实例上运行所有站点,因此我可以完全使用服务器并在负载均衡器后面添加更多服务器。问题是django假设在很多地方每个进程只运行一个站点,所以对于我到目前为止我想到的,我必须实现:
这就是我想出来的,甚至没有实现它并看到它破坏的地方,我确信我需要更多的更改才能工作。所以我真的不想这样做,特别是我需要额外的维护工作,但我没有看到任何替代方案,并且很想知道有人已经以更好的方式解决了这个问题。当然我也可以完全停止使用django(我已经有很多理由这样做了)但是这意味着重大改写并且有两个维护软件的两个不兼容的分支,直到新的一个与django版本达到功能奇偶校验,所以到我似乎比所有丑陋的黑客更糟糕。
答案 0 :(得分:6)
我最近开发了一个具有类似要求的电子商务系统 - 许多从同一个项目运行的实例几乎可以共享所有内容。该系统的先前版本是一堆独立安装(~30),因此它非常难以维护。我确定要求仍然与您的要求不同(例如,在我的情况下所有实例共享相同的模型),但分享我的经验可能仍然有用。
你是对的,Django没有开箱即用这样的场景,但实际上它很容易解决。以下是我所做的简要描述。
我可以看到我想要实现的目标与django.contrib.sites
之间的协同作用。此外,因为许多第三方Django应用程序知道如何使用它并使用它,例如,生成当前站点的绝对URL。 sites
的主要问题是它希望您在settings.SITE_ID
中指定当前站点ID,这是一种非常天真的多主机问题方法。人们自然想要的,以及您还提到的是从Host
请求标头确定当前站点。为了解决这个问题,我从django-multisite
:https://github.com/shestera/django-multisite/blob/master/multisite/threadlocals.py#L19
接下来,我创建了一个应用程序,它封装了与项目的多主机方面相关的所有功能。就我而言,该应用程序被称为stores
,其中包括两个重要的类:stores.middleware.StoreMiddleware
和stores.models.Store
。
模型类是django.contrib.sites.models.Site
的子类。子类化Site
的好处是,您可以将Store
传递给期望Site
的任何函数。因此,您实际上仍然只是使用旧的,记录良好且经过测试的sites
框架。对于Store
类,我添加了配置所有不同商店所需的所有字段。所以它包含urlconf
,theme
,robots_txt
以及诸如此类的字段。
中间件类的功能是将Host
标头与数据库中相应的Store
实例相匹配。检索到匹配的Store
后,它会以类似https://github.com/shestera/django-multisite/blob/master/multisite/middleware.py的方式修补SITE_ID
。此外,它查看了store
的{{1}},如果它不是None,则会设置urlconf
以应用其特殊的URL要求。之后,当前request.urlconf
实例存储在Store
中。事实证明这非常有用,因为我能够在我的观点中做到这样的事情:
request.store
def homepage(request):
featured = Product.objects.filter(featured=True, store=request.store)
...
在整个项目中成为request.store
对象的一个自然的额外维度。
在request
类上定义的另一件事是函数Store
,其实现看起来大致如下:
get_absolute_url
因此,我可以轻松地为当前商店以外的对象生成URL,例如:
def get_absolute_url(self, to='/'):
"""
Return an absolute url to this `Store` or to `to` on this store.
The URL includes http:// and the domain name of the store.
`to` can be an object with `get_absolute_url()` or an absolute path as string.
"""
if isinstance(to, basestring):
path = to
elif hasattr(to, 'get_absolute_url'):
path = to.get_absolute_url()
else:
raise ValueError(
'Invalid argument (need a string or an object with get_absolute_url): %s' % to
)
url = 'http://%s%s%s' % (
self.domain,
# This setting allowed for a sane development environment
# where I just set it to ".dev:8000" and configured `dnsmasq`.
# The same value was also removed from the `Host` value in the middleware
# before looking up the `Store` in database.
settings.DOMAIN_SUFFIX,
path
)
return url
基本上我只需要能够实现一个系统,允许用户通过Django管理员在自己的域中创建一个新的电子商店。