GO Feature Flag 1.57.2 配置参数详解

系统梳理 GO Feature Flag relay-proxy 1.57.2 的三类配置来源(命令行、配置文件、环境变量)与优先级,逐项实测服务端、retriever、鉴权、导出、flags 文件语义并给出生产模板。

GO Feature FlagGolangDevOps工程Ubuntu 22.042026/10/28

GO Feature Flag 1.57.2 配置参数详解

目标:讲清 GO Feature Flag relay-proxy 1.57.2 的配置从哪来、谁覆盖谁、每个参数改了会发生什么,并用可复现命令验证配置生效。全部命令与回显在 Ubuntu 22.04.5 LTS(x86_64)+ relay-proxy 1.57.2 环境实测,版本查询日期 2026-10-28。

一、环境信息

本节给出复现实测所需的软件与路径清单。阅读本文不需要重新安装,只要机器上有一个可执行的 relay-proxy 与一份可写的配置目录即可。

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • 服务版本:GO Feature Flag relay-proxy 1.57.2(构建信息 goversion="go1.26.10"、tags="fips140v1.0")
  • 二进制:/usr/local/bin/go-feature-flag-relay-proxy
  • 主配置:/etc/go-feature-flag/goff.yaml
  • flags 文件:/etc/go-feature-flag/flags.yaml
  • 运行用户:goff
  • 监听端口:1031
  • 服务单元:go-feature-flag.service
  • 配置默认搜索目录:./、/goff/、/etc/opt/goff/,文件名固定为 goff-proxy.yaml

安装步骤见 《GO Feature Flag 1.57.2 Ubuntu 22.04 安装指南》;本文只讨论"装好之后怎么配"。

二、前置条件

  1. relay-proxy 已安装且能执行 --help;
  2. 具备 /etc/go-feature-flag/ 的写权限与 systemctl 操作权限;
  3. 有一个空闲端口用于对照实验(本文用 1032、1033、1035 做覆盖实验);
  4. curl 可用。
/usr/local/bin/go-feature-flag-relay-proxy --help

实测回显:

Usage of config:
      --config string   Location of your config file

这条回显说明了本文的一个关键事实:relay-proxy 的命令行参数只有 --config 一个,它只决定"读哪个配置文件",不参与具体参数的赋值。因此"命令行 flag > 环境变量 > 配置文件"这种常见优先级在 relay-proxy 上并不成立,真实优先级见第三章。

三、配置来源与优先级

3.1 命令行参数

--config 是唯一的启动参数,取值是配置文件路径(相对或绝对均可)。示例:

/usr/local/bin/go-feature-flag-relay-proxy --config /etc/go-feature-flag/goff.yaml

传一个不存在的路径时,程序不会立即报"文件不存在",而是回退到默认搜索逻辑,并在日志里明确写出尝试过的路径:

/usr/local/bin/go-feature-flag-relay-proxy --config /etc/go-feature-flag/not-exist.yaml

实测回显(节选):

{"level":"error","caller":"config/config_loader.go:239","msg":"not using any configuration file","error":"impossible to find config file /etc/go-feature-flag/not-exist.yaml"}
{"level":"fatal","caller":"relayproxy/main.go:76","msg":"configuration error","error":"no retriever available in the configuration"}

含义:配置文件没读到 → 没有 retriever → 直接致命退出。这类故障在 systemd 场景下表现为服务反复重启,排查入口就是 journalctl -u go-feature-flag。

3.2 配置文件与默认搜索路径

不传 --config 时,程序依次在 ./、/goff/、/etc/opt/goff/ 目录下查找名为 goff-proxy.yaml 的文件。实测把配置复制到 /goff/goff-proxy.yaml 后不带参数启动:

mkdir -p /goff
cp /etc/go-feature-flag/goff.yaml /goff/goff-proxy.yaml
SERVER_PORT=1033 timeout 3 /usr/local/bin/go-feature-flag-relay-proxy

实测回显(节选):

