在同一台服务器上的 2 Rails 个应用程序(最好是离线的)之间交换数据

Exchange data between 2 Rails apps (preferably offline) on the same server

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

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,所以我不会进一步探索它,而是更喜欢不同的解决方案。

我的建议是保留 API,为此,如果您想要一个快速的解决方案,您可以查看 rails-api gem 及其文档。

对于数据交换,您可以使用持久性 HTTP 库,例如 Typhoeus and queue the requests, the same way is explained in the documentation in the section Making Parallel Requests

关于安全性,一个简单的 "token" 就足以确保您不会收到伪造的请求。您还可以保留 table 台主机来交换数据,并且它们会令牌。

其他选项是使用 Publisher/Subscriber pattern, where I recommend this article to have a more in depth knowledge and this blog post

您似乎需要在这两个应用程序之间共享 某些内容,在您的示例中,它是 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

无需互联网连接!

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

区别是:

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

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

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

它的好处是它是异步的,所以您真的不必担心两个应用程序是否都已启动并且 运行 一旦发生更新。

我不是真正的消息队列专家,但如果你问我,RabbitMQ with something like RubyBunny 可以做得很好。

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

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

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

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

    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 中有一个命名空间 class Admin::User,我可以发布属性 :as => 'AdminUser' 并且 App2 有模型 AdminUser 它会正确监听。

  5. 如果您按照 github 页面中的说明进行操作,包括您的混入并设置 publishable/subscribable 属性,您将不可避免地想要 运行 它在生产中,在这种情况下,您的订阅者需要 run a worker. I use a pretty shameless ripoff of this Resque deploy script, my version for Promiscuous can be found here 并且它似乎有效。

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