Rails 3.2如何跨会话保护共享资源访问

时间:2012-05-14 16:44:46

标签: ruby-on-rails ruby semaphore

我有一个共享资源,一次只能由一个会话使用,如何向其他会话发信号通知当前正在使用该资源?

在Java或C中,我会使用互斥信号量来协调线程,如何在Rails中实现?我是否定义了一个新的环境变量并使用它来协调会话?

一小段代码片段和答案将非常有用。

4 个答案:

答案 0 :(得分:2)

由于你的Rails实例在使用Nginx或Apache时可以在不同的进程中运行(没有像线程一样的共享内存),我想唯一的解决方案是使用文件锁:

lock = File.new("/lock/file")
begin
  lock.flock(File::LOCK_EX)
  # do your logic here, or share information in your lock file
ensure
  lock.flock(File::LOCK_UN)
end

答案 1 :(得分:1)

我会考虑使用Redis锁定资源。

这具有跨多个服务器工作且不将锁定时间限制为当前HTTP请求的生存期的优点。

答案 2 :(得分:0)

Ruby有Mutex class可能会做你想要的,虽然它不能跨进程工作。我很抱歉,我不知道给你一个示例代码片段。以下是文档所说的内容:“Mutex实现了一个简单的信号量,可用于协调对来自多个并发线程的共享数据的访问。”

答案 3 :(得分:0)

您可以使用acts_as_lockable_by宝石来做到这一点。

想象一下,共享资源是一个Patient ActiveRecord类,该类只能由单个用户访问(您可以将其替换为session_id),如下所示:

class Patient < ApplicationRecord
  acts_as_lockable_by :id, ttl: 30.seconds
end

然后您可以在控制器中执行此操作:

class PatientsController < ApplicationController
  def edit
    if patient.lock(current_user.id) 
      # It will be locked for 30 seconds for the current user
      # You will need to renew the lock by calling /patients/:id/renew_lock
    else
      # Could not lock the patient record which means it is already locked by another user
    end
  end

  def renew_lock
    if patient.renew_lock(current_user.id)
      # lock renewed return 200
    else
      # could not renew the lock, it might be already released
    end
  end

  private

  def patient
    @patient ||= Patient.find(params[:id])
  end
end

这是一个解决方案,它使用最少的代码并跨RoR机器/服务器集群工作,而不仅仅是在一个服务器上本地(例如使用文件锁定),因为gem使用redis作为锁定/信号代理。 lockunlockrenew_lock方法都是原子和线程安全的;)