{"level":"info","caller":"api/server_lifecycle.go:119","msg":"Starting go-feature-flag relay proxy ...","address":"0.0.0.0:1033","version":"1.57.2"}

而把同样内容命名为 /goff/goff.yaml(其他文件名)时,日志回退为:

{"level":"error","msg":"not using any configuration file","error":"impossible to find config file in the default locations [./,/goff/,/etc/opt/goff/]"}

⚠️ 注意:默认搜索"路径 + 文件名"两者都固定。生产环境不要依赖默认搜索,ExecStart 里显式带 --config 最稳妥。

3.3 环境变量映射规则

配置文件里的每个字段都可以用同名大写环境变量覆盖,嵌套字段用下划线连接:

配置路径环境变量实测是否生效
server.portSERVER_PORT是(1031 → 1032)
retriever.pathRETRIEVER_PATH是(加载到另一份 flags 文件中的 env-flag)
logLevelLOGLEVEL是(出现 debug 日志)
pollingIntervalPOLLINGINTERVAL是(3 秒内感知 flags 变更)

验证端口覆盖:

SERVER_PORT=1032 timeout 4 /usr/local/bin/go-feature-flag-relay-proxy --config /etc/go-feature-flag/goff.yaml

实测回显(节选):

{"level":"info","caller":"api/server_lifecycle.go:119","msg":"Starting go-feature-flag relay proxy ...","address":"0.0.0.0:1032","version":"1.57.2"}

验证 retriever 路径覆盖(flags2.yaml 中开关名为 env-flag):

RETRIEVER_PATH=/etc/go-feature-flag/flags2.yaml timeout 4 \
  /usr/local/bin/go-feature-flag-relay-proxy --config /etc/go-feature-flag/goff.yaml

实测回显:

{"level":"info","ts":1793121897.106729,"msg":"flag added","key":"env-flag"}

验证日志级别覆盖:

LOGLEVEL=DEBUG timeout 3 /usr/local/bin/go-feature-flag-relay-proxy --config /etc/go-feature-flag/goff.yaml 2>&1 | grep -c '"level":"debug"'

实测回显:

2

3.4 三者优先级实测

用一个最小实验固定优先级:配置文件写 1031,环境变量写 1032,命令行只给路径。

grep -A1 '^server' /etc/go-feature-flag/goff.yaml
SERVER_PORT=1032 timeout 3 /usr/local/bin/go-feature-flag-relay-proxy --config /etc/go-feature-flag/goff.yaml \
  | grep -o '"address":"[^"]*"'

实测回显:

server:
  port: 1031
"address":"0.0.0.0:1032"

结论(实测优先级,从高到低):

  1. 环境变量:覆盖配置文件同名字段;
  2. 配置文件:提供其余全部字段的取值;
  3. 命令行 --config:不参与数值覆盖,只决定读哪份配置文件。

💡 说明:环境变量在编排平台与自动化部署里最常用(如把端口、日志级别注入),配置文件适合承载结构化的 retriever/exporter 配置。两者同时存在时以环境变量为准,排障时先 env | grep -E 'PORT|RETRIEVER|LOGLEVEL' 排除环境干扰。

四、参数总览

下表是 relay-proxy 常用参数的全景,默认值与实测行为对齐(完整清单以官方配置文档为准):

参数类型默认值作用是否实测
server.portint1031HTTP 监听端口是
pollingIntervalint(毫秒)60000flags 重新拉取间隔,最小 1000是
enablePollingJitterboolfalse拉取时间加 ±10% 抖动,避免多实例同时打存储否
fileFormatstringyamlflags 文件格式:yaml/json/toml是(yaml)
logLevelstringinfoERROR/WARN/INFO/DEBUG是
logFormatstringjsonjson/logfmt是(json)
hideBannerboolfalse是否隐藏启动 Banner否
enableSwaggerboolfalse是否开启 /swagger/是(默认 404)
enablePprofboolfalse是否开启 /debug/pprof/是(默认 404)
startWithRetrieverErrorboolfalseretriever 失败时是否仍启动是
persistentFlagConfigurationFilestring无本地持久化 flags 快照,供启动兜底否
retriever / retrieversobject / array无(必填)flags 来源是(file)
authorizedKeys.evaluation[]string无求值接口鉴权 Key是
authorizedKeys.admin[]string无管理接口鉴权 Key是
exporterobject无求值事件导出否
notifierobject无flags 变更通知否
evaluationContextEnrichmentobject无全局附加求值上下文否

