Rails在多个实例/服务器上重新加载动态路由

时间:2018-01-29 16:53:13

标签: ruby-on-rails ruby ruby-on-rails-3 ruby-on-rails-4 routing

我们如何在多个服务器/实例上强制Rails重新加载路由?

我们在5个以上的实例上运行Google App-Engine中的多租户平台,我们希望所有站点都能从后端定义自己的路由集。每当我们有一个新站点时,我们当前必须重新启动所有服务器才能访问新路由。

我们遵循此guide但它只适用于本地环境,并且不会在不重新启动服务器的情况下更新生产中所有服务器上的路由。

我们的路径文件如下所示:

的routes.rb

df1

LIB / dynamic_routes.rb

Frontend::Application.routes.draw do
  root 'home#index'
  ...
  DynamicRoutes.load
end

每次更新路线或创建新网站后,我们都会运行def self.load Frontend::Application.routes.draw do Site.all.each do |site| site.routes.each do |custom_route| route_name = custom_route[0] route = custom_route[1] # write the route with the host constraint self.constraints(:host => site.hostname) do case route_name when :contact_form mapper.match "#{route}", to: 'contact_forms#new' as: "contact_#{site.id}" end ... end end end end end def self.reload Frontend::Application.reload_routes! end

2 个答案:

答案 0 :(得分:1)

我们终于找到了一个效果很好的解决方案,也没有太多影响性能。我们使用生产中的线程在请求​​之间保持状态的事实。所以我们决定创建一个中间件来检查路由更改的最新时间戳,如果时间戳与Thread.current中保存的时间戳不同,我们会强制Frontend::Application.reload_routes!

配置/ production.rb

Frontend::Application.configure do
  ...
  config.middleware.use RoutesReloader
  ...
end

应用/中间件/ routes_reloader.rb

class RoutesReloader
  SKIPPED_PATHS = ['/assets/', '/admin/']

  def initialize(app)
    @app = app
  end

  def call(env)
    if reload_required?(env)
      timestamp = Rails.cache.read(:routes_changed_timestamp)

      if Thread.current[:routes_changed_timestamp] != timestamp
        Frontend::Application.reload_routes!

        Thread.current[:routes_changed_timestamp] = timestamp
      end
    end

    @app.call(env)
  end

  private

  def reload_required?(env)
    SKIPPED_PATHS.none? { |word| env['PATH_INFO'].include?(word) }
  end
end

应用程序/模型/ routes.rb中

class Routes < ActiveRecord::Base

  after_save :save_timestamp

  private

  def save_timestamp
    ts = Time.zone.now.to_i
    Rails.cache.write(:routes_changed_timestamp, ts, expires_in: 30.minutes)
  end
end

好处:

  • 您可以在某些路径上排除重新加载,例如/ assets /和/ admin /
  • 线程服务器多个请求,重新加载只发生一次
  • 您可以在任何您喜欢的型号上实现此功能

警告:

  • 新主题将加载路由两次
  • 如果您清除Rails Cache,所有线程将重新加载路由(您可以通过持久解决方案克服此问题;例如将时间戳保存到mysql然后再保存到缓存中)

但总的来说,我们没有发现任何性能下降。

我们多年来一直在努力解决这个问题,上面的解决方案是第一个真正帮助我们在多个线程上重新加载路由的解决方案。

答案 1 :(得分:0)

假设您没有共享存储:您可以编写一个操作来重新加载该特定实例的路由。当您触发DynamicRoutes :: reload时,您将向其他实例发出请求。重新加载行动。

如果您确实有共享存储,请编写一个before_action,以便在特定文件被触及时重新加载路由&#34;如果要让所有实例重新加载路由,请触摸该文件。