You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails:两应用共享同一数据库,如何让App1感知App2新增记录?

问题:拆分Rails应用后,Web应用如何感知API应用的数据库插入操作?

我原本有个同时管理Web应用和API的Rails项目,现在为了优化服务器资源,打算把两者拆成独立应用:

  • App1:端口80的Web服务,共用数据库DB1
  • App2:端口8080的API服务,同样使用DB1

核心需求:当App2插入新的Post记录时,App1需要触发对应的业务逻辑。目前考虑了几个方向:Rpush、SQL触发器,或者把API转成gem,想了解各方案的优劣及实现方式。

现有代码片段

App1的Post模型:

class Post < ActiveRecord::Base
 belongs_to :user
 before_save :do_stuff_when_app2_insert_record # 跨应用场景下该钩子不生效

 def do_stuff_when_app2_insert_record
  ... # 需要执行的业务逻辑
 end
end

App2的Post模型及创建代码:

class Post < ActiveRecord::Base
 belongs_to :user
end

Post.create(name: 'post')

可选方案分析

方案1:消息队列(如Rpush/Sidekiq+Redis)

核心思路:App2创建Post后主动发送消息到队列,App1监听队列并执行对应逻辑。

实现步骤

  1. 两个应用配置同一消息队列服务(如Rpush);
  2. App2的Post模型添加after_create钩子发送消息:
    class Post < ActiveRecord::Base
      belongs_to :user
      after_create :notify_web_app
    
      private
      def notify_web_app
        Rpush::Notification.create!(
          app: Rpush::Apns::App.find_by(name: 'web_app'),
          registration_ids: ['web_app_listener'],
          data: { post_id: id }
        )
      end
    end
    
  3. App1启动后台进程监听队列,收到消息后执行逻辑:
    # App1的后台任务
    class HandleNewPostJob < ApplicationJob
      queue_as :default
    
      def perform(post_id)
        post = Post.find(post_id)
        post.do_stuff_when_app2_insert_record
      end
    end
    
    # Rpush监听回调
    Rpush.configure do |config|
      config.on_notification_delivered do |notification|
        HandleNewPostJob.perform_later(notification.data['post_id'])
      end
    end
    

优劣对比

  • ✅ 解耦性强,应用间独立;支持高并发;业务逻辑在Rails层,易维护
  • ❌ 需要额外维护消息队列服务;存在消息丢失风险(可通过持久化队列缓解)

方案2:SQL触发器

核心思路:在数据库的posts表上创建触发器,插入新记录时触发事件,App1通过轮询或数据库通知感知变化。

实现步骤(以PostgreSQL为例)

  1. 创建事件记录表:
    CREATE TABLE post_events (
      id SERIAL PRIMARY KEY,
      post_id INTEGER REFERENCES posts(id),
      event_type VARCHAR(20) DEFAULT 'created',
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      processed BOOLEAN DEFAULT FALSE
    );
    
  2. 创建插入触发器及通知函数:
    CREATE OR REPLACE FUNCTION notify_post_created()
    RETURNS TRIGGER AS $$
    BEGIN
      INSERT INTO post_events (post_id) VALUES (NEW.id);
      PERFORM pg_notify('post_created', NEW.id::TEXT); -- 实时通知
      RETURN NEW;
    END;
    $$ LANGUAGE plpgsql;
    
    CREATE TRIGGER trigger_post_created
    AFTER INSERT ON posts
    FOR EACH ROW EXECUTE FUNCTION notify_post_created();
    
  3. App1的两种监听方式:
    • 定时轮询(简单但有延迟):
      # 用whenever gem配置定时任务
      every 1.minute do
        runner "Post.process_new_events"
      end
      
      # Post模型添加处理方法
      def self.process_new_events
        PostEvent.where(processed: false).each do |event|
          post = Post.find(event.post_id)
          post.do_stuff_when_app2_insert_record
          event.update(processed: true)
        end
      end
      
    • 实时监听pg_notify:
      # App1后台进程
      require 'pg'
      
      conn = PG.connect(dbname: 'DB1')
      conn.exec("LISTEN post_created")
      
      loop do
        conn.wait_for_notify do |channel, pid, payload|
          post_id = payload.to_i
          post = Post.find(post_id)
          post.do_stuff_when_app2_insert_record
        end
      end
      

优劣对比

  • ✅ 无需修改应用代码(App2无需改动);实时性可保障
  • ❌ 业务逻辑下沉到数据库,维护成本高;触发器出错影响数据库性能;不同数据库语法差异大

方案3:将API逻辑封装为Gem

核心思路:把创建Post的逻辑封装成共用Gem,两个应用依赖该Gem,在Gem中统一处理创建及通知逻辑。

实现步骤

  1. 创建post_management Gem,定义统一创建逻辑:
    module PostManagement
      class Creator
        def self.create_post(attributes)
          post = Post.create(attributes)
          notify_web_app(post.id)
          post
        end
    
        private
        def self.notify_web_app(post_id)
          # 调用App1的webhook接口
          HTTP.post("http://app1.example.com/webhooks/post_created", json: { post_id: post_id })
        end
      end
    end
    
  2. App1和App2引入该Gem,App2用Gem方法创建Post:
    # App2代码
    PostManagement::Creator.create_post(name: 'post')
    
  3. App1提供webhook接口处理通知:
    # App1 routes.rb
    post '/webhooks/post_created', to: 'webhooks#post_created'
    
    # WebhooksController
    class WebhooksController < ApplicationController
      skip_before_action :verify_authenticity_token
    
      def post_created
        post_id = params[:post_id]
        post = Post.find(post_id)
        post.do_stuff_when_app2_insert_record
        head :ok
      end
    end
    

优劣对比

  • ✅ 业务逻辑统一维护,避免重复;逻辑清晰
  • ❌ 需要维护额外Gem;应用版本需同步升级;耦合度较高

方案选择建议

  • 优先选消息队列方案:适合大多数生产场景,解耦性和扩展性强;
  • 选SQL触发器方案:适合不想改动应用代码,且数据库支持触发器/通知的场景,注意数据库维护风险;
  • 选Gem封装方案:适合业务逻辑高度关联、可接受一定耦合度的小型项目,便于快速迭代。

内容的提问来源于stack exchange,提问作者Breno Perucchi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 12:15:39