解读:真正影响"服务能不能起来"的只有 retriever(或无 retriever 时的 retrievers);其余参数都有默认值。"是否实测"列为否的项,本文给出参数语义但不虚构回显,落地前请在自己的环境验证一次。

五、服务端参数详解

5.1 监听端口 server.port

server:
  port: 1031

改端口后 /health 与全部 API 都会跟着迁移。实测以 SERVER_PORT=1032 启动后,1031 上无响应、1032 上返回正常:

curl -s http://127.0.0.1:1032/health

实测回显:

{"initialized":true}

⚠️ 注意:v1.57.2 启动日志会出现 The server port is set using 'port', this option is deprecated, please migrate to 'server.port'。即使只写 server.port 也会出现,这是内部默认值回填触发的误报,监听地址以 Starting ... address 0.0.0.0:<port> 为准。

5.2 拉取间隔 pollingInterval

单位是毫秒,默认 60000。设为 3000 后,修改 flags 文件约 3 秒即生效。实测:

POLLINGINTERVAL=3000 nohup /usr/local/bin/go-feature-flag-relay-proxy \
  --config /etc/go-feature-flag/goff.yaml > /var/log/go-feature-flag/poll.log 2>&1 &
sleep 2
curl -s http://127.0.0.1:1031/info

实测回显:

{"cacheRefresh":"2026-10-27T17:25:27.304969458Z"}

往 flags 文件追加一个 promo-banner 开关,等待 6 秒后再次查询:

curl -s http://127.0.0.1:1031/info
curl -s -X POST -H "Content-Type: application/json" \
  -d '{"context":{"targetingKey":"user-003"}}' \
  http://127.0.0.1:1031/ofrep/v1/evaluate/flags/promo-banner

实测回显:

{"cacheRefresh":"2026-10-27T17:25:33.308169786Z"}
{"key":"promo-banner","value":true,"reason":"STATIC","variant":"on","metadata":{"gofeatureflag_cacheable":true}}

cacheRefresh 相隔 6 秒、开关已可求值,证明轮询确实按 3 秒周期工作。取值小于 1000 会被拒绝/回落到最小间隔;设为负数可关闭轮询(仅适合 flags 完全静态的场景)。

5.3 日志 logLevel / logFormat

logLevel: info
logFormat: json

json 便于日志采集系统按字段解析;logfmt 输出 key=value 形式,适合直接 grep。日志中几个高频字段含义:

字段含义
msg事件名,如 flag added、Starting go-feature-flag relay proxy ...
address实际监听地址,排障第一现场
version运行版本
error失败原因,配置类故障几乎都在这里

5.4 开关端点与调试端点

enableSwagger 与 enablePprof 默认关闭,实测访问均返回 404:

curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:1031/swagger/index.html
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:1031/debug/pprof/

实测回显:

404
404

生产环境保持默认关闭;确需临时排障再开启,用完立即关掉并重启。

5.5 启动容错 startWithRetrieverError

默认 false:flags 读不到就崩溃。设为 true 后,即使 flags 文件语法错误也能启动。实测用一份坏 YAML:

server:
  port: 1035
retriever:
  kind: file
  path: /tmp/bad-flags.yaml
startWithRetrieverError: true
curl -s http://127.0.0.1:1035/health
curl -s -o /dev/null -w "%{http_code}\n" -X POST -H "Content-Type: application/json" \
  -d '{"context":{"targetingKey":"u"}}' \
  http://127.0.0.1:1035/ofrep/v1/evaluate/flags/any-flag

