【问题标题】:Django + Celery + Apache mod_wsgi + Postgres + RabbitMQ Application for multiple clients用于多个客户端的 Django + Celery + Apache mod_wsgi + Postgres + RabbitMQ 应用程序
【发布时间】:2020-12-03 09:21:58
【问题描述】:

我有一个使用 Celery、RabbitMQ 和 Apache mod_wsgi 的 Django 应用程序。目前都在一台服务器上。每个客户端都有自己的 URL 挂载,例如:

www.example.com/Client001

www.example.com/Client002

www.example.com/Client003

每个客户端都有自己的数据库和项目目录,其中包含用于 Django 设置的 local_setting.py。

我正在使用 supervisord 为每个客户管理 Celery Worker + Celery Beat。

随着客户越来越多,维护变得越来越耗时。

我已经开始使用 Docker 来尝试简化部署,并可能跨多个主机进行扩展。

虽然设置 Docker Compose 为一个客户端运行一组服务非常容易,但我正在尝试找出易于管理的多个客户端的最佳方法,例如快速设置挂载在主 URL 下的新客户端。

我认为 Postgres 数据库实例应该共享以保存每个客户端数据库,就像现在一样。并拥有一个共享的 NGIX 实例来处理 HTTP 端。为每个客户端使用一个 Kubernetes Pod,其中包括:

  • Gunicorn 处理 Django
  • 芹菜节拍
  • 芹菜工人
  • 用于静态文件的轻型 HTTP 服务器。

所以问题是,这是一个好方法还是有更好的方法来处理和处理这个问题?

我还想知道是否应该为每个客户端构建一个映像,因为这样可能更容易管理?

欢迎任何建议。

【问题讨论】:

    标签: django apache docker nginx celery


    【解决方案1】:

    我的建议是为此保持一个代码库和一个服务器运行(或同一 Django 应用程序的多个服务器,无需任何基于客户端的自定义)。主要原因是维护更容易。您不希望多次进行更改以向多个客户端提供功能。

    由于您已经有一个 Django 应用程序,我认为最好利用该代码来适应上面给出的方法,而代码更改最少。这意味着,您需要某种方法来处理连接到多个数据库的多个客户端。我建议使用中间件和database router。像这样:(代码基于snippet)。

    import threading
    
    request_cfg = threading.local()
    
    class RouterMiddleware (object):
        def process_view( self, request, view_func, args, kwargs ):
            if 'client' in kwargs:
                request_cfg.client = kwargs['client']
                request.client = client 
                # Here, we are adding client info with request object.
                # It will make the implementation bit easier because
                # then you can access client information anywhere in the view/template.
    
        def process_response( self, request, response ):
            if hasattr( request_cfg, 'client' ):
                del request_cfg.client
            return response
    
    class DatabaseRouter (object):
        def _default_db( self ):
            from django.conf import settings
            if hasattr( request_cfg, 'client' ) and request_cfg.client in settings.DATABASES:
                return request_cfg.client            
            else:
                return None
    
        def db_for_read( self, model, **hints ):
            return self._default_db()
    
        def db_for_write( self, model, **hints ):
            return self._default_db()
    

    然后将它们添加到settings.py:

    DATABASES = {
        'default': {
            'NAME': 'user',
            'ENGINE': 'django.db.backends.postgresql',
            'USER': 'postgres_user',
            'PASSWORD': 's3krit'
        },
        'client1': {
            'NAME': 'client1',
            'ENGINE': 'django.db.backends.postgresql',
            'USER': 'postgres_user',
            'PASSWORD': 's3krit'
        },
        'client2': {
            'NAME': 'client2',
            'ENGINE': 'django.db.backends.postgresql',
            'USER': '_user',
            'PASSWORD': 'priv4te'
        }
    }
    
    DATABASE_ROUTERS = [
        'path.to.DatabaseRouter', 
    ]
    
    MIDDLEWARE = [
        # middlewares
        'path.to.RouterMiddleware'
    ]
    

    终于更新urls.py

    urlpatterns = [
        path('<str:client>/admin/', admin.site.urls),
        path('<str:client>/', include('client_app.urls')),
        # and so on
    ]
    

    这种方法的优点是您不必为新客户端配置任何内容,您只需在设置中添加一个新数据库并按照@987654323 中的说明运行迁移@。无需配置反向代理服务器或其他任何东西。

    现在,当涉及到在 celery 中处理任务时,您可以提供您将使用哪个数据库来运行查询(参考 docs)。这是一个例子:

    @app.task
    def some_task():
        logger.info("-"*25)
        for db_name in settings.DATABASES.keys():
            Model.objects.using(db_name).filter(some_condition=True)
        logger.info("-"*25)
    

    【讨论】:

    • 当然是我会研究更多的方法。我目前有一些复杂的 django 设置,对于一些客户来说是不同的,但我认为我需要简单地介绍一下。这种方法将降低资源使用率。我还在为一些前端代码使用 collectstatic_js_reverse,我需要仔细检查,但不认为这会导致问题。
    【解决方案2】:

    处理客户的三种方式:

    1. 孤立的方法:单独的数据库。每个租户都有自己的数据库。
    2. 半隔离方法:共享数据库,单独的模式。一个数据库供所有租户使用,但每个租户一个架构。
    3. 共享方法:共享数据库、共享架构。所有租户共享相同的数据库和架构。有一个主租户表,所有其他表都有一个外键指向。

    django-tenents 包使用第二种方法,并有一个用于客户端的子域,例如 client1.example.com、client2.example.com 等。

    我也使用 django-tenants 为我创建的每个模型添加了 Company 模型外键。

    django 租户有帮助,但在 postgres 中有不同的架构;具有更少的开销集成。

    添加Company 必须在每个模型中实现,并且如果您使用基于类的视图,则应该使用中间件或混入来处理。

    【讨论】:

      猜你喜欢
      • 2012-07-15
      • 2014-12-06
      • 2021-03-14
      • 2023-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-09
      • 2021-04-05
      相关资源
      最近更新 更多