Active Support 缓存存储¶ ↑
这是一个抽象的缓存存储类。有多种缓存存储实现,每种都有其额外的功能。请参阅 ActiveSupport::Cache 模块下的类,例如 ActiveSupport::Cache::MemCacheStore。MemCacheStore 目前是大型生产网站最受欢迎的缓存存储。
某些实现可能不支持除基本的缓存方法 fetch、write、read、exist? 和 delete 之外的所有方法。
ActiveSupport::Cache::Store 可以存储任何其 coder 的 dump 和 load 方法支持的 Ruby 对象。
cache = ActiveSupport::Cache::MemoryStore.new cache.read('city') # => nil cache.write('city', "Duckburgh") # => true cache.read('city') # => "Duckburgh" cache.write('not serializable', Proc.new {}) # => TypeError
键始终被转换为字符串,并且区分大小写。当指定一个对象作为键且该对象定义了 cache_key 方法时,将调用此方法来定义键。否则,将调用 to_param 方法。哈希和数组也可以用作键。元素将用斜杠分隔,并且 Hash 中的元素将按键排序,以保持一致性。
cache.read('city') == cache.read(:city) # => true
可以缓存 nil 值。
如果您的缓存位于共享基础设施上,您可以为您的缓存条目定义一个命名空间。如果定义了命名空间,它将作为前缀添加到每个键中。命名空间可以是静态值,也可以是 Proc。如果是 Proc,它将在评估每个键时被调用,以便您可以使用应用程序逻辑来使键失效。
cache.namespace = -> { @last_mod_time } # Set the namespace to a variable @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
- C
- D
- E
- F
- I
- K
- M
- N
- R
- S
- W
常量
| DEFAULT_POOL_OPTIONS | = | { size: 5, timeout: 5 }.freeze |
默认的 |
||
| MAX_KEY_SIZE | = | 250 |
如果键超过限制,则会使用 Active Support 摘要进行截断。 |
||
Attributes
| [R] | options | |
| [R] | silence | |
| [R] | silence? |
类公共方法
new(options = nil) Link
创建一个新的缓存。
选项¶ ↑
:namespace-
设置缓存的命名空间。如果您的应用程序与其他应用程序共享缓存,此选项特别有用。
:serializer-
缓存值的序列化器。必须响应
dump和load方法。默认序列化器取决于缓存格式版本(在
Rails中使用时通过config.active_support.cache_format_version设置)。每个格式版本的默认序列化器都包含一个回退机制,可以从任何格式版本反序列化值。这种行为使得在不使整个缓存失效的情况下轻松迁移不同格式版本成为可能。您还可以指定
serializer: :message_pack来使用基于ActiveSupport::MessagePack的预配置序列化器。:message_pack序列化器包含相同的反序列化回退机制,可以轻松地从(或迁移到)默认序列化器。:message_pack序列化器可能会提高性能,但它需要msgpackgem。 :compressor-
序列化缓存值的压缩器。必须响应
deflate和inflate方法。默认压缩器是
Zlib。要定义一个可以解压旧缓存条目的新自定义压缩器,您可以检查 Zlib 的"\x78"签名来查找压缩值。module MyCompressor def self.deflate(dumped) # compression logic... (make sure result does not start with "\x78"!) end def self.inflate(compressed) if compressed.start_with?("\x78") Zlib.inflate(compressed) else # decompression logic... end end end ActiveSupport::Cache.lookup_store(:redis_cache_store, compressor: MyCompressor)
:coder-
用于序列化和(可选)压缩缓存条目的编码器。必须响应
dump和load方法。默认编码器组合了序列化器和压缩器,并包含一些性能优化。如果您只需要覆盖序列化器或压缩器,则应改用
:serializer或:compressor选项。如果存储可以直接处理缓存条目,您也可以指定
coder: nil来省略序列化器、压缩器和编码器。例如,如果您正在使用ActiveSupport::Cache::MemoryStore并可以保证缓存值不会被修改,您可以指定coder: nil来避免防御性编程的开销。:coder选项与:serializer和:compressor选项互斥。同时指定它们将引发ArgumentError。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 300 def initialize(options = nil) @options = options ? validate_options(normalize_options(options)) : {} @options[:compress] = true unless @options.key?(:compress) @options[:compress_threshold] ||= DEFAULT_COMPRESS_LIMIT @max_key_size = @options.delete(:max_key_size) @max_key_size = MAX_KEY_SIZE if @max_key_size.nil? # allow 'false' as a value @coder = @options.delete(:coder) do legacy_serializer = Cache.format_version < 7.1 && !@options[:serializer] serializer = @options.delete(:serializer) || default_serializer serializer = Cache::SerializerWithFallback[serializer] if serializer.is_a?(Symbol) compressor = @options.delete(:compressor) { Zlib } Cache::Coder.new(serializer, compressor, legacy_serializer: legacy_serializer) end @coder ||= Cache::SerializerWithFallback[:passthrough] @coder_supports_compression = @coder.respond_to?(:dump_compressed) end
实例公共方法
cleanup(options = nil) Link
通过删除过期的条目来清理缓存。
选项将传递给底层缓存实现。
某些实现可能不支持此方法。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 785 def cleanup(options = nil) raise NotImplementedError.new("#{self.class.name} does not support cleanup") end
clear(options = nil) Link
清除整个缓存。请谨慎使用此方法,因为如果正在使用共享缓存,它可能会影响其他进程。
选项哈希将传递给底层缓存实现。
某些实现可能不支持此方法。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 795 def clear(options = nil) raise NotImplementedError.new("#{self.class.name} does not support clear") end
decrement(name, amount = 1, options = nil) Link
在缓存中递减一个整数值。
选项将传递给底层缓存实现。
某些实现可能不支持此方法。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 750 def decrement(name, amount = 1, options = nil) raise NotImplementedError.new("#{self.class.name} does not support decrement") end
delete(name, options = nil) Link
删除缓存中的一个条目。如果删除了一个条目,则返回 true,否则返回 false。
选项将传递给底层缓存实现。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 686 def delete(name, options = nil) options = merged_options(options) key = normalize_key(name, options) instrument(:delete, key, options) do delete_entry(key, **options) end end
delete_matched(matcher, options = nil) Link
删除所有键匹配模式的条目。
选项将传递给底层缓存实现。
某些实现可能不支持此方法。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 732 def delete_matched(matcher, options = nil) raise NotImplementedError.new("#{self.class.name} does not support delete_matched") end
delete_multi(names, options = nil) Link
一次删除多个缓存条目。返回被删除条目的数量。
选项将传递给底层缓存实现。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 699 def delete_multi(names, options = nil) return 0 if names.empty? options = merged_options(options) names.map! { |key| normalize_key(key, options) } instrument_multi(:delete_multi, names, options) do delete_multi_entries(names, **options) end end
exist?(name, options = nil) Link
如果缓存包含给定键的条目,则返回 true。
选项将传递给底层缓存实现。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 713 def exist?(name, options = nil) options = merged_options(options) key = normalize_key(name, options) instrument(:exist?, key) do |payload| entry = read_entry(key, **options, event: payload) (entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false end end
fetch(name, options = nil, &block) Link
使用给定的键从缓存中获取数据。如果缓存中有具有给定键的数据,则返回该数据。
如果缓存中没有此类数据(缓存未命中),则返回 nil。但是,如果传递了块,则在缓存未命中时将该块传递给键并执行。块的返回值将被写入缓存下的给定缓存键,并返回该返回值。
cache.write('today', 'Monday') cache.fetch('today') # => "Monday" cache.fetch('city') # => nil cache.fetch('city') do 'Duckburgh' end cache.fetch('city') # => "Duckburgh"
选项¶ ↑
内部,fetch 调用 read_entry,并在缓存未命中时调用 write_entry。因此,fetch 支持与 read 和 write 相同的选项。此外,fetch 支持以下选项
-
force: true- 强制缓存“未命中”,这意味着即使缓存值存在,我们也将其视为缺失。当force为 true 时,必须传递一个块,以便这始终导致缓存写入。cache.write('today', 'Monday') cache.fetch('today', force: true) { 'Tuesday' } # => 'Tuesday' cache.fetch('today', force: true) # => ArgumentError
:force选项在使用其他方法来询问是否应强制进行缓存写入时很有用。否则,直接调用write更清晰。 -
skip_nil: true- 防止缓存 nil 结果cache.fetch('foo') { nil } cache.fetch('bar', skip_nil: true) { nil } cache.exist?('foo') # => true cache.exist?('bar') # => false
-
:race_condition_ttl- 指定在生成新值期间可以重用过期值的秒数。这可以用来防止缓存条目过期时出现竞争条件,通过防止多个进程同时重新生成同一条目(也称为“狗pile”效应)。当一个进程遇到一个在
:race_condition_ttl秒前过期的缓存条目时,它将在生成新值之前将过期时间延长:race_condition_ttl秒。在此延长的窗口期内,当进程生成新值时,其他进程将继续使用旧值。第一个进程写入新值后,其他进程将使用它。如果第一个进程在生成新值时出错,另一个进程可以在延长的窗口期过后尝试生成新值。
# Set all values to expire after one second. cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1) cache.write("foo", "original value") val_1 = nil val_2 = nil p cache.read("foo") # => "original value" sleep 1 # wait until the cache expires t1 = Thread.new do # fetch does the following: # 1. gets an recent expired entry # 2. extends the expiry by 2 seconds (race_condition_ttl) # 3. regenerates the new value val_1 = cache.fetch("foo", race_condition_ttl: 2) do sleep 1 "new value 1" end end # Wait until t1 extends the expiry of the entry # but before generating the new value sleep 0.1 val_2 = cache.fetch("foo", race_condition_ttl: 2) do # This block won't be executed because t1 extended the expiry "new value 2" end t1.join p val_1 # => "new value 1" p val_2 # => "original value" p cache.fetch("foo") # => "new value 1" # The entry requires 3 seconds to expire (expires_in + race_condition_ttl) # We have waited 2 seconds already (sleep(1) + t1.join) thus we need to wait 1 # more second to see the entry expire. sleep 1 p cache.fetch("foo") # => nil
动态选项¶ ↑
在某些情况下,可能需要根据缓存值动态计算选项。为了支持这一点,将 ActiveSupport::Cache::WriteOptions 实例作为第二个参数传递给块。例如
cache.fetch("authentication-token:#{user.id}") do |key, options| token = authenticate_to_service options.expires_at = token.expires_at token end
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 452 def fetch(name, options = nil, &block) if block_given? options = merged_options(options) key = normalize_key(name, options) entry = nil unless options[:force] instrument(:read, key, options) do |payload| cached_entry = read_entry(key, **options, event: payload) entry = handle_expired_entry(cached_entry, key, options) if entry if entry.mismatched?(normalize_version(name, options)) entry = nil else begin entry.value rescue DeserializationError entry = nil end end end payload[:super_operation] = :fetch if payload payload[:hit] = !!entry if payload end end if entry get_entry_value(entry, name, options) else save_block_result_to_cache(name, key, options, &block) end elsif options && options[:force] raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block." else read(name, options) end end
fetch_multi(*names) Link
使用给定的键从缓存中获取数据。如果缓存中有具有给定键的数据,则返回该数据。否则,将为没有数据的每个键调用提供的块,并将结果写入缓存并返回。因此,您需要传递一个返回要写入缓存的数据的块。如果您不想在缓存未找到时写入缓存,请使用 read_multi。
返回一个包含每个名称数据的哈希。例如
cache.write("bim", "bam") cache.fetch_multi("bim", "unknown_key") do |key| "Fallback value for key: #{key}" end # => { "bim" => "bam", # "unknown_key" => "Fallback value for key: unknown_key" }
您还可以通过 options 参数指定其他选项。有关详细信息,请参阅 fetch。其他选项将传递给底层缓存实现。例如
cache.fetch_multi("fizz", expires_in: 5.seconds) do |key| "buzz" end # => {"fizz"=>"buzz"} cache.read("fizz") # => "buzz" sleep(6) cache.read("fizz") # => nil
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 603 def fetch_multi(*names) raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given? return {} if names.empty? options = names.extract_options! options = merged_options(options) keys = names.map { |name| normalize_key(name, options) } writes = {} ordered = instrument_multi :read_multi, keys, options do |payload| if options[:force] reads = {} else reads = read_multi_entries(names, **options) end ordered = names.index_with do |name| reads.fetch(name) { writes[name] = yield(name) } end writes.compact! if options[:skip_nil] payload[:hits] = reads.keys.map { |name| normalize_key(name, options) } payload[:super_operation] = :fetch_multi ordered end write_multi(writes, options) ordered end
increment(name, amount = 1, options = nil) Link
在缓存中递增一个整数值。
选项将传递给底层缓存实现。
某些实现可能不支持此方法。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 741 def increment(name, amount = 1, options = nil) raise NotImplementedError.new("#{self.class.name} does not support increment") end
mute() Link
在块内使日志记录器静音。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 330 def mute previous_silence, @silence = @silence, true yield ensure @silence = previous_silence end
namespace() Link
获取当前命名空间
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 800 def namespace @options[:namespace] end
namespace=(namespace) Link
设置当前命名空间。注意,如果向带有命名空间键的缓存提供了自定义选项,此设置将被忽略。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 806 def namespace=(namespace) @options[:namespace] = namespace end
read(name, options = nil) Link
使用给定的键从缓存中读取数据。如果缓存中有具有给定键的数据,则返回该数据。否则,返回 nil。
注意,如果数据是使用 :expires_in 或 :version 选项写入的,则在返回数据之前会应用这两个条件。
选项¶ ↑
-
:namespace- 替换此调用的存储命名空间。 -
:version- 为缓存条目指定一个版本。如果缓存的版本与请求的版本不匹配,读取将被视为缓存未命中。此功能用于支持可回收的缓存键。
其他选项将由特定的缓存存储实现处理。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 506 def read(name, options = nil) options = merged_options(options) key = normalize_key(name, options) version = normalize_version(name, options) instrument(:read, key, options) do |payload| entry = read_entry(key, **options, event: payload) if entry if entry.expired? delete_entry(key, **options) payload[:hit] = false if payload nil elsif entry.mismatched?(version) payload[:hit] = false if payload nil else payload[:hit] = true if payload begin entry.value rescue DeserializationError payload[:hit] = false nil end end else payload[:hit] = false if payload nil end end end
read_counter(name, **options) Link
读取由 increment / decrement 设置的计数器。
cache.write_counter("foo", 1) cache.read_counter("foo") # => 1 cache.increment("foo") cache.read_counter("foo") # => 2
选项将传递给底层缓存实现。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 762 def read_counter(name, **options) options = merged_options(options).merge(raw: true) read(name, **options)&.to_i end
read_multi(*names) Link
一次从缓存中读取多个值。选项可以作为最后一个参数传递。
一些缓存实现可能会优化此方法。
返回一个映射所提供名称到找到的值的哈希。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 544 def read_multi(*names) return {} if names.empty? options = names.extract_options! options = merged_options(options) keys = names.map { |name| normalize_key(name, options) } instrument_multi :read_multi, keys, options do |payload| read_multi_entries(names, **options, event: payload).tap do |results| payload[:hits] = results.keys.map { |name| normalize_key(name, options) } end end end
silence!() Link
使日志记录器静音。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 324 def silence! @silence = true self end
write(name, value, options = nil) Link
使用键将值写入缓存。值必须受 coder 的 dump 和 load 方法支持。
如果写入成功,则返回 true;如果与缓存后端通信时出现错误,则返回 nil;如果写入因其他原因失败,则返回 false。
默认情况下,大于 1KB 的缓存条目会被压缩。压缩可以使相同的内存占用存储更多数据,从而减少缓存驱逐并提高命中率。
选项¶ ↑
-
compress: false- 禁用缓存条目的压缩。 -
:compress_threshold- 压缩阈值,以字节为单位指定。大于此阈值的缓存条目将被压缩。默认为1.kilobyte。 -
:expires_in- 设置缓存条目的相对过期时间,以秒为单位指定。:expire_in和:expired_in是:expires_in的别名。cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes) cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
-
:expires_at- 设置缓存条目的绝对过期时间。cache = ActiveSupport::Cache::MemoryStore.new cache.write(key, value, expires_at: Time.now.at_end_of_hour)
-
:version- 为缓存条目指定一个版本。从缓存读取时,如果缓存版本与请求版本不匹配,读取将被视为缓存未命中。此功能用于支持可回收的缓存键。 -
:unless_exist- 防止覆盖现有的缓存条目。
其他选项将由特定的缓存存储实现处理。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 672 def write(name, value, options = nil) options = merged_options(options) key = normalize_key(name, options) instrument(:write, key, options) do entry = Entry.new(value, **options, version: normalize_version(name, options)) write_entry(key, entry, **options) end end
write_counter(name, value, **options) Link
写入一个计数器,然后可以由 increment / decrement 修改。
cache.write_counter("foo", 1) cache.read_counter("foo") # => 1 cache.increment("foo") cache.read_counter("foo") # => 2
选项将传递给底层缓存实现。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 775 def write_counter(name, value, **options) options = merged_options(options).merge(raw: true) write(name, value.to_i, **options) end
write_multi(hash, options = nil) Link
Cache 存储 API,用于一次写入多个值。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 559 def write_multi(hash, options = nil) return hash if hash.empty? options = merged_options(options) normalized_hash = hash.transform_keys { |key| normalize_key(key, options) } instrument_multi :write_multi, normalized_hash, options do |payload| entries = hash.each_with_object({}) do |(name, value), memo| memo[normalize_key(name, options)] = Entry.new(value, **options, version: normalize_version(name, options)) end write_multi_entries entries, **options end end
实例私有方法
key_matcher(pattern, options) Link
将选项中定义的命名空间添加到用于匹配键的模式中。支持 delete_matched 的实现应调用此方法来将匹配名称的模式转换为匹配带命名空间键的模式。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/cache.rb, line 826 def key_matcher(pattern, options) # :doc: prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace] if prefix source = pattern.source if source.start_with?("^") source = source[1, source.length] else source = ".*#{source[0, source.length]}" end Regexp.new("^#{Regexp.escape(prefix)}:#{source}", pattern.options) else pattern end end