我有一个一次只能由一个会话使用的共享资源,我如何向其他会话发出该资源当前正在使用的信号?
在 Java 或 CI 中会使用互斥信号量在线程之间进行协调,我如何在 Rails 中实现这一点?我是否定义了一个新的环境变量并使用它在会话之间进行协调?
一个小代码片段以及答案将非常有帮助。
我有一个一次只能由一个会话使用的共享资源,我如何向其他会话发出该资源当前正在使用的信号?
在 Java 或 CI 中会使用互斥信号量在线程之间进行协调,我如何在 Rails 中实现这一点?我是否定义了一个新的环境变量并使用它在会话之间进行协调?
一个小代码片段以及答案将非常有帮助。
由于您的 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
我会考虑使用 Redis 来锁定资源。
这具有跨多个服务器工作的优点,并且不会将锁定时间限制为当前 HTTP 请求的生命周期。
Ruby 有一个Mutex 类,它可以做你想做的事,尽管它不能跨进程工作。我很抱歉,虽然我知道的不够多,无法给你一个示例代码片段。文档是这样说的:“Mutex 实现了一个简单的信号量,可用于协调对来自多个并发线程的共享数据的访问。”
您可以使用acts_as_lockable_by gem 来做到这一点。
假设共享资源是一个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 机器/服务器集群工作,而不仅仅是在一个服务器上本地(如使用文件锁),因为 gemredis
用作锁/信号量代理。,lock
和方法都是原子的和线程安全的; unlock
)renew_lock