实测回显:

{"initialized":true}
404

日志同时给出原因:

{"level":"error","msg":"Impossible to retrieve the flags, starting with the retriever error","error":"yaml: line 1: did not find expected ',' or ']'"}

⚠️ 注意:该参数让服务"活着但不可用",适合与 persistentFlagConfigurationFile 搭配做降级;无持久化快照时,求值请求会全部落空(未知开关返回 404),不建议在核心链路上长期开启。

六、Retriever 参数详解

retriever 是唯一必填项,决定 flags 从哪读。它由两个并列字段承载:单数的 retriever 与复数的 retrievers(多个来源并行拉取、按顺序合并覆盖)。

6.1 本地文件 retriever(本文使用)

retriever:
  kind: file
  path: /etc/go-feature-flag/flags.yaml

kind 固定为 file,path 为绝对路径。文件变更通过 pollingInterval 感知,无需重启。

6.2 其他 retriever 类型

kind必填字段典型用途
httpurl(可选 method/headers/timeout)从配置中心或静态站点拉取
github / gitlab / bitbucketrepositorySlug、path(可选 branch/token)flags 跟代码一起评审、走 Git 流程
s3 / googleStorage / azureBlobStorage桶/对象相关字段对象存储集中托管
redisoptions、prefix低延迟、高频变更
mongodburi、database、collection已有 MongoDB 的团队
configmapnamespace、configmap、keyKubernetes 内运行

💡 技术拓展:flags 存储位置的选择本质是"变更流程"的选择——

方案变更入口优点代价
本地文件登录服务器改 YAML最简单、无外部依赖无审计、多实例易漂移
Git 仓库提 PR 改文件有评审与历史受仓库 API 限流影响
对象存储上传对象与发布流水线天然集成需要云凭证管理
Redis/MongoDB写库变更即时、可做管理台多一个高可用组件要运维

本文采用本地文件 retriever,理由是单机部署、开关量小、变更走运维工单;多实例生产建议换成 Git 或对象存储,避免各节点文件不一致。

6.3 多来源合并 retrievers

retrievers:
  - kind: file
    path: /etc/go-feature-flag/base-flags.yaml
  - kind: file
    path: /etc/go-feature-flag/override-flags.yaml

多个来源并行读取,按声明顺序合并,后者的同名开关覆盖前者,适合"基础开关 + 环境覆盖"的分层管理。

七、求值鉴权参数详解

默认没有任何鉴权,任何能访问 1031 的客户端都可求值。设置 authorizedKeys 后,请求必须带 Authorization: Bearer <key>。

authorizedKeys:
  evaluation:
    - eval-demo-key
  admin:
    - admin-demo-key

实测(配置文件 /etc/go-feature-flag/goff-auth.yaml,端口 1032):

# 不带 Key
curl -s -o /dev/null -w "%{http_code}\n" -X POST -H "Content-Type: application/json" \
  -d '{"context":{"targetingKey":"u1"}}' \
  http://127.0.0.1:1032/ofrep/v1/evaluate/flags/promo-banner

实测回显:

401
# 带求值 Key
curl -s -X POST -H "Authorization: Bearer eval-demo-key" -H "Content-Type: application/json" \
  -d '{"context":{"targetingKey":"u1"}}' \
  http://127.0.0.1:1032/ofrep/v1/evaluate/flags/promo-banner

实测回显:

{"key":"promo-banner","value":true,"reason":"STATIC","variant":"on","metadata":{"gofeatureflag_cacheable":true}}

管理接口 /admin/v1/retriever/refresh 只认 admin 列表中的 Key:

curl -s -o /dev/null -w "%{http_code}\n" -X POST http://127.0.0.1:1032/admin/v1/retriever/refresh
curl -s -w " http=%{http_code}\n" -X POST -H "Authorization: Bearer admin-demo-key" \
  http://127.0.0.1:1032/admin/v1/retriever/refresh

