2011-06-24 66 views
14

在我们公司,我们为当地很多报纸制作新闻门户(目前13个,下个月30个以上,未来更多),每个页面浏览量为2k到10万次/天。由于我们正在从每个网站大量定制的情况演变为每个网站的差异都是配置或自定义模板的问题,因此我们的软件对于所有网站而言已经几乎相同。现在我们的部署策略是每个站点的一个gunicorn实例(每个站点有1-17个工人,具体取决于站点流量),16核服务器和12GB RAM。这个设置的问题是每个工作人员(普通预分叉的gunicorn)需要110MB,无论是否使用。现在,我们需要添加更多的RAM来提供更多的请求,所以基本上不会扩展。另外,由于我们正在从这个模型转向每个站点都是独立的,每个站点都有自己的数据库,我非常喜欢这种方式,特别是因为我们使用关系数据库(mysql,但是迁移到pgsql),所以它更容易碎片这样。在同一个python进程上运行多个站点

我正在做一些研究和试验,在一个gunicorn实例上运行所有站点,这样我就可以完全使用服务器,并在负载平衡器后面添加更多服务器。问题是,Django的假设在很多地方,只有一个网站每个进程正在运行,所以对于我所想的,到目前为止我不得不执行:

  • 中间件,是以从HTTP_HOST请求并在threadlocal变量上放置一个标识符。
  • 使用该变量相应地加载自定义模板的模板加载器。
  • 猴子补丁django.db.model.Model,可能会添加一个元类(甚至不能确定这是可能的,但我想我会需要它,因为我们有时需要使用自定义管理器)会覆盖管理器,首先会调用原始管理器上的db_manager(标识符),然后调用预期的方法。我还需要覆盖保存和删除方法,以始终包含using = identifier参数。
  • 我想我需要停止使用inclusion_tag装饰器,不是一个大问题,但我需要考虑这样的其他情况。
  • 如果我需要每个站点的自定义或额外的URL,则重复和丑陋地修复urlresolvers。我现在不需要它们,但可能会在某个时候。

这就是我想到的,甚至没有实现它,看到它的破裂,我相信我需要更多的更改才能工作。所以我真的不想这样做,特别是在需要额外的维护工作时,但我没有看到任何替代方案,并且很想知道某人已经以更好的方式解决了这个问题。当然,我也可以完全停止使用django(我已经有很多理由这样做),但这意味着要进行重大改写,并且有两个维护软件的两个不兼容的分支,直到新版本与django版本达到功能匹配为止,所以我看起来比所有丑陋的黑客更糟糕。

+0

这不正是*网站的贡献是什么? –

+0

这些网站contrib假定所有网站都在同一个数据库上,并为每个模型添加一个外键。此外,它不包括我为每个网站定制模板的要求。 –

+0

当然可以。您可以在模板路径中使用Site对象中的一些标识片段。 –

回答

6

我最近开发的电子商务系统具有类似的要求 - 许多实例从同一个项目中运行,几乎可以共享所有内容。以前的系统版本是一堆独立安装(〜30),所以它很难维护。我相信这些要求仍然与您的要求不同(例如,所有实例在我的案例中共享相同的模型),但分享我的经验仍然可能有用。

你说的对,Django并没有像这样开箱即可,但实际上它实际上很容易实现。这里是我做了什么的简要描述。

我可以看到我想要实现的和django.contrib.sites之间的协同作用。另外,因为许多第三方Django应用程序都知道如何使用它并使用它,例如,为当前网站生成绝对URL。 sites的主要问题在于,它希望您指定settings.SITE_ID中的当前网站ID,这是对多主机问题非常天真的方法。您自然需要什么以及您还提到的是从Host请求标头中确定当前网站。为了解决这个问题,我借用了django-multisitehttps://github.com/shestera/django-multisite/blob/master/multisite/threadlocals.py#L19

接下来我创建了一个应用程序,封装了与我的项目的多主机方面相关的所有功能。在我的情况下,该应用程序被称为stores,其中包括两个重要的类别:stores.middleware.StoreMiddlewarestores.models.Store

模型类是django.contrib.sites.models.Site的子类。关于继承Site的好处是,您可以将Store传递给预期为Site的任何函数。因此,您仍然只是使用旧的,有据可查的和经过测试的sites框架。对于Store类,我添加了配置所有不同存储所需的所有字段。所以它有像urlconf,theme,robots_txt和whatnot字段。

中间件类的功能是将Host标头与数据库中相应的Store实例相匹配。一旦找到匹配的Store,它将以类似于https://github.com/shestera/django-multisite/blob/master/multisite/middleware.py的方式修补SITE_ID。另外,它查看了storeurlconf,如果它不是None,它将设置request.urlconf以应用其特殊的URL要求。之后,当前的Store实例被存储在request.store中。这已经被证明是非常有用的,因为我能够做这样的事情在我的观点:

def homepage(request): 
    featured = Product.objects.filter(featured=True, store=request.store) 
    ... 

request.store成为整个项目对我来说request对象的自然额外维度。

这是在Store类中定义为另一件事的功能get_absolute_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 

所以,我可以很容易地生成URL对象上的其他比目前的专卖店,例如:

# Redirect to `product` on `store`. 
redirect(store.get_absolute_url(product)) 

这基本上是我所需要的一切,以便能够实现一个系统,允许用户通过Django管理员在其自己的域中创建一个新的电子商店。

相关问题