【问题标题】:One rails application for multiple domain names一个用于多个域名的 Rails 应用程序
【发布时间】:2010-09-01 17:20:37
【问题描述】:

我有一个 Rails 应用程序需要由乘客模块 nginx 部署。此应用程序需要为数百个域名提供服务。我没有足够的内存来启动数百个 Rails 实例。我不确定在少数情况下启动 rails 的正确方法。它是不同域名下的同一个应用程序。

server {
    listen 80;
    server_name www.a_domain.com;
    root /webapps/mycook/public;
    passenger_enabled on;
}
server {
    listen 80;
    server_name www.b_domain.com;
    root /webapps/mycook/public;
    passenger_enabled on;
} 
server {
    listen 80;
    server_name www.c_domain.com;
    root /webapps/mycook/public;
    passenger_enabled on;
}

您可以使用上面的代码,它会启动三个 rails 实例。最好只启动实例以在这 3 个域下提供服务。有人有什么建议吗?

【问题讨论】:

    标签: ruby-on-rails nginx passenger


    【解决方案1】:

    只需为该服务器条目设置多个域别名。

    server {
        listen 80;
        server_name www.a_domain.com www.b_domain.com www.c_domain.com;
        root /webapps/mycook/public;
        passenger_enabled on;
    }
    

    这将为这些域中的每一个提供请求,并且都访问同一个应用程序池。

    【讨论】:

    • +1 设置多个别名并让您的 Rails 应用程序检测应该执行哪个别名来检查主机变量。
    • 我们已经按照 Chris 描述的方式完全按照 Simone 的建议进行了操作,而且效果很好。
    • 注意:虽然您可以像这样列出明确的域名,但 Nginx 还允许在 server_name 中使用通配符和正则表达式。 nginx.org/en/docs/http/server_names.html
    【解决方案2】:

    我在 Chris 的回答中提到,您可以在 server_name 中使用通配符和正则表达式,这些将传递给您的 rails 实例。

    server_name *;  # handle requests from all domains
    

    在您的 Rails 应用程序中有四种使用方法(据我所知)。

    “大包内容”方法

    什么都不做

    如果你只是让 Nginx 将所有域发送到同一个 rails 应用程序,它们都会得到相同的内容。在这种情况下,www.abc.com 和 www.xyz.com 可以访问相同的数据。这是最容易做到的,因为你什么都不做。

    当您想让域具有不同的内容时,此解决方案的局限性就出现了。例如,如果 www.abc.com/about 和 www.xyz.com/about 应该是不同的页面,就会变得很棘手。

    “幕后人”法

    使用 Nginx 的“重写”

    在某些情况下,您可以让 Nginx 将域名重写为子域并将其传递给您的 rails 应用程序。例如:

    server_name *;  # handle requests from all domains
    rewrite ^(?:www.)?([^.]*)\..*$ $1.yourdomain.com last;
    

    这个正则表达式需要一点解释。它只是说从以下任何一个中获取 xyz:www.xyz.com、xyz.com、xzy.co.uk、www.xyz.co.uk。重写将请求从其中任何一个更改为 xyz.yourdomain.com。

    这样做的好处是 Nginx 做的非常快,rails 应用程序直到以后才涉及,并且请求内容可以限制在子域中。以Page.where(subdomain: request.subdomain, permalink: params[:permalink]) 为例。

    不过,这是相当严格的,因为这意味着子域和域名必须相同。也许这对您的应用程序来说是个问题,也许不是。但是,虽然我在子域中使用了$1,但您可以轻松地将其作为参数插入到 url 中。例如,yourdomain.com/$1 会将请求从 www.xyz.com 重写为 yourdomain.com/xyz。

    另一个问题是“幕后的人”。尽管用户访问 www.xyz.com,但重写意味着他们会在地址栏中看到 xyz.yourdomain.com。

    “Rails 方式”方法

    在 ApplicationController 中使用 Rails 请求对象

    您可以使用您的应用程序控制器使用 Rails 的 request 对象来确定与域名关联的内容的范围。

    在本例中,我们将使用 ApplicationController 来查找与域名关联的用户帐户。假设您有一个带有 domain_name 属性的 User 模型:

    def domain_user
      @domain_user ||= User.where(domain_name: request.domain()).take
    end
    

    如果对数据库的额外命中造成性能障碍,Redis 可用于缓存这些查找。到目前为止,我的应用程序还没有达到这一点。

    此解决方案的弱点在于,虽然查找可能会找到 www.xyz.com,但它会错过 xyz.com。为了适应这种情况,我们可以使用一些正则表达式:

    def domain_user
      request.domain.match /(?:www.)?(.*)/
      @domain_user ||= User.where(domain_name: $1).take
    end
    

    这个正则表达式去掉了 www.如果它存在。其余的成为域(ruby 为我们存储在$1 中)。

    与 Nginx 重写解决方案不同,如果用户访问 www.xyz.com,他们仍然会在地址栏中看到。

    使用路由约束

    “对我不起作用”方法

    或者,Rails 3 及更高版本具有约束,存在于您的路由文件中的函数可以半动态地生成路由表。我说“半动态”是因为路由表是在应用程序启动时生成的。如果 User 模型(在我们的示例中)发生变化,则需要特别注意重建路由表。对于分布在多个服务器上的应用程序,这可能会变得难以管理,尽管people have done it

    到目前为止,我的所有应用程序最终(如果不是最初)都使用了 ApplicationController 解决方案,因为它被证明是最干净且最容易实现的。从 MCV 的角度来看,这也很有意义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-08
      • 2014-11-08
      • 2010-10-03
      • 1970-01-01
      • 2016-09-02
      • 1970-01-01
      • 2012-02-10
      • 1970-01-01
      相关资源
      最近更新 更多