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



我在同一台服务器上有 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已经从Rails 4中删除了。无论如何,我从未真正了解过ActiveResource,所以我不会进一步探索它,而是更喜欢不同的解决方案。

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

它使运行发布/订阅系统变得非常简单,该系统使数据在两个 ruby/rails 应用程序之间保持同步。它适用于ActiveRecord和Mongoid。该文档更深入,但以下是我在尝试使用 github 页面上的快速入门指南进行设置时发现的一些陷阱。

  1. 确保在两个应用程序中都有一个连接到共享 RabbitMQ 实例的初始值设定项文件。
  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 页面中的说明,包含您的 mixins 并设置了可发布/可订阅的属性,那么您将不可避免地希望在生产中运行它,在这种情况下,您的订阅者将需要运行一个工作线程。我使用了一个非常无耻的剽窃这个 Resque 部署脚本,我的混杂版本可以在这里找到,它似乎有效。

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

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

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

不是消息队列方面的专家,但是如果你问我,像RubyBunny这样的RabbitMQ可以做得很好。

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

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

对于数据交换,您可以使用像 Typhoeus 这样的持久 HTTP 库并对请求进行排队,在发出并行请求部分中的文档中解释了相同的方法。

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

另一种选择是使用发布者/订阅者模式,我建议本文对本文有更深入的了解和这篇博文。

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

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

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

#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

耙子任务可能如下所示:

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任务中与两个数据库建立连接。参考。

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

相关内容

最新更新