2

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

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

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

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

app1 请求 app2 使用 username 更新用户的名字和姓氏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,所以我不会进一步探索它,而是更喜欢不同的解决方案。

4

5 回答 5

3

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

这使得运行一个 pub/sub 系统变得非常简单,该系统可以在两个 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. 您可以设置已发布模型的名称。例如,如果我Admin::User在 App1 中有一个命名空间类,我可以发布属性:as => 'AdminUser',并且 App2 具有AdminUser它将正确侦听的模型。

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

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

于 2015-01-20T18:25:05.300 回答
1

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

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

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

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

于 2015-01-20T15:33:23.550 回答
0

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

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

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

其他选择是使用发布者/订阅者模式,我推荐这篇文章以获得更深入的知识和这篇文。

于 2015-01-16T18:37:18.983 回答
0

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

无需互联网连接!

于 2015-01-17T13:43:25.760 回答
0

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

区别在于:

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

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

于 2015-01-20T15:02:51.010 回答