【问题标题】:Exchange data between 2 Rails apps (preferably offline) on the same server在同一服务器上的 2 个 Rails 应用程序(最好是离线)之间交换数据
【发布时间】:2015-01-12 23:40:41
【问题描述】:

我在同一台服务器上有 2 个 Ruby on Rails 4 应用程序(它们不 - 也不应该 - 共享数据库):

deploy@Ubuntu-1404-trusty-64-minimal:~/applications$ ls
app1  app2

如何在 app1 和 app2 之间交换数据?

我当前的实现不稳定且不安全:

app1 请求 app2 更新用户名 bobby 的用户的名字和姓氏:

# app1
HTTParty.get("https://app2.com/update_full_name?username=bobby&first_name=Bob&last_name=Dylan")

app2收到app1的请求并处理:

# app2 app/controllers/some_controller.rb
def update_full_name
  user = User.find_or_create_by(username: params[:username])
  user.update_attributes(first_name: params[:first_name], last_name: params[:last_name])
end

我读过ActiveResource has been removed from Rails 4。反正我从来没有真正理解过 ActiveResource,所以我不会进一步探索它,而是更喜欢不同的解决方案。

【问题讨论】:

  • 活动资源是必须的,或者你可以使用“她”的宝石。另一个没有从 rails 4 中移除的活动资源,它被分离为不同的 gem。我在 rails 4 中使用了活动资源。您在不同的应用程序中有不同的数据库吗?或者你有前端和后端这样的应用程序吗?

标签: ruby-on-rails ruby ruby-on-rails-4 activeresource data-exchange


【解决方案1】:

我遇到了同样的问题并探索了每个选项:ActiveResource(已弃用)、使用我自己开发的 API 包装器进行回调、使用 Redis 或 RabbitMQ 进行排队。没有什么比我简单的头脑更容易实现的了。如果App1 中的Model1 总是会更新App2 中的Model2,那么我在Rails 中找到的最佳解决方案是Promiscuous gem

运行 pub/sub 系统让两个 ruby​​/rails 应用程序之间的数据保持同步变得非常简单。它适用于 ActiveRecord 和 Mongoid。文档更深入,但这里有一些我在尝试使用 github 页面上的 Quick Start 指南进行设置时发现的问题。

  1. 确保在连接到共享 RabbitMQ 实例的两个应用程序中都有一个 initializer file
  2. 如果在发布者端使用 ActiveRecord,您将需要创建一个新表(据我所知,订阅者不需要此表):

    create_table :_promiscuous do |t|
      t.string    :batch
      t.timestamp :at, :default => :now
    end
    
  3. 您还需要为每个发布者和订阅者模型添加一列

    # in App1 - publisher
    add_column :publisher_model, :_v, :integer, limit: 8, default: 1
    
    # in App2 - subscriber
    add_column :subscriber_model, :_v, :integer, limit: 8
    
  4. 您可以设置已发布模型的名称。例如,如果我在 App1 中有一个命名空间类 Admin::User,我可以发布属性 :as => 'AdminUser',而 App2 具有模型 AdminUser,它将正确侦听。

  5. 如果您已按照 github 页面上的说明进行操作,包含您的 mixin 并设置可发布/订阅属性,您将不可避免地希望在生产环境中运行它,在这种情况下您的订阅者需要run a worker。我使用了这个 Resque deploy script 的相当无耻的盗版,我的 Promiscuous 版本可以是 found here,它似乎可以工作。

我正在寻找越来越多的方法来使用此设置。在共享和管理我的数据方面给了我更多的灵活性。祝你好运。

【讨论】:

  • 这个项目在 2019 年仍然可以使用 Rails 5 或 6 吗?
【解决方案2】:

所以如果我是你,我会研究排队系统。

它的好处是它是异步的,因此您不必担心两个应用程序在更新后立即启动并运行。

我并不是真正的消息队列专家,但如果你问我,RabbitMQRubyBunny 这样的东西可以做得很好。

任何有消息队列经验的人都可以随时编辑我。

