☰
Flagsmith 自托管缓存策略实战:为 /flags、/identities 与环境文档端点配置高效缓存
2026/10/8 1:57:30 网站建设 项目流程
  • 后端
  • 前端

【免费下载链接】flagsmith

Flagsmith is an open-source feature flag platform with remote config, experimentation, and self-hosted or cloud deployment options.

项目地址:https://gitcode.com/gh_mirrors/fl/flagsmith
点击查看免费下载

本文以 Flagsmith 开源项目官方文档 caching-strategies.md 为骨架,结合仓库源码,系统讲解 Flagsmith API 的内存缓存体系:涵盖环境标志缓存、项目分段缓存、GET /flags与GET /identities端点缓存,以及环境文档(Environment Document)的两种缓存模式。读完本文,你将掌握全部相关环境变量的含义、默认值与适用场景,并能针对高并发 SDK 流量设计出一套可落地的自托管缓存方案。

一、Flagsmith 缓存了什么:四类核心缓存对象

Flagsmith 的 Django API 通过内存(in-memory)缓存改善多个高频端点的响应性能。根据官方文档,被缓存的主要对象分为以下四类:

  1. 环境标志(Environment flags):/flags接口返回的标志数据,缓存秒数由环境变量CACHE_FLAGS_SECONDS控制。
  2. 项目分段(Project segments):给定项目下所有分段(Segment)的集合,缓存秒数由CACHE_PROJECT_SEGMENTS_SECONDS控制。
  3. 标志与身份端点缓存(Flags & identities endpoint caching):为GET /flags与GET /identities接口的完整响应提供缓存,后端、位置、超时均由开发者自行选择。
  4. 环境文档(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 端点的响应的秒数600
GET_[FLAGS\|IDENTITIES]_ENDPOINT_CACHE_BACKEND所选 Django 缓存后端的 Python 路径django.core.cache.backends.memcached.PyMemcacheCachedjango.core.cache.backends.dummy.DummyCache
GET_[FLAGS\|IDENTITIES]_ENDPOINT_CACHE_LOCATION缓存后端的位置(如 Memcached 的地址)127.0.0.1:11211get_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:11211

3.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,默认实际上并不缓存PERSISTENTEXPIRING
CACHE_ENVIRONMENT_DOCUMENT_SECONDS环境文档的缓存秒数(仅在CACHE_ENVIRONMENT_DOCUMENT_MODE=EXPIRING时生效)600(即不缓存)

定义位于 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.

项目地址:https://gitcode.com/gh_mirrors/fl/flagsmith
点击查看免费下载

相关推荐

上一篇:Beremiz开源PLC深度解析:IEC-61131标准下的工业自动化架构设计与实战指南
下一篇:Inconsolata 字体:程序员必备的终极等宽字体解决方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询