- 后端
- 前端
【免费下载链接】flagsmith
Flagsmith is an open-source feature flag platform with remote config, experimentation, and self-hosted or cloud deployment options.
本文以 Flagsmith 开源项目官方文档 caching-strategies.md 为骨架,结合仓库源码,系统讲解 Flagsmith API 的内存缓存体系:涵盖环境标志缓存、项目分段缓存、GET /flags与GET /identities端点缓存,以及环境文档(Environment Document)的两种缓存模式。读完本文,你将掌握全部相关环境变量的含义、默认值与适用场景,并能针对高并发 SDK 流量设计出一套可落地的自托管缓存方案。
一、Flagsmith 缓存了什么:四类核心缓存对象
Flagsmith 的 Django API 通过内存(in-memory)缓存改善多个高频端点的响应性能。根据官方文档,被缓存的主要对象分为以下四类:
- 环境标志(Environment flags):
/flags接口返回的标志数据,缓存秒数由环境变量CACHE_FLAGS_SECONDS控制。 - 项目分段(Project segments):给定项目下所有分段(Segment)的集合,缓存秒数由
CACHE_PROJECT_SEGMENTS_SECONDS控制。 - 标志与身份端点缓存(Flags & identities endpoint caching):为
GET /flags与GET /identities接口的完整响应提供缓存,后端、位置、超时均由开发者自行选择。 - 环境文档(Environment document):当环境文档(即 SDK 拉取的完整环境数据快照)被重度使用、给数据库造成较大压力时,可借助缓存降低负载。
这四类缓存对 SDK 初始化与持续评估路径的性能至关重要,下面的小节分别深入讲解各自的配置方式与底层实现。
二、环境标志与项目分段缓存
2.1 环境标志缓存(CACHE_FLAGS_SECONDS)
CACHE_FLAGS_SECONDS用于控制/flags接口返回的环境标志在内存中的缓存秒数。在 api/app/settings/common.py 中定义如下:
CACHE_FLAGS_SECONDS = env.int("CACHE_FLAGS_SECONDS", default=0) FLAGS_CACHE_LOCATION = "environment-flags"默认值为0,即默认不启用该层缓存。该缓存对应CACHES字典中的environment-flags后端(api/app/settings/common.py):
FLAGS_CACHE_LOCATION: { "BACKEND": "django.core.cache.backends.locmem.LocMemCache", "LOCATION": FLAGS_CACHE_LOCATION, },从源码实现看,SDKFeatureStates.get()在 api/features/views.py 中先判断settings.CACHE_FLAGS_SECONDS > 0,命中时才走_get_flags_from_cache(request.environment)的缓存读取路径,否则直接查询数据库计算特征状态:
if settings.CACHE_FLAGS_SECONDS > 0: data = self._get_flags_from_cache(request.environment) else: data = self.get_serializer( get_environment_feature_states( request.environment, hide_server_key_only=self._hide_server_key_only, from_replica=True, ), many=True, ... )实际使用时,将该变量设为大于 0 的秒数即可启用,例如CACHE_FLAGS_SECONDS=60表示/flags结果最多缓存 60 秒。
2.2 项目分段缓存(CACHE_PROJECT_SEGMENTS_SECONDS)
CACHE_PROJECT_SEGMENTS_SECONDS控制给定项目的分段集合在内存中的缓存秒数,定义于 api/app/settings/common.py:
CACHE_PROJECT_SEGMENTS_SECONDS = env.int("CACHE_PROJECT_SEGMENTS_SECONDS", 0) PROJECT_SEGMENTS_CACHE_LOCATION = "project-segments"默认同样为0(不缓存)。对应缓存后端在 api/projects/services.py 中被取出并使用:
project_segments_cache = caches[settings.PROJECT_SEGMENTS_CACHE_LOCATION]写入时(api/projects/services.py)以timeout=settings.CACHE_PROJECT_SEGMENTS_SECONDS作为过期时间。在分段数量较多、分段规则复杂的项目中,开启该缓存可以显著减少每次评估时的分段查询开销。
三、Flags 与 Identities 端点缓存
3.1 三个配置变量
官方文档指出,要为GET /flags与GET /identities两个端点(仅限 GET 请求)启用缓存,需要设置以下环境变量([FLAGS|IDENTITIES]表示分别对两个端点各配置一套):
| 环境变量 | 说明 | 示例值 | 默认值 |
|---|---|---|---|
GET_[FLAGS\|IDENTITIES]_ENDPOINT_CACHE_SECONDS | 缓存对应 GET 端点的响应的秒数 | 60 | 0 |
GET_[FLAGS\|IDENTITIES]_ENDPOINT_CACHE_BACKEND | 所选 Django 缓存后端的 Python 路径 | django.core.cache.backends.memcached.PyMemcacheCache | django.core.cache.backends.dummy.DummyCache |
GET_[FLAGS\|IDENTITIES]_ENDPOINT_CACHE_LOCATION | 缓存后端的位置(如 Memcached 的地址) | 127.0.0.1:11211 | get_flags_endpoint_cache(各自对应的缓存名) |
默认情况下,GET_FLAGS_ENDPOINT_CACHE_BACKEND与GET_IDENTITIES_ENDPOINT_CACHE_BACKEND都指向DummyCache(空操作缓存),且秒数为0,因此默认不启用任何端点级缓存,保证行为与未配置时完全一致。相关定义见 api/app/settings/common.py:
GET_FLAGS_ENDPOINT_CACHE_SECONDS = env.int("GET_FLAGS_ENDPOINT_CACHE_SECONDS", default=0) GET_FLAGS_ENDPOINT_CACHE_NAME = "get_flags_endpoint_cache" GET_FLAGS_ENDPOINT_CACHE_BACKEND = env.str( "GET_FLAGS_ENDPOINT_CACHE_BACKEND", default="django.core.cache.backends.dummy.DummyCache", ) GET_FLAGS_ENDPOINT_CACHE_LOCATION = env.str( "GET_FLAGS_ENDPOINT_CACHE_LOCATION", default=GET_FLAGS_ENDPOINT_CACHE_NAME, )这两个缓存会在CACHES中注册为独立的命名缓存(api/app/settings/common.py),与默认缓存相互隔离:
GET_FLAGS_ENDPOINT_CACHE_NAME: { "BACKEND": GET_FLAGS_ENDPOINT_CACHE_BACKEND, "LOCATION": GET_FLAGS_ENDPOINT_CACHE_LOCATION, }, GET_IDENTITIES_ENDPOINT_CACHE_NAME: { "BACKEND": GET_IDENTITIES_ENDPOINT_CACHE_BACKEND, "LOCATION": GET_IDENTITIES_ENDPOINT_CACHE_LOCATION, },3.2 完整示例:Memcached 缓存 30 秒
官方文档给出的示例是在memcached-container:11211的 Memcached 实例上,同时缓存 flags 与 identities 两个端点的响应 30 秒:
GET_FLAGS_ENDPOINT_CACHE_SECONDS: 30 GET_FLAGS_ENDPOINT_CACHE_BACKEND: django.core.cache.backends.memcached.PyMemcacheCache GET_FLAGS_ENDPOINT_CACHE_LOCATION: memcached-container:11211 GET_IDENTITIES_ENDPOINT_CACHE_SECONDS: 30 GET_IDENTITIES_ENDPOINT_CACHE_BACKEND: django.core.cache.backends.memcached.PyMemcacheCache GET_IDENTITIES_ENDPOINT_CACHE_LOCATION: memcached-container:112113.3 底层实现:cache_page 装饰器与 Vary 头
端点级缓存通过 Django 的cache_page装饰器实现。在 api/features/views.py 中,SDKFeatureStates(即GET /flags)的get()方法被如下修饰:
@method_decorator(vary_on_headers(SDK_ENVIRONMENT_KEY_HEADER)) @method_decorator( cache_page( timeout=settings.GET_FLAGS_ENDPOINT_CACHE_SECONDS, cache=settings.GET_FLAGS_ENDPOINT_CACHE_NAME, ) ) def get(self, request, identifier=None, *args, **kwargs):GET /identities的SDKIdentities.get()采用了完全相同的模式(api/environments/identities/views.py):
@method_decorator(vary_on_headers(SDK_ENVIRONMENT_KEY_HEADER)) @method_decorator( cache_page( timeout=settings.GET_IDENTITIES_ENDPOINT_CACHE_SECONDS, cache=settings.GET_IDENTITIES_ENDPOINT_CACHE_NAME, ) ) def get(self, request):两点值得注意:
vary_on_headers(SDK_ENVIRONMENT_KEY_HEADER):缓存键依据环境 SDK 密钥请求头区分,保证不同环境之间的响应互不串扰——这是多租户场景下端点级缓存能够安全使用的前提。- 整页缓存 vs 标志缓存:
CACHE_FLAGS_SECONDS缓存的是标志评估结果本身,而GET_FLAGS_ENDPOINT_CACHE_SECONDS缓存的是整个 HTTP 响应;两者可以独立启用,也可以同时启用。
3.4 选用何种后端
由于CACHE_BACKEND是任意 Django 缓存后端的 Python 路径,理论上可以接入 Django 官方支持的所有后端(api/app/settings/common.py 中另有注释提示:使用 Redis 时应将后端设为django_redis.cache.RedisCache、位置设为 Redis URL,且默认开启DJANGO_REDIS_IGNORE_EXCEPTIONS以避免 Redis 故障拖垮主链路)。常见选择包括:
django.core.cache.backends.memcached.PyMemcacheCache(示例所用,适合跨进程共享的端点缓存);django_redis.cache.RedisCache(若基础设施已有 Redis,且希望与限流、会话等共用一套缓存);django.core.cache.backends.locmem.LocMemCache(单进程部署可考虑,但多进程/多副本部署时各进程缓存不一致,不推荐用于端点缓存);django.core.cache.backends.dummy.DummyCache(默认值,即不缓存)。
四、环境文档缓存(Environment Document Caching)
环境文档是 SDK 在启动时拉取的环境级完整配置快照,包含特征状态、多变量选项、分段等信息。构建它的查询较重(见 api/environments/models.py 中_get_environment_document_from_db的大量select_related/prefetch_related),因此在“重度使用环境文档”的场景下,缓存收益非常明显。
4.1 配置变量
启用环境文档缓存需要设置以下环境变量:
| 环境变量 | 说明 | 示例值 | 默认值 |
|---|---|---|---|
CACHE_ENVIRONMENT_DOCUMENT_MODE | 缓存模式,取值为PERSISTENT或EXPIRING。虽然默认值是EXPIRING,但由于CACHE_ENVIRONMENT_DOCUMENT_SECONDS默认值为0,默认实际上并不缓存 | PERSISTENT | EXPIRING |
CACHE_ENVIRONMENT_DOCUMENT_SECONDS | 环境文档的缓存秒数(仅在CACHE_ENVIRONMENT_DOCUMENT_MODE=EXPIRING时生效) | 60 | 0(即不缓存) |
定义位于 api/app/settings/common.py:
CACHE_ENVIRONMENT_DOCUMENT_LOCATION = env( "CACHE_ENVIRONMENT_DOCUMENT_LOCATION", default="environment-documents" ) CACHE_ENVIRONMENT_DOCUMENT_BACKEND = env( "CACHE_ENVIRONMENT_DOCUMENT_BACKEND", "django.core.cache.backends.db.DatabaseCache" ) CACHE_ENVIRONMENT_DOCUMENT_MODE = env.enum( "CACHE_ENVIRONMENT_DOCUMENT_MODE", enum=EnvironmentDocumentCacheMode, default=EnvironmentDocumentCacheMode.EXPIRING.value, ) CACHE_ENVIRONMENT_DOCUMENT_SECONDS = env.int("CACHE_ENVIRONMENT_DOCUMENT_SECONDS", 0) CACHE_ENVIRONMENT_DOCUMENT_OPTIONS = env.json( "CACHE_ENVIRONMENT_DOCUMENT_OPTIONS", default=None )额外还有三个非必需变量:
CACHE_ENVIRONMENT_DOCUMENT_LOCATION:缓存键前缀/位置,默认environment-documents;CACHE_ENVIRONMENT_DOCUMENT_BACKEND:缓存后端路径,默认django.core.cache.backends.db.DatabaseCache(数据库表缓存);CACHE_ENVIRONMENT_DOCUMENT_OPTIONS:透传给后端构造的额外选项(JSON),默认None。
该缓存在CACHES中注册如下(api/app/settings/common.py),TIMEOUT在PERSISTENT模式下为None(永不过期),在EXPIRING模式下为CACHE_ENVIRONMENT_DOCUMENT_SECONDS:
CACHE_ENVIRONMENT_DOCUMENT_LOCATION: { "BACKEND": CACHE_ENVIRONMENT_DOCUMENT_BACKEND, "LOCATION": CACHE_ENVIRONMENT_DOCUMENT_LOCATION, "TIMEOUT": ( None if CACHE_ENVIRONMENT_DOCUMENT_MODE == EnvironmentDocumentCacheMode.PERSISTENT else CACHE_ENVIRONMENT_DOCUMENT_SECONDS ), "OPTIONS": CACHE_ENVIRONMENT_DOCUMENT_OPTIONS or {}, },4.2 PERSISTENT 与 EXPIRING 两种模式的区别
EXPIRING(默认):文档在缓存中保存CACHE_ENVIRONMENT_DOCUMENT_SECONDS秒后过期,过期后重新从数据库构建并回填。适合“允许短暂的数据延迟、希望简单可控”的场景。注意默认秒数为0,不设置该变量就等于没有缓存。PERSISTENT:文档写入缓存后不设置过期时间,直到文档被显式失效。适合环境文档读取量极大、需要极致减少数据库压力的场景。若同时设置了CACHE_ENVIRONMENT_DOCUMENT_SECONDS,系统会打印警告并忽略该变量(api/app/settings/common.py):
if ( CACHE_ENVIRONMENT_DOCUMENT_MODE == EnvironmentDocumentCacheMode.PERSISTENT and CACHE_ENVIRONMENT_DOCUMENT_SECONDS ): warnings.warn( "Ignoring CACHE_ENVIRONMENT_DOCUMENT_SECONDS variable " 'since CACHE_ENVIRONMENT_DOCUMENT_MODE == "PERSISTENT"' )4.3 两个重要的使用警告(务必阅读)
官方文档对持久缓存给出两条明确提示:
caution:持久缓存只能与提供集中式缓存(centralised cache)的后端配合使用,不应与例如
LocMemCache这样的本地内存缓存一起使用。
原因很直观:LocMemCache是每进程独立的内存缓存,多进程/多副本部署下各进程的缓存互不可见;一旦通过模型钩子删除了某一个进程里的缓存条目,其他进程仍会返回过期文档,造成数据不一致。生产环境请使用 Memcached、Redis、或默认的数据库表缓存(DatabaseCache)这类集中式后端。
info:使用持久缓存时,一次变更可能需要数秒才能反映到缓存中;这也可以通过提升任务处理器(task processor)的性能来优化。
也就是说,PERSISTENT模式下的缓存更新是异步的,变更生效存在数秒延迟,这是需要接受的最终一致性代价。
4.4 写入与读取的完整调用链
环境文档缓存的读写逻辑集中在 api/environments/models.py:
- 写入:
Environment.write_environment_documents()(api/environments/models.py)在PERSISTENT模式下批量构建文档并写入缓存,且特意“使用 SDK mapper 使缓存与数据库回退路径完全一致”(map_environment_to_sdk_document):
elif ( settings.CACHE_ENVIRONMENT_DOCUMENT_MODE == EnvironmentDocumentCacheMode.PERSISTENT ): environment_document_cache.set_many( { e.api_key: map_environment_to_sdk_document(e) for e in environments } )- 读取:
Environment.get_environment_document(api_key)(api/environments/models.py)先判断缓存是否启用,命中走_get_environment_document_from_cache;后者(api/environments/models.py)在缓存未命中时回源数据库并回填缓存,同时通过 Prometheus 指标flagsmith_environment_document_cache_queries_total记录CACHE_HIT/CACHE_MISS,方便观测缓存命中率:
environment_document = environment_document_cache.get(api_key) if not (cache_hit := environment_document is not None): environment_document = cls._get_environment_document_from_db(api_key) environment_document_cache.set(api_key, environment_document) flagsmith_environment_document_cache_queries_total.labels( result=CACHE_HIT if cache_hit else CACHE_MISS, ).inc()- 失效:模型钩子
delete_environment_document_from_cache(api/environments/models.py)在环境被删除时按api_key删除对应缓存;更新环境api_key时同样先删除旧键再重建(update_environment_document_cache,api/environments/models.py)。这正是PERSISTENT模式能保持最终一致性的机制——变更后的文档会由后台任务批量写回缓存,因此文档提到“变更可能需要几秒才生效”。
五、常见部署组合建议
根据上述四类缓存,典型的自托管生产配置(以 Redis 为例)可以这样组织:
# 标志级缓存:SDK 拉取 /flags 的结果缓存 60 秒 CACHE_FLAGS_SECONDS: 60 # 项目分段缓存:分段集合缓存 300 秒 CACHE_PROJECT_SEGMENTS_SECONDS: 300 # 端点级缓存:/flags 与 /identities 整页响应,使用集中式后端 GET_FLAGS_ENDPOINT_CACHE_SECONDS: 30 GET_FLAGS_ENDPOINT_CACHE_BACKEND: django_redis.cache.RedisCache GET_FLAGS_ENDPOINT_CACHE_LOCATION: redis://redis:6379/1 GET_IDENTITIES_ENDPOINT_CACHE_SECONDS: 30 GET_IDENTITIES_ENDPOINT_CACHE_BACKEND: django_redis.cache.RedisCache GET_IDENTITIES_ENDPOINT_CACHE_LOCATION: redis://redis:6379/1 # 环境文档缓存:持久模式,文档变更异步更新 CACHE_ENVIRONMENT_DOCUMENT_MODE: PERSISTENT CACHE_ENVIRONMENT_DOCUMENT_BACKEND: django_redis.cache.RedisCache CACHE_ENVIRONMENT_DOCUMENT_LOCATION: environment-documents需要注意:环境文档的持久缓存只应配合集中式缓存后端(如 Redis、Memcached、数据库表缓存),切勿使用LocMemCache。
六、验证与观测
仓库测试覆盖了这些缓存的读写行为,可作为配置正确性的参考:
- api/tests/unit/features/test_unit_features_views.py:覆盖
CACHE_FLAGS_SECONDS、GET_FLAGS_ENDPOINT_CACHE_*相关的标志响应缓存行为; - api/tests/unit/environments/test_unit_environments_models.py:覆盖环境文档缓存的读写与失效;
- api/tests/unit/projects/test_unit_projects_models.py:覆盖项目分段缓存。
运行层面,可重点关注以下观测手段:
- 环境文档缓存命中率由
flagsmith_environment_document_cache_queries_total指标按CACHE_HIT/CACHE_MISS标签统计(api/environments/models.py),可通过 Prometheus 监控该指标评估缓存收益; - 端点级缓存生效与否可通过响应时间与数据库查询日志对比验证;
PERSISTENT模式下的异步更新延迟,则可通过“修改标志后数秒再拉取文档”的时序来确认任务处理器是否及时刷新。
结语
Flagsmith 的缓存体系覆盖了从标志评估结果、分段集合、端点整页响应到环境文档的四条路径,其中前两者由秒数变量简单开启,端点级缓存通过 Djangocache_page实现并支持自由选择后端,环境文档缓存则提供了EXPIRING与PERSISTENT两种模式以满足不同的延迟与一致性诉求。配置时的三条底线是:端点缓存键会按环境 SDK 密钥头隔离、环境文档持久缓存必须使用集中式后端、PERSISTENT模式下的更新存在数秒延迟。合理组合这四类缓存,即可在自托管部署中显著降低 SDK 高频请求对数据库的压力。
- 后端
- 前端
【免费下载链接】flagsmith
Flagsmith is an open-source feature flag platform with remote config, experimentation, and self-hosted or cloud deployment options.
相关推荐
Vector缓存策略完全指南:高效内存缓存与磁盘缓存配置
Vector缓存策略完全指南:高效内存缓存与磁盘缓存配置 Vector作为一个高性能的开源observability数据管道工具,提供了强大的缓存机制来确保数据
可观测性数据工程数据集成日志分析UnifoLM-WMA-0代码架构详解:从模型训练到推理的完整实现
UnifoLM WMA 0代码架构详解:从模型训练到推理的完整实现 UnifoLM WMA 0是宇树科技开源的世界模型 动作框架,专为通用机器人学习设计。这个创
人工智能具身智能机器人计算机视觉媒体生成预训练终极指南:如何用RxJS shareReplay解决前端数据缓存痛点与高级失效策略
终极指南:如何用RxJS shareReplay解决前端数据缓存痛点与高级失效策略 在现代前端开发中,数据缓存是提升用户体验和应用性能的关键技术。而RxJS作为
后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考