2011-06-24 98 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管理員在其自己的域中創建一個新的電子商店。

相關問題