更新日志
3.10.6
发布日期:2026-08-25
不兼容变更
控制面
-
自定义插件改为按网关组管理
升级说明自定义插件此前是控制面级资源,一份代码全局共用,因此为预发布环境上传一个新构建会立刻同时生效到该插件绑定的所有网关组。现在它归属于单个网关组,接口、权限以及升级后的现有部署形态都随之变化。
接口从
/api/custom_plugins迁移到/api/gateway_groups/{gateway_group_id}/custom_plugins,旧路径的所有方法都返回HTTP 410,并在消息中给出替代路径。PUT按插件名做「创建或替换」,因此流水线一次调用即可上传,且只写入自己的网关组。请求和响应体中不再有gateway_groups,改为携带gateway_group_id。GET /api/plugins现在必须带gateway_group_id查询参数,因为插件目录已经是按网关组给出的结果。权限随资源一起迁移,从
arn:api7:gateway:gatewaysetting/*变为arn:api7:gateway:gatewaygroup/{gateway_group_id}。gateway:CreateCustomPlugin不再存在——上传是同一个端点上的「创建或替换」,由gateway:UpdateCustomPlugin覆盖——读取自定义插件则需要新增的gateway:GetCustomPlugin权限。此前读取不需要任何权限,任何已登录用户都能读到任意自定义插件的源码;升级后,从未被授予自定义插件权限的角色无法列出或读取它们,需要额外授予gateway:GetCustomPlugin。升级程序会改写已存储的策略:原本在旧资源上授予自定义插件动作的语句保留其余动作,自定义插件相关动作迁移到arn:api7:gateway:gatewaygroup/*上的新语句,效果与条件不变,其中gateway:CreateCustomPlugin被改写,并补上gateway:GetCustomPlugin。被替换的策略文档保存在permission_policy_backup中。同一个升级程序会把每个已有插件展开成它曾部署到的每个网关组各一条记录,因此插件在升级后无需任何操作即可继续工作。升级后有两种情况需要留意:未部署到任何网关组的插件没有归属,会被记录到日志而不迁移,需要手动上传到需要它的网关组;此前路由可以引用一个从未部署到其所在网关组的插件,控制面接受该配置而数据面忽略它,因此插件从未真正运行过,升级后该网关组没有这个插件,下一次写入该路由时会报告插件未知,直到移除这段失效的插件配置。
最后请注意,在 3.10.6 上上传的插件代码只存在于新表中,因此回滚到更早的版本会使用升级前的旧表中保存的代码。
升级须知
$http_x_forwarded_* 现在记录的是客户端发送的值对于来自不受信任对端的请求,网关一直会覆盖 X-Forwarded-Proto、X-Forwarded-Host 和 X-Forwarded-Port 并清除 Forwarded。这部分工作现在由 NGINX 配置完成,不再由 Lua 执行。上游收到的内容没有变化,插件看到的也没有变化:core.request.header 与 ctx.var.http_x_forwarded_* 返回的仍是覆盖后的值,ctx.var.original_x_forwarded_* 返回的仍是客户端发送的值。
不同的是 NGINX 配置层。在访问日志格式、if 或 map 中引用 $http_x_forwarded_proto、$http_x_forwarded_host、$http_x_forwarded_port 或 $http_forwarded 时,现在读到的是客户端发送的值,而此前读到的是覆盖后的值。因此,用 $http_x_forwarded_host 记录审计日志的格式会开始记录由客户端控制的值:伪造 X-Forwarded-Host: evil.example.com 的请求在升级前记录的是真实 host,升级后记录的是 evil.example.com。
如需记录覆盖后的值,请改用 $scheme、$var_x_forwarded_host 和 $var_x_forwarded_port。$http_x_forwarded_for 不受影响。客户端发送的值也可以通过新增的 $original_x_forwarded_proto、$original_x_forwarded_host、$original_x_forwarded_port、$original_x_forwarded_for 和 $original_forwarded 这几个 NGINX 变量读取。
除非在插件元数据中设置了 max_pending_entries,所有日志插件背后的批处理器都会把未投递的条目无上限地保留在 worker 内存中,因此日志服务器变慢或不可达时,worker 内存会随请求速率一起增长。该上限现在默认为 8192 条,同时 datadog、lago、loggly、sls-logger 以及 stream 子系统的 syslog 首次暴露该配置项。
日志服务器能正常消费的部署不受影响:待处理量取决于 batch_max_size 而不是请求速率,通常远低于一千条。达到上限后,新条目会被丢弃,并以每秒至多一条日志的频率报告累计丢弃数。如果你记录了大于几 KB 的请求体或响应体,请在插件元数据中调低 max_pending_entries,使待处理量符合你的内存预算;如果调高过 batch_max_size,也应同步调高该值。
OpenID Connect 上可配置的三项校验此前并非在所有情况下都生效,导致本应被拒绝的请求被放行。现在这些配置会被真正执行,此前能通过的流量可能开始收到 HTTP 401 或 403。
required_scopes 此前只作用于通过内省校验的令牌。使用授权码流程的路由(即未设置 bearer_only 的默认形态)从不读取会话被授予的 scope,因此任何完成登录的用户都会被放行。现在 scope 会从 access token 中读取,无法确定所授予 scope 的会话会被拒绝而不是放行。在授权码流程中依赖 required_scopes 之前,请确认你的身份提供方签发的 access token 是携带 scope 的 JWT,或者 ID token 中携带该字段。
claim_validator.audience.match_with_client_id 此前只在 aud 声明存在时才与 client id 比对,因此不带 aud 的令牌会跳过该校验。现在该选项隐含要求该声明必须存在,不带 aud 的令牌会收到 HTTP 403。
在 bearer_only 模式下且未配置 valid_issuers 时,受信任的 issuer 来自提供方的 discovery 文档;此前该请求失败时网关只记录一条 warning,并在完全没有 issuer 约束的情况下校验令牌。现在会返回 HTTP 401,并携带 error_description="issuer validation unavailable"。如果不希望请求路径依赖 discovery 端点的可用性,请显式配置 valid_issuers。
AI Proxy Multi 此 前接受两个 name 相同的 instance,这会让请求实际路由到哪个 instance 变得不确定。此类配置现在在写入时就以 HTTP 400 拒绝。
已经存储的配置仍可继续工作,但升级后会在数据面的配置兼容性报告中以 error 级别出现。请重命名重复的 instance 使每个名字唯一,配置被重新写入后该报告项即消失。
LDAP Auth 此前通过普通字符串拼接重建一个可分辨名称(DN)来查找消费者,而只要用户名中含有在 DN 中具有结构含义的字符(, + = < > ; " \),这个 DN 就与实际用于绑定的 DN 不同。现在改为使用绑定时生成的转义 DN。
目录中所有用户名都不含上述字符的部署不受影响。如果某个消费者的 user_dn 是按旧行为以未转义形式写入的,请改写为 RFC 4514 转义形式(例如 cn=comma\,user,ou=users,dc=example,dc=org),否则该消费者在升级后不再匹配。
为修复下文「缺陷修复」中描述的变体键碰撞问题,Proxy Cache 在 memory 策略下的存储键布局发生了变化,缓存版本号也随之提升。因此旧版本写入的条目在新布局下不可达:每个键的第一次请求为 MISS 并重新填充缓存,而对只有升级前条目的 URL 执行 PURGE 会返回 HTTP 404。旧条目会一直留在共享内存中,直到各自的 TTL 到期。使用默认 disk 策略的部署不受影响。
新功能
插件
- GraphQL Limit Count
- 新增按查询成本限流的能力,使一个 GraphQL 请求按其请求的工作量消耗配额,而不是一律计为一次请求。
cost_strategy选择成本的计算方式——depth按选择集的嵌套深度计算,complexity按查询解析的节点数计算,node_quantifier按参数要求每个字段返回的对象数 量计算——score_factor对结果进行缩放。设置max_cost后,超过该成本的查询在到达上游之前即以HTTP 403拒绝,计算出的成本通过X-Graphql-Query-Cost响应头返回。当服务上配置了成本装饰时,需要内省上游 schema 才能与之匹配,该内省按服务执行一次,端点由introspection_endpoint指定,introspection_headers携带该端点所需的凭据。启用resolve_variables后,通过 GraphQL 变量传入的参数会参与成本计算,而不是被当作未提供。
- 新增按查询成本限流的能力,使一个 GraphQL 请求按其请求的工作量消耗配额,而不是一律计为一次请求。
- Prometheus
- 新增四个四层流量指标:按 stream 路由统计的
apisix_stream_connection_total,按终止状态、监听地址和上游节点统计已结束会话的apisix_stream_status,按监听地址反映当前 TCP 连接与 UDP 会话数的apisix_stream_active_connections,以及按监听地址、方向和连接侧统计代理字节数的apisix_stream_bandwidth。采集这些指标需要在 stream 路由上启用该插件。插件元数据的disabled_labels现在支持stream_status键,用于关闭apisix_stream_status上的标签,与status对 HTTP 计数器的作用相同。
- 新增四个四层流量指标:按 stream 路由统计的
- AI Proxy Multi
- 新增
fallback_http_statuses,用于列出哪些上游 HTTP 状态码会让网关把请求重试到下一个 instance。此前只有在完全无法连接某个 instance 时才会回退,因此提供方因密钥过期返回401、或因该账号配额耗尽返回429时,这些响应会直接返回给客户端,而不会转由其他 instance 重试。
- 新增
- LDAP Auth 与 LDAP Auth Advanced
- 两个插件都新增
hide_credentials,在客户端通过认证后移除Authorization请求头,使目录凭据不会被转发到上游。默认值为false,与此前行为一致。
- 两个插件都新增
控制面
- 自定义插件现在归属于单个网关组而不是整个部署,因此同一个插件名可以在预发布和生产环境运行不同的代码,向一个环境上传也不会影响其他环境。接口、权限与迁移细节参见「不兼容变更」。
- GraphQL 查询成本现在可以通过服务上新增的
graphql_cost_decorations子资源按服务调整。一条装饰指定一个字段路径(如Query.products或Product)并调整该字段对成本的贡献:既可以是固定的add_value,也可以按mul_arguments中列出的参数值对其子节点做乘法——例如返回first: 10个对象的字段按十倍计价。装饰会针对其所属服务做校验,同一个字段路径只能被装饰一次,且改动无需触碰承载该插件的路由即可下发到数据面。 - 路由现在可以匹配
PURGE方法,Proxy Cache 和 GraphQL Proxy Cache 正是用它来失效缓存条目的。网关一直接受该方法,只有控制台自身的 schema 拒绝它,因此此前无法通过接口或控制台创建用于缓存失效的路 由。 - 上游与路由的超时现在接受小数秒,可以配置
0.5,而此前接口要求整数。数据面一直支持亚秒级超时。最小值为0.001,因为低于一毫秒的部分会被截断。
数据面
- 请求携带的
X-Forwarded-*请求头改由 NGINX 配置消毒,不再由 Lua 在每个请求上处理。插件看到的值与此前一致,而客户端发送的原始值——在 Lua 中一直可以通过ctx.var.original_x_forwarded_*读取——现在也成为 NGINX 变量,可以写进日志格式。唯一发生变化的记录位置参见「升级须知」。
控制台(Dashboard)
- 新增繁体中文(台湾)与越南语。中文的语言标签现在明确区分是哪一种中文——
zh-Hans-CN与zh-Hant-TW——因此缺失的繁体文案会明显地回退到英文,而不是静默显示简体文案。此前已选择中文的用户,语言设置保持不变。 - License 页面现在把部署 ID(Deployment ID)作为许可证表格的第一行展示,并带复制按钮。部署 ID 是申请签发或续期许可证时需要提供给 API7 的信息,此前只出现在「更新 License」对话框中,而该对话框受更新许可证权限限制,没有该权限的用户根本无法读到它。即使尚未激活许可证,该行也会显示。