【讨论】:

    【解决方案3】:

    我的建议是保留一个 API,如果你想要一个快速的解决方案,你可以查看rails-api gem 和他们的文档。

    对于数据交换,您可以使用像 Typhoeus 这样的持久 HTTP 库并将请求排队,这与 Making Parallel Requests 部分的文档中的说明相同。

    关于安全性,一个简单的“令牌”就足以让您不会收到伪造的请求。您还可以保留一个主机表来交换数据和它们的令牌。

    其他选项是使用Publisher/Subscriber 模式,我建议this article 拥有更深入的知识和这个blog post

    【讨论】:

    • 嗨。这真的是最好的解决方案吗?似乎没有必要通过常规的 Web API 而不是在应用程序之间建立内部通信。
    • @ChristofferJoergensen 最好的解决方案是您的决定,除此之外,您还可以查看 PUB SUB 解决方案。
    • 您能否参考一篇描述这在应用程序之间如何工作的文章?快速的 Google 搜索指向 redis pub/sub,应用程序可以在其中通过 redis db 发布更新。还是我误会了?
    • 这就是这样做的方法。您提出的任何解决方案都需要一个添加“发布”更改,另一个“订阅”它们,您也可以使用已有的 HTTP 来完成,而无需添加任何额外的基础架构。
    【解决方案4】:

    您似乎需要在这两个应用程序之间共享 一些东西,在您的示例中,它是一个 URL。我理解对单独数据库的需求,但我假设您可以接受某种第三共享资源。

    我喜欢 Paulo 在这里使用 Redis 的想法,但我认为进入 pub/sub 和 Typheous 可能比必要的复杂性更高。我的建议是:

    1. 在 Redis 中存储可更新信息
    2. 使用 cron 运行 rake 任务来拉取更新

    #1

    假设您设置了 Redis 和 redis-rb

    当您在 app1 上更新模型时,将适用的更改存储在 Redis 中:

    ### User just updated! ###
    # Grab the attributes you want to update on other server
    attributes_i_care_about = user.attributes.extract!(*%w( username first_name last_name ))
    
    # Set a key for this user, future updates will overwrite, leaving only most recent
    key_for_this_user = "user_updates:#{user.username}"
    
    # Store it
    @redis.hmset(key_for_this_user,*attributes_i_care_about)
    

    #2

    cron 设置为随心所欲地运行,我不会在这里详细介绍 cron,但是一旦设置了 Rake 任务,命令应该非常简单。比如:bundle exec rake user_updates:process

    rake 任务可能如下所示:

    namespace :user_updates do
    
      desc "Process user updates from other server"
      task process: :environment do
    
        @redis.keys("user_updates*").each do |key|
          updated_attributes = @redis.hgetall(key)
          user = User.find_or_create_by(username: updated_attributes["username"])
          user.update_attributes(updated_attributes)
          @redis.del(key) # get rid of the key after use
        end
    
      end
    
    end
    

    无需互联网连接!

    【讨论】:

    • 非常有趣的方法。依靠共享的 redis db 听起来很聪明!我会试试这个。一个问题:您建议运行一个重复的 cron 作业来启动 rake 任务。我需要尽可能快的沟通,所以我需要 cron 作业每秒运行一次。我可以从几个来源e.g. this one 读到一个 cron 作业至少运行一分钟。你能想到 cron 作业的替代解决方案吗?
    • 我打算采用一种不会过多增加要求的基本方法,但您可以使用此方法:github.com/homer6/frequent-cron。或者,由于您已经考虑了 Redis,因此 Resque 非常适合这种事情,您可以将您的 rake 任务转移到 User.perform 方法上,然后运行 ​​Resque 工作程序。当然,这将需要更多的设置工作。 github.com/resque/resque
    • 1) 对于基于 API 的方法,您也不需要 Internet 连接,只需在 localhost 上连接即可。 2)如果应用程序不允许共享数据库,为什么允许它们共享一个redis,这只是另一个数据库?在这里添加另一块基础设施似乎是不必要的复杂性。
    • 3) 将 cron 用于常规任务是一个坏主意,尤其是当它们经常发生时。如果不仔细编码,您可能会冒着赶上一个新流程的风险,即在最后一个流程完成之前安排新流程。一个 while true 循环在这里稍作睡眠会更好。 (或者更好:您的进程管理器应该优雅地重新启动退出 0 的进程,因此您自己甚至不需要循环。)
    • @XavierShay,感谢您的评论。首先,关于共享资源,我认为使用共享redis在应用程序之间存储命令,然后与例如共享数据库有很大的不同。用户信息。所以我会尝试这种方法。我也相信 app1 如何“启动” app2 的处理任务仍然是一个悬而未决的问题。您认为我应该针对这个主题提出一个单独的问题,还是有必要在这个开放式问题中回答/解决它?
    【解决方案5】:

    我的主要选择是使用ActiveResource 实现一个api。如果您不使用 API 解决方案,您可以使用 rake 任务和 cron 在两个 Rails 应用程序之间交换数据,就像 @TheWorkwerAnt 建议的那样,但没有 redis。

    区别在于:

    • 在您的记录中添加一列以指示其中哪些是同步的。
    • 在您的rake 任务中连接到您的两个数据库。 Reference

    这种方法的优点是它不依赖外部系统。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-31
      • 1970-01-01
      • 1970-01-01
      • 2012-12-20
      • 1970-01-01
      相关资源
      最近更新 更多