一个用于多个域名的rails应用程序

时间:2010-09-01 17:20:38

标签: ruby-on-rails nginx passenger

我有一个需要由乘客模块nginx部署的rails应用程序。此应用程序需要为数百个域名提供服务。我没有足够的内存来启动一百个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个域下提供服务,那将会很高兴。有人有什么建议吗?

2 个答案:

答案 0 :(得分:9)

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

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 :(得分:0)

我在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 Way”方法

在ApplicationController中使用Rails请求对象

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

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

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及更高版本具有约束,这些函数存在于您的路由文件中,可以半动态生成路由表。我说“半动态”,因为路由表是在应用程序启动时生成的。如果用户模型(在我们的示例中)发生更改,则需要特别注意重建路由表。对于分布在多个服务器上的应用程序,虽然people have done it

,但这可能会变得难以管理

到目前为止,我的所有应用程序最终都会使用ApplicationController解决方案,因为它最终是最干净,最容易实现的。从MCV的角度来看,这也很有意义。