首页 > 解决方案 > Rails ActiveSupport::CachingKeyGenerator 的内存使用问题

问题描述

为了在数据库中加密和存储用户信息,我的 Rails 应用程序使用salt(每个用户)生成keyusing ActiveSupport::KeyGenerator. 但是,方法执行的默认迭代generate_key2**16[1]。在每次读取(用于解密)和写入(用于加密)时使用用户的密钥生成salt会减慢我的应用程序。

我发现如果和用于密钥生成的密钥保持不变[2] ,ActiveSupport::CachingKeyGenerator可以使用它来缓存密钥。在内部,它使用[3] 来缓存密钥。使用提高了我的应用程序的性能,因为它并不总是生成密钥。saltlengthConcurrent::MapActiveSupport::CachingKeyGenerator

这会将我的应用程序的内存使用量增加到可以使应用程序崩溃的水平吗?

参考:

  1. https://github.com/rails/rails/blob/b9ca94caea2ca6a6cc09abaffaad67b447134079/activesupport/lib/active_support/key_generator.rb#L16

  2. https://api.rubyonrails.org/classes/ActiveSupport/CachingKeyGenerator.html

  3. https://github.com/rails/rails/blob/b9ca94caea2ca6a6cc09abaffaad67b447134079/activesupport/lib/active_support/key_generator.rb#L33

标签: ruby-on-railsrubyencryption

解决方案


共享“工作”解决方案

由于ActiveSupport::CachingKeyGeneratoruses Concurrent::Map,我发现缓存键会导致内存使用量与用户数量成正比(假设所有请求都以某种方式路由到同一个节点)。

为了解决这个问题,我写了一个类似CachingKeyGenerator的包装ActiveSupport::KeyGeneratorActiveSupport::Cache::MemoryStore.

class CachingKeyGenerator
  BASE = Rails.application.secrets.secret_key_base
  LENGTH = ActiveSupport::MessageEncryptor.key_len
  KEY_EXPIRY = 1.day

  def initialize
    @key_generator = ActiveSupport::KeyGenerator.new(Rails.application.secrets.secret_key_base)
    @keys_cache = ActiveSupport::Cache::MemoryStore.new(expires_in: KEY_EXPIRY)
  end

  def generate_key(salt)
    key = @keys_cache.fetch(salt)
    if key.nil?
      key = @key_generator.generate_key(salt, LENGTH)
      @keys_cache.write(salt, key)
    end
    key
  end
end

根据 Rails 文档,ActiveSupport::Cache::MemoryStore它是线程安全的,并实现了基于 LRU 的清理机制 [1]。这应该使缓存的内存使用具有确定性 - 为内存存储设置的大小限制(默认 - 32Mb,可以在初始化期间定义)。

PS:尚未将其部署到生产中,如果我遇到任何意外问题,将在此处更新。

[1] http://api.rubyonrails.org/classes/ActiveSupport/Cache/MemoryStore.html


推荐阅读