实测回显:

401
{"refreshed":true} http=200

💡 说明:evaluation 与 admin 是两套独立 Key,求值 Key 不能调用管理接口。Key 属于系统级凭据,生产环境应从密钥管理系统注入(如 systemd EnvironmentFile 或编排平台的 Secret),不要写进配置文件明文。

八、导出与通知参数详解

8.1 求值事件导出 exporter

用于把每次求值的结果导出到下游(对象存储、Kafka、Webhook 等),支撑"谁在什么时候命中哪个变体"的审计。典型结构:

exporter:
  kind: webhook
  endpointUrl: https://audit.example.com/goff
  flushInterval: 10s
  maxEventInMemory: 1000

flushInterval 控制批量落盘/推送周期,maxEventInMemory 是内存缓冲上限,超过即触发一次发送。本文未配置 exporter,实测的求值响应不带导出副作用;上线前建议先在预发环境验证下游能收到事件。

8.2 变更通知 notifier

flags 文件变更后可推送到 Slack/Webhook,便于团队感知"开关被改了"。结构:

notifier:
  kind: webhook
  endpointUrl: https://chat.example.com/hook

配合 DisableNotifierOnInit: true 可避免每次启动都推一条"flags 已加载"的噪声消息。

九、flags 文件语义

配置参数决定"服务怎么跑",flags 文件决定"开关怎么算"。一个开关由三部分组成:

键必填含义
variations是变体名到取值的映射,取值可为 bool/string/number/object
targeting否定向规则列表,按顺序匹配
defaultRule是所有规则未命中时的兜底变体

9.1 定向规则与 defaultRule

new-checkout-flow:
  variations:
    enabled: true
    disabled: false
  targeting:
    - query: companyID eq "admin"
      variation: enabled
  defaultRule:
    variation: disabled

query 使用 GO Feature Flag 的表达式语法,companyID 取自求值上下文的同名字段。实测结果(见第三章求值示例):companyID=admin 返回 enabled/TARGETING_MATCH,其他值返回 disabled/DEFAULT。

9.2 百分比灰度

beta-banner:
  variations:
    on: true
    off: false
  targeting:
    - query: email co "@example.com"
      percentage:
        on: 50
        off: 50
  defaultRule:
    variation: off

对同一批 @example.com 用户按 targetingKey 做 50/50 分流,实测:

for k in u-1002 u-1003 u-1004; do
  printf "%s " $k
  curl -s -X POST -H "Content-Type: application/json" \
    -d "{\"context\":{\"targetingKey\":\"$k\",\"email\":\"$k@example.com\"}}" \
    http://127.0.0.1:1036/ofrep/v1/evaluate/flags/beta-banner
  echo
done

实测回显:

u-1002 {"key":"beta-banner","value":false,"reason":"TARGETING_MATCH_SPLIT","variant":"off","metadata":{"gofeatureflag_cacheable":true}}
u-1003 {"key":"beta-banner","value":true,"reason":"TARGETING_MATCH_SPLIT","variant":"on","metadata":{"gofeatureflag_cacheable":true}}
u-1004 {"key":"beta-banner","value":false,"reason":"TARGETING_MATCH_SPLIT","variant":"off","metadata":{"gofeatureflag_cacheable":true}}

不带 email 的用户不命中定向规则,落到默认变体:

{"key":"beta-banner","value":false,"reason":"DEFAULT","variant":"off","metadata":{"gofeatureflag_cacheable":true}}

💡 技术拓展:灰度分流算法在同类的特性开关产品里有几种常见实现——

算法特点适用场景
一致性哈希百分比同一 key 稳定落在同一桶,扩缩容影响小长周期灰度、A/B 实验
纯随机百分比每次请求随机,比例精确但用户会来回跳流量级实验
定向规则(属性匹配)按用户属性精确命中内测名单、租户白名单
渐进式发布(按时间递增)按计划逐步放量高风险变更的自动放量

