会话竞争条件在 Rails 中很常见。Redis 会话存储也无济于事!原因是 Rails 仅在收到请求时读取并创建会话对象,并在请求完成并即将返回给用户时将其写回会话存储。
Java 有一个解决方案,称为会话复制。我们可以构建自己的基于 redis 的并发会话存储。下面就差不多了。除了更新方法缺少锁。但它几乎从来没有发生过比赛条件。
要获取会话哈希,只需使用返回哈希的 concurrent_session 。要在其中设置一些值,请使用 update_concurrent_session 方法。它将新哈希深度合并到旧值中:
class ApplicationController < ActionController::Base
def concurrent_session
@concurrent_session ||= get_concurrent_session
end
def update_concurrent_session h
return unless session.id.present?
@concurrent_session = get_concurrent_session.deep_merge(h)
redis.set session_key, @concurrent_session.to_json
redis.expire session_key, session_timeout
end
private
def redis
Rails.cache.redis
end
def session_timeout
2.weeks.to_i
end
def session_key
'SESSION_' + session.id.to_s if session.id.present?
end
def get_concurrent_session
return {} unless session.id.present?
redis.expire session_key, session_timeout
redis.get(session_key).yield_self do |v|
if v
JSON.parse v, symbolize_names: true
else
{}
end
end
end
end
使用示例:
my_roles = concurrent_session[:my_roles]
update_concurrent_session({my_roles: ['admin', 'vip']})