后端【免费下载链接】reddithistorical code from reddit.com项目地址https://gitcode.com/gh_mirrors/re/reddit点击查看免费下载本文以 r2/r2/config/feature/README.md 为核心系统讲解 reddit 历史代码库中用于按用户与请求维度快速启停功能的特性开关Feature FlagAPIr2.config.feature。你将掌握从is_enabled基础调用、live_config 全量选择器配置到 A/B 实验分桶、URL 强制变体的完整实战方法并对照源码与单元测试理解其底层判定与确定性分桶原理。一、什么是 r2.config.featurer2.config.feature是 reddit 官方历史代码仓库re/reddit中的特性开关 API其设计目标是快速地对特定用户群体和特定请求切换功能的开与关无需重新发布代码。它的设计受 Etsy 的开源特性框架Etsys feature framework启发README 明确建议在扩展本模块前先去参考该框架的经验积累。从代码结构看本模块位于 r2/r2/config/feature/由三个文件组成一个完整的判定链路文件职责feature.py对外 APIis_enabled、variant、all_enabled以及 FeatureState 进程内缓存state.pyFeatureState解析配置并执行全部选择器与分桶判定逻辑world.pyWorld封装对当前请求/应用状态用户、子版块、子域名、loid、live_config的访问代理World的存在使判定逻辑与请求环境解耦便于在 cron 任务等非请求上下文中复用也便于单元测试用 Mock 替换见 feature_test.py 中MockWorld的实现。二、快速上手在代码与模板中使用1. Python 中的判定核心 API 是feature.is_enabled(name)按当前请求的用户与上下文返回布尔值from r2.config import feature if feature.is_enabled(some_flag): result do_new_thing() else: result do_old_thing()is_enabled的签名见 feature.py为def is_enabled(name, userNone, subredditNone):其中user与subreddit是可选的覆盖参数——正常情况下无需传参函数会通过_world自动获取当前登录用户与当前子版块这两个参数主要供 cron 作业等非请求上下文使用。判定时还会自动附加当前子域名与 OAuth client 信息if not user: user _world.current_user() if not subreddit: subreddit _world.current_subreddit() subdomain _world.current_subdomain() oauth_client _world.current_oauth_client() return _get_featurestate(name).is_enabled( useruser, subredditsubreddit, subdomainsubdomain, oauth_clientoauth_client, )值得注意的是is_enabled通过_get_featurestate(name)对每个特性名创建或复用一个FeatureState对象并缓存在_featurestate_cache字典中当 live_config 发生更新时通过feature_hooks注册的worker.live_config.update钩子见 feature.py会清空该缓存实现配置的实时生效。2. Mako 模板中的判定同一 API 可直接用于 Mako 模板实现服务端渲染时的条件分支% if feature.is_enabled(some_flag): strongNew thing!/strong % else: spanOld thing./span % endif3. 仓库中的真实调用示例该 API 在仓库中被广泛使用可作为参考范例api.py 中用于sticky_comments置顶评论、stylesheets_everywhere子版块样式表、mobile_settings、related_subreddits、autoexpand_media_previews、persistent_vote_a_a等特性的分支front.py 中用于safe_search安全搜索与legacy_search旧版搜索结果页渲染的切换promotecontroller.py 中用于ads_auction广告竞价的灰度开关feature_utils.py 中通过feature.is_enabled(utm_comment_links)判断是否为评论链接附加 UTM 跟踪参数。这些调用点恰好印证了 README 中“特性名应为字符串字面量”的编码规范——全仓库搜索feature.is_enabled(xxx)即可快速定位某个开关的全部影响面。三、live_config 配置从“全开/全关”到精细选择器特性的开闭状态通过 live_config 配置项驱动。由于当前实现复用 live_config 机制每个特性开关必须在配置中加feature_前缀README 说明未来可能提供独立的实时更新特性配置块。配置值可以是简单的on/off标识也可以是 JSON 对象含选择器组合。以下配置示例全部来自 README 原文可直接照抄使用# Completely On feature_some_flag on # Completely Off feature_some_flag off # On for admin feature_some_flag {admin: true} # On for employees feature_some_flag {employee: true} # On for gold users feature_some_flag {gold: true} # On for users with the beta preference enabled feature_some_flag {beta: true} # On for logged in users feature_some_flag {loggedin: true} # On for logged out users feature_some_flag {loggedout: true} # On by URL, like ?featurepublic_flag_name feature_some_flag {url: public_flag_name} # On by group of users feature_some_flag {users: [umbrae, ajacksified]} # On when viewing certain subreddits feature_some_flag {subreddits: [wtf, aww]} # On by subdomain feature_some_flag {subdomains: [beta]} # On by OAuth client IDs feature_some_flag {oauth_clients: [xyzABC123]} # On for a percentage of loggedin users (0 being no users, 100 being all of them) feature_some_flag {percent_loggedin: 25} # On for a percentage of loggedout users (0 being no users, 100 being all of them) # N.B: This is based on the value of the loid cookie, if there is no loid # cookie the feature will be off. # The loid cookie is currently set in JavaScript, so you cant expect it to # exist on the first visit or in requests made by API clients. feature_some_flag {percent_loggedout: 25} # For both admin and a group of users feature_some_flag {admin: true, users: [user1, user2]}配置的解析规则FeatureState._parse_config见 state.py决定了配置的取值语义配置名为feature_%s% 特性名也可显式传入config_name覆盖缺失配置或值为off→ 视为全局关闭DISABLED_CFG {enabled: off}值为on→ 视为全局开启ENABLED_CFG {enabled: on}其他值尝试按 JSON 解析解析失败ValueError/TypeError或结果不是 dict 时记录 warning 并回退为关闭fail-closed 语义保证配置写错不会误开放流量。选择器判定顺序与“或”语义FeatureState.is_enabled见 state.py的判定顺序为先做常规选择器判定_is_config_enabled再判百分比选择器_is_percent_enabled最后检查experiment实验配置全部未命中则返回False默认关闭。_is_config_enabled见 state.py按如下顺序逐一检查多个选择器之间是“或”关系任一命中即开启选择器判定依据对应World方法enabled: on/off全局开/关优先于其他选择器测试test_multiple验证了全局 off 时即使admin: true也关闭urlrequest.GET.getall(feature)即 URL 参数?featurexxxadmin用户名是否在g.admins列表中world.is_adminemployee用户是否标记为员工user.employeebeta用户是否开启 beta 偏好user.pref_betagold用户是否拥有 gold 会员user.goldloggedin/loggedout是否登录world.is_user_loggedinusers用户名列表大小写不敏感双方转小写后比较subreddits子版块名列表大小写不敏感subdomains子域名列表大小写不敏感oauth_clientsOAuth client ID 集合这里体现了一个重要特性URL 选择器优先于其他选择器。这在 README 的“URL 强制变体”部分有专门用途见下文第四节。百分比放量percent_loggedin 与 percent_loggedout百分比选择器用于逐步放量0 表示不放量、100 表示全量percent_loggedin基于用户_fullname如t2_beef做确定性哈希分桶。_calculate_bucket见 state.py使用sha1(feature名 seed)取模NUM_BUCKETS1000因此同一用户对同一特性永远落在同一桶且不同特性因混入特性名而不会让相同用户群同时被选中放量。判定时bucket / (NUM_BUCKETS / 100) percent_loggedin即开启精度可达 0.1%。percent_loggedout基于loidcookie 分桶。代码特意“只看 loid 末尾 4 个字符”并转 36 进制后对 100 取模见 state.py目的是与前端 JS 的分桶逻辑保持一致。注意 README 的强调loid cookie 由 JavaScript 写入因此首次访问或 API 客户端请求可能没有 loid此时该开关必然为 off。单元测试 feature_test.py 用 2000 个用户验证了percent_loggedin/percent_loggedout在 0%、10%、25%、50%、99%、100% 六档下实际开启比例与设定值的偏差小于 10%模糊断言并验证 0% 时全关、100% 时全开。四、A/B 实验eligibility 与 bucketing 两阶段模型README 指出特性开关还可以用来定义 A/B 型实验。实验逻辑上分为两部分资格检查eligibility check判断用户是否被允许进入实验。资格由与上文相同的选择器决定但不包括percent_loggedin/percent_loggedout因为实验本身已承担放量职责二者冗余。分桶bucketing将合格用户分配到某个实验变体variant或排除当所有变体百分比之和小于 100 时剩余用户落在未分配桶中。对实验而言is_enabled对以下用户返回False不合格用户、落入对照组control group的用户、被排除的用户。对is_enabled为True的用户应调用variant(name)获取其具体变体。1. 实验配置格式from r2.config import feature if feature.is_enabled(some_flag): variant feature.variant(some_flag) if variant test_something: do_new_thing() elif variant test_something_else: do_other_new_thing() else: raise NotImplementedError(unknown variant %s for some_flag % variant) else: do_old_thing()对应 live_config 实验参数# loggedin only experiment with two test variants feature_some_flag {experiment: {loggedin: true, experiment_id: 12345, variants: {test_something: 5.5, test_something_else: 10}}} # Or with custom control group sizes: feature_some_flag {experiment: {loggedin: true, experiment_id: 12345, variants: {test_something: 5.5, test_something_else: 10, control_1: 20, control_2: 20}}} # these can be mixed and matched with other selectors (and will OR) # this will enable the flag for gold users, and then run an experiment for other logged in users feature_some_flag {gold: true, experiment: {loggedin: true, experiment_id: 12345, variants: {test_something: 5.5, test_something_else: 10, control_1: 20, control_2: 20}}}第三种写法体现了“常规选择器 实验”的混用gold 用户直接命中gold选择器被开启不进实验其余 loggedin 用户则进入实验分桶——二者通过“或”逻辑共存。如果只定义了一个非对照组变体A/A/B 测试代码可简化为普通is_enabled分支from r2.config import feature if feature.is_enabled(some_flag): do_new_thing() else: do_old_thing()2. experiment 字段说明README 明确给出实验字典的三个核心字段experiment_id整数特性名要求在所有当前特性中唯一而experiment_id 要求在全时间线上唯一。这样数据团队在分析历史数据时可以无歧义地标识实验。bucket 事件中会携带该 id见g.events.bucketing_event(experiment_id...)。variants变体名 → 百分比的字典百分比表示大约多少合格用户会被选中进入该变体。百分比不应超过 100/nn 为变体数量实验中变体数量不能中途改变但各变体百分比可以动态调整百分比可精确到十分位。若不定义系统自动添加两个对照组control_1和control_2各占 10%——这正是 state.py 中DEFAULT_CONTROL_GROUPS {control_1: 10, control_2: 10}的含义。enabled布尔值默认 true设为false可临时停用某个实验而保留其定义。此外源码还支持两类实验变体用户实验user experiment默认类型基于用户身份分桶。登录用户以user._fullname为种子未登录用户以loid为种子受g.enable_loggedout_experiments配置约束无 loid 则不参与。页面实验page experiment当 experiment 配置含page字段时生效基于当前内容link / comment / subreddit的_fullname分桶并支持subreddit_only、link_only约束与experiment_seed种子见 state.py。all_enabled明确排除页面实验因其与特定用户无关。3. 确定性分桶算法_choose_variant见 state.py是分桶核心满足两个关键性质确定性相同的桶号与变体集必然得到相同结果单调可扩展提高某个变体百分比时原本落在 A、B 等变体的桶保持不变新增的流量只来自此前“未分配”的桶。源码注释用一个直观示意说明分配策略先按bucket % n决定候选变体再判断bucket 变体百分比 × n × bucket_multiplier是否落在该变体的实际比例内否则返回None被排除。这种“不重排旧桶”的特性使其非常适合上线后逐步提高实验流量的场景。代码还会对超过 1/n 上限的变体记录 warning 日志帮助及早发现配置错误。4. Bucket 事件与 all_enabled_is_experiment_enabled见 state.py在用户获得变体时会通过g.events.bucketing_event发送分桶事件且每个请求对同一(user|page, 特性名, 分桶 id)只发送一次由c.have_sent_bucketing_event集合去重。对外 APIall_enabled(userNone)见 feature.py返回用户所有已启用特性及实验变体信息格式为{特性名: True}或{实验名: {experiment_id: ..., variant: ...}}。README 和 docstring 都强调它不触发分桶事件不能用于服务端特性开关只供客户端据此联动展示客户端需自行上报相应的 bucketing 事件。五、URL 强制分桶测试与定向流量的利器为便于测试与定向投放可以对部分标志条件使用第二种 URL 语法强制指定变体# ?featuresome_flag_something will force the test_something variant and # ?featuresome_flag_something_else will force test_something_else feature_some_flag {url: {some_flag_something: test_something, some_flag_something_else: test_something_else}}源码实现中见 state.py当url的值为 dict 时遍历当前请求的?feature参数命中的键将其映射值作为变体名返回is_enabled通过_is_variant_enabled判定——变体是None或属于默认对照组control_1/control_2时视为未开启其余变体视为开启。variant()见 state.py在存在 dict 型url配置时直接返回强制变体覆盖实验分桶结果。单元测试 feature_test.py 验证了url为字符串时命中即开启url为 dict 时返回对应变体若强制变体是control_1/control_2is_enabled返回False与对照组语义一致。六、什么时候该用特性开关README 总结了四个典型使用场景全部围绕“降低发布风险、快速回退”Admin 先睹为快功能上线前先只对公司内部admin开启供评审而暂不适合用 staging 环境验证时外部先期合作正式上线前先开放给第三方开发者与版主mods试用渐进放量对可能显著影响负载的功能逐步增加流量对应percent_loggedin/percent_loggedout放量快速熔断为可能需要立刻关闭的功能加一层保护用于负载卸载load shedding、安全应急等场景。七、编码规范Style GuidelinesREADME 沿用了 Etsy 的特性框架编码准则共三条特性名必须是字符串字面量传给is_enabled的特性名应始终是字符串字面量便于全局搜索所有检查点。如果在运行时动态拼接特性名再去检查说明你很可能滥用了特性系统——这种场景应直接用普通配置数据驱动代码而非特性 API。不要缓存特性判定结果不要调用一次feature.is_enabled就把结果存进控制器实例变量。特性机制本身已缓存计算产物即上文_featurestate_cache直接随时调用feature.is_enabled已足够快这也让“哪些代码依赖某特性”始终可被搜索到。可整体删除性检查每当写一个以feature.is_enabled为条件的 if 块时想象一下——要么去掉检查保留代码要么把检查和代码一起删掉。if 块内不应存在“特性删除后还需要被抢救保留”的碎片代码。八、验证与测试如何确认开关行为仓库在 r2/r2/tests/unit/config/feature_test.py 提供了覆盖完整选择器矩阵的单元测试可作为理解行为的最佳佐证全局开关enabled: on/off的优先级与豁免性test_enabled、test_disabled、test_multiple身份选择器admin、employee、beta、gold、loggedin、loggedout 各自的开启/关闭用例名单与上下文选择器users、subreddits、subdomains的命中与未命中并验证大小写不敏感匹配如配置WTF可命中wtf百分比放量2000 用户的模糊比例断言0/10/25/50/99/100URL 强制变体字符串与 dict 两种语法的开启、关闭、变体返回。测试通过MockWorld替换World的真实请求依赖将current_user、current_subreddit、current_loid等全部 mock 掉使判定逻辑可以在无请求环境下独立验证——这从侧面印证了World抽象层带来的可测试性设计。九、小结r2.config.feature以“live_config 配置 World 请求代理 FeatureState 判定/分桶”三层结构为 reddit 提供了从简单on/off、按身份/上下文细分到确定性 A/B 实验放量的完整特性开关能力。理解其选择器“或”语义、fail-closed 配置解析、基于 SHA-1 桶的单调扩展分桶算法以及“特性名必须字符串字面量”的编码纪律是正确使用并在自己项目中借鉴这套方案的关键。结合 feature_test.py 的用例逐项验证可以快速确认每个配置项的准确行为边界。赞分享后端【免费下载链接】reddithistorical code from reddit.com项目地址https://gitcode.com/gh_mirrors/re/reddit点击查看免费下载相关推荐kOps 集成 Spot Ocean 实战指南特性开关、实例组配置与源码实现解析kOps 集成 Spot Ocean 实战指南特性开关、实例组配置与源码实现解析 Spot Ocean Spot.io https://link.gitco云原生集群管理运维IaCModin 日志系统Modin Logging深度指南启用、配置与源码实现剖析Modin 日志系统Modin Logging深度指南启用、配置与源码实现剖析 导读 Modin Logging 是 Modin 内置的一套日志与可观测性数据分析数据工程大数据k6 Feature Flags 深度解析k6 内核功能开关系统的规范与源码实现k6 Feature Flags 深度解析k6 内核功能开关系统的规范与源码实现 k6 通过一套统一的 Feature Flags 机制管理引擎内部的实验性与测试开发工具CI/CD上一篇华硕笔记本又慢又吵GHelper 轻量替代奥创中心的一站式使用指南下一篇Quick 共享示例Shared Examples与 Behavior用共享断言消除测试样板代码创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考