本文示例使用"一致性哈希百分比 + 属性定向"组合:先按 email 圈定人群,再在人群内 50/50 稳定分流。

十、生产配置模板

以下模板适用于"4 vCPU / 8 GB 内存 / SSD、单机承载 200 QPS 求值、flags 由 Git 仓库维护"的场景:

server:
  port: 1031
pollingInterval: 60000
enablePollingJitter: true
fileFormat: yaml
logLevel: info
logFormat: json
persistentFlagConfigurationFile: /var/lib/go-feature-flag/flags-snapshot.yaml
startWithRetrieverError: true
retriever:
  kind: github
  repositorySlug: example/feature-flags
  path: flags/prod.yaml
  branch: main
  token: ${GOFF_GITHUB_TOKEN}
authorizedKeys:
  evaluation:
    - ${GOFF_EVAL_KEY}
  admin:
    - ${GOFF_ADMIN_KEY}

逐项说明:

  • enablePollingJitter: true:多实例同时启动时打散拉取时间,避免对 Git 仓库形成尖峰。
  • persistentFlagConfigurationFile:把最近一次成功拉取的配置落盘,配合 startWithRetrieverError 实现"仓库不可用也能按旧配置服务"。
  • retriever.kind: github:开关变更走 PR 评审;token 用于私有仓库。
  • authorizedKeys:对外只暴露求值 Key,管理 Key 仅运维侧持有。
  • 两个 ${...} 为系统级凭据占位,部署时通过 EnvironmentFile= 或环境变量注入,不要提交到仓库。

⚠️ 注意:模板中的 /var/lib/go-feature-flag/ 需提前创建并 chown goff:goff,否则持久化写入会失败。

十一、常见问题

11.1 改了配置文件但行为没变

先确认实际加载的是哪份文件,以及有没有被环境变量覆盖:

systemctl show go-feature-flag -p ExecStart
env | grep -E 'SERVER_PORT|RETRIEVER_PATH|LOGLEVEL|POLLINGINTERVAL'

实测回显(第一条,节选):

ExecStart={ path=/usr/local/bin/go-feature-flag-relay-proxy ; argv[]=/usr/local/bin/go-feature-flag-relay-proxy --config /etc/go-feature-flag/goff.yaml ; ... }

若环境变量存在,它会盖掉文件值(见 3.4 节实验)。清理环境变量或改用 EnvironmentFile 统一管理。

11.2 求值全部返回默认值

按顺序排查:/health 是否 initialized:true → /info 的 cacheRefresh 是否在更新 → flags 文件里开关名是否拼写一致 → 定向规则的 query 字段名是否与上下文一致。

curl -s http://127.0.0.1:1031/health
curl -s http://127.0.0.1:1031/info
curl -s -X POST -H "Content-Type: application/json" -d '{"flags":["new-checkout-flow"]}' \
  http://127.0.0.1:1031/v1/flag/configuration

实测回显:

{"initialized":true}
{"cacheRefresh":"2026-10-27T17:29:04.266149696Z"}
{"flags":{"new-checkout-flow":{"variations":{"disabled":false,"enabled":true},"targeting":[{"query":"companyID eq \"admin\"","variation":"enabled"}],"defaultRule":{"variation":"disabled"}}}}

/v1/flag/configuration 返回的是服务端真实生效的开关定义,用它和本地文件比对即可确认"文件是否真的被加载"。

11.3 百分比灰度的比例看起来不对

百分比分流是按 targetingKey 做稳定哈希,不是每次请求随机。样本量小时比例偏离属于正常现象;要验证分流是否生效,用固定的一批 key 循环求值,观察是否稳定落在同一变体即可(见 9.2 节实测)。

11.4 启动告警 server port is set using port

v1.57.2 的已知误报:只要端口来自内部默认回填就会打印,即使配置只写 server.port。判断是否真的生效请看 Starting ... address,或直接 curl /health。不影响功能,无需处理。

十二、参考链接