深色模式
第五章 数据网关与端到端:把能力安全地开放出去
一个团队在平台里辛辛苦苦搭好了一条信贷打分流:数仓每天把业务数据加工成宽表,评分卡模型算出通过概率,规则集给出准入分和决策。可这条能力现在被关在平台内部——业务系统的同事没有平台账号,也不该拿到平台账号;他们只想发一个 HTTP 请求,把申请人的几个特征传进来,拿回一句"通过还是拒绝"。
数据网关就是解决这"最后一公里"的模块。通俗来讲,它做的事只有一件:把平台内部的一条能力(一个服务、一条打分流),包装成一个对外的 API 端点,并在这个端点前面架起一道岗——谁能进、带什么钥匙进、进来能访问哪条接口、超时/限流/加解密怎么管,全在网关上定义。下游因此不需要懂内部的模型编排,只需要一次带身份的 HTTP 调用。
有点像小区门口的门禁与快递柜:住户(内部能力)不必和每个访客直接打交道,访客(下游消费方)只要在门口刷一张被登记过的卡、把包裹投进指定格口,就能完成交付;谁的卡有效、能开哪个格口,全由门岗这一处说了算。
本章按"把一条能力安全开放出去"的真实作业顺序推进:先管住"谁能调"(应用与 apiKey),再管住"转发到哪"(上游服务),然后管住"暴露什么"(路由),接着发布生效、把授权收在资源上,最后端到端跑通一笔信贷评分并验收。读完这一章,能从零把第一条对外 API 配起来、发出去、调通、并挡住越权。
说明: 本章示例中的具体资源 ID、密钥字面量、端口取自开发环境实测,仅用于说明配置项之间的对应关系;实际部署的地址、端口与密钥以运维《运行说明》为准。
5.1 先认清三个"网关":控制面与数据面分开
场景切入。新手接手网关的第一天,最容易在同一个词上栽跟头——系统里有三个都叫"网关"的角色,端口不同、职责不同,拿错端口去调,轻则报错、重则把内部后台暴露出去。所以动手之前,先把这三者分清。
是什么。三个"网关"各管一段:
- 平台网关: 内部统一入口,按前缀把请求路由到各内部服务(去掉一段前缀后转发)。后台前端、内部联调走它。开发环境端口
38080。 - 网关后台(gateway-admin): 管理端,用来配置路由、上游、应用、插件、密钥的后台。它是控制面——只负责"存配置"和"发布"。开发环境端口
39301。 - 对外消费网关(api-gateway): 对外的业务调用入口,只承载被显式发布的业务路由。它是数据面——只认发布好的快照,不读管理态的关系表。下游消费方永远只调它。开发环境端口
39302。
为什么要拆成三个。核心是控制面与数据面解耦。通俗来讲,控制面像"排班表的编辑室",数据面像"照排班表干活的车间":管理端改配置不会即时影响线上,只有"发布"这个动作才把配置推到数据面;而对外网关绝不透传任何内部服务前缀(/system、/data-management、/model 等),避免下游用外网入口打到内部管理接口。两者一分开,改配置的人和被调用的线上互不惊扰,出了问题也好定位——是"没发布"还是"发错了",一眼能分。
重要提示: 三个端口别混。
38080(平台网关)走前缀转发到内部服务,例如/model/...会打到模型运行时;39302(对外消费网关)只认已发布的业务路由;消费方接入只给39302。若拿38080去调对外路由,或拿39302去调内部接口,都会得到"驴唇不对马嘴"的结果。
产品功能入口:打开侧边导航栏,展开数据网关目录。这个目录本身不是页面,只承载下面五个子菜单;一条对外 API 从建到发,正好按这个顺序走一遍:
| 菜单 | 这个页面解决什么问题 |
|---|---|
| 客户端管理 | 登记「谁能调网关」。一条记录 = 一个 AppID + 若干条访问鉴权规则(见 5.2)。 |
| 上游服务管理 | 登记「转发到哪」。一条上游 = 协议 + 负载算法 + 一组目标节点(见 5.3)。 |
| 路由管理 | 登记「对外暴露什么」。一条路由 = 匹配条件 + 转发配置 + 可选的参数契约与治理模版(见 5.4)。 |
| 插件模版 | 把限流、IP 名单、加解密、熔断、访问日志组合成可复用的治理模版,再由路由绑定(见 5.11.2)。 |
| 密钥管理 | 集中管理加解密插件引用的密钥(见 5.11.3)。 |
本章的绝大部分配置都在这五个页面里完成。
说明: 「客户端管理」页面里的每一条记录,本章统一称作应用(App)——两者是同一个东西,后台表与权限标识用的都是
app。
5.2 第一步:把"谁能调"管起来——应用与 apiKey
客户端管理页面解决什么问题:登记「谁能调网关」。运行时网关拿请求里带的 AppID 找到一条已发布的客户端记录,再用这条记录上的鉴权规则校验凭据,最后看这条客户端有没有被授权访问当前这条路由——三关都过才转发。三关缺一,分别对应 5.7 里的不同 401 / 403 文案。
5.2.1 注册应用、发放 apiKey
场景切入。信贷业务方和股票业务方都想调平台的打分接口。不能给他们一把"万能钥匙",否则一把泄露就全线沦陷。正确做法是:每来一个消费方,先在网关里给它建一个独立的调用身份。
是什么。应用(App): 为每一个下游消费方建立的一个对外调用身份主体。通俗来讲,它就是一把"以消费方为单位"的门禁卡——谁来调用,就先给谁建一个应用,apiKey 挂在这个应用上,按消费方独立发放、重置、停用,而不是全平台共用一把钥匙。本章案例有两个消费方应用:代表信贷消费方的「风控消费方App」(appId=RISK-CONSUMER-001),和代表股票消费方的「股票消费方App」(appId=STOCK-CONSUMER-001)。
为什么。把"谁能调用"抽象成可管理的对象,好处是隔离与可控:某个消费方的 key 泄露了,只重置它这一把,不影响别家;某个消费方要下线,只停用它这一个应用即可。举个例子——股票业务方的 key 被误提交到了外部代码仓库,只需给「股票消费方App」重置 key、重新发布,信贷业务方完全无感,不必全平台换钥匙。
怎么做。
涉及该操作的功能权限:应用的新建(
gateway:app:add)、编辑(gateway:app:edit)、发布(gateway:app:publish);取明文 key 需查询权限(gateway:app:query);列表与删除对应gateway:app:list、gateway:app:remove。
- 打开侧边导航栏数据网关 → 客户端管理,点击列表左上角新建应用按钮,展示编辑页面。
- 填写应用基本信息(见下表),点击保存。此时应用处于**草稿(draft)**态。
- 进入应用编辑页,在访问鉴权子表中至少添加一条 API Key 类型的规则(配置方式见 5.2.2)。
- 点击列表操作列的发布(changeStatus),把应用状态从草稿推为已发布(published)。
- 取明文 key:在应用详情页点击查看密钥,系统调用取明文接口
GET /gateway-admin/app/{id}/sensitive返回未打码的 apiKey。普通详情接口GET /gateway-admin/app/{id}只回打码值(API Key 只露首 4 尾 4,Basic 的密码一律显示******)。 - 换 key:在详情页对某条 API Key 规则点重置,系统重新生成一把并让旧 key 立即失效。
应用字段:
| 参数名称 | 描述 |
|---|---|
| appName | 应用名称,便于在列表里辨认消费方。必填。 |
| appId | 对外身份标识。1. 新建时由系统按 DP-CLIENT-<时间戳36进制> 自动生成,保存后不可再改;2. 下游调用时要在请求头 X-APP-ID 里带的正是这个值(如 RISK-CONSUMER-001);3. 它与数据库主键 id 是两回事——头里带的是 appId,不是那串数字 id。 |
| description | 描述,记录消费方归属、用途,便于后续审计"这把 key 是发给谁的"。 |
| status | 状态:draft(草稿)/ published(已发布)/ offline(已下线)。三个取值的完整含义见 5.5,只有 published 才进运行时。 |
| customAttributes | 自定义属性(KV),按需给应用挂业务标签(如所属业务线、责任人)。纯标注,不参与运行时判定。 |
| extraParams | 额外参数规则(参数位置 + 生效规则)。当前未实现,见下方重要提示。 |
| authRules[] | 访问鉴权规则列表,一个应用可挂多条,见 5.2.2。 |
- 验证:发布成功后,用取到的明文 key 和 appId 发一次带身份的调用(见 5.10),能返回业务结果即说明应用已生效;若返回 401「应用不存在或未发布」,回到第 4 步确认应用确实已从草稿推为已发布。
重要提示: 「额外参数」当前配了等于没配。界面帮助文案写的是"用于在请求转发前补充固定参数,可插入 Header、Query、Body",但数据面的客户端模型里根本没有这个字段,快照下发时被直接丢弃,运行时没有任何一处读它——三个生效规则(原参数存在时覆盖 / 原参数不存在时新增 / 原参数存在时移除)一个都不生效,也不会报错。要给上游补固定请求头,请改用路由编辑页第 3 步的请求头处理(见 5.4.2)。
注意:
- 只有已发布的应用才会进入运行时快照。草稿态应用去调用,会被判为"应用不存在或未发布"并返回 401。
- 明文 apiKey 只在
/{id}/sensitive接口返回,列表与详情页永远打码——不要在列表里找 key。- 重置 API Key(接口
PUT /app/{id}/authRule/{ruleId}/resetApiKey)之后,旧 key 立即失效。客户端若处于已发布态,系统会自动重新发布刷新快照;若处于草稿或已下线态,要等下次发布才进运行时。- 删除应用是软删,记录仍在库里,只是列表不再展示。
5.2.2 应用鉴权规则:两种类型、三个取值位置、有效期
场景切入。同一个消费方常常不是"一把 key 用到底":灰度期新老 key 要并存,一部分老客户端习惯把 key 放在请求头、另一部分放在查询参数。硬要求它们同一时刻只用一种方式,切换就会中断业务。鉴权规则的多条 OR + 有效期,正是为平滑切换设计的。
是什么。一个应用可以配置多条鉴权规则(authRules),运行时任意一条命中即通过,是"或(OR)"的关系。规则分两类:apiKey 和 Basic;凭证可以从三个位置取——请求头(Header)、查询参数(Query)、请求体(Body);还能设置过期时间。
用一个成对反义的骨架帮助建立心智模型:apiKey / Basic 是两种"钥匙形态",Header / Query / Body 是三个"钥匙插孔",有过期 / 不过期是"钥匙有没有到期日"。三组维度自由组合,拼出一条具体的鉴权规则。
为什么要支持多条 OR + 有效期。多规则取并集,让 key 轮换和到期下线不打断业务——先加新规则、放一段时间、再让老规则过期,平滑切换。举个例子:要把「风控消费方App」的 key 从旧值换到新值,先加一条新 key 规则并给旧规则设 7 天后的 expiresAt,7 天里新旧都能调,到期后旧规则自动失活,期间业务一秒不停。
怎么做。
涉及该操作的功能权限:应用的编辑(
gateway:app:edit);改完须重新发布(gateway:app:publish)刷新快照。
在应用编辑页的访问鉴权子表逐条添加。一条规则由"鉴权名称 + 鉴权类型 + 参数位置 + 参数位置名称 + 凭据值 + 过期时间"六段组成:
鉴权类型(type)逐取值:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| API Key | 网关按"参数位置 + 参数位置名称"取到一个值(默认 Header 的 X-API-Key),与库里存的 key 做全等比对。key 由系统用安全随机生成 32 位大小写字母 + 数字,界面上不可手填。新接入的调用方默认用这种,是最省事、也最不容易配错的一档。 |
| Basic | 网关按"参数位置 + 参数位置名称"取值(默认 Header 的 Authorization)。取到的值以 Basic 开头时,按 Base64 解出"用户名:密码"再与配置比对;不以 Basic 开头时,直接和明文 用户名:密码、或和整串 Basic base64(用户名:密码) 比对,任一相等即通过。用在只会发标准 Basic 头的老系统上;自测时也可以直接发明文 用户名:密码,省去手工做 Base64。 |
| (其它任何值) | 运行时对类型做归一化——去掉空格 / 短横 / 下划线后转小写,只认 basic 和 apikey,所以 API_KEY、Api Key 都识别成 API Key,不必纠结大小写与分隔符。归一后仍不是这两个的,一律判为不匹配,表现为这条规则永远校验不过。界面上只能选这两种,只有绕过界面直连接口写入才会出现这种数据。 |
参数位置(position)逐取值——决定网关到请求的哪一处去取这把钥匙:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| Header | 从请求头里按"参数位置名称"取值。默认值,也是标准做法,优先选它。API Key 默认名 X-API-Key,Basic 默认名 Authorization。 |
| Query | 从 URL 查询参数里按名称取值。只在调用方受限、凭据只能挂在 URL 上时才用。后果: 凭据会原样出现在访问日志的查询串列以及沿途各级代理的日志里——访问日志只对请求头和请求体里的敏感字段名做掩码,查询串是原样落库的。 |
| Body | 网关会先把请求体缓存下来再取值:Content-Type 是表单时按表单字段取,是 JSON(或没带 Content-Type)时按顶层 JSON 字段取,其余按表单解析。用在凭据只能放报文体里的老协议上。后果: 多一次读取并缓存请求体的开销;且只支持顶层字段,嵌套路径取不到,取到的值若是对象 / 数组会被序列化成 JSON 字符串再比对。 |
其余字段:
| 参数名称 | 描述 |
|---|---|
| 鉴权名称(name) | 这条规则的名字,必填——留空保存会被拒(前端提示「鉴权名称不能为空」,后端同样拦一道)。新增规则时系统按类型自动生成 Basic鉴权1、API Key鉴权1 这类默认名,可以直接沿用;一个应用挂多条规则时,改成能看出用途的名字(如「灰度新key」「老客户端Basic」)更好辨认。它只是标注,不参与运行时判定。 |
| 参数位置名称(headerName) | 到"参数位置"里按这个名字取值。不填时,API Key 默认取 X-API-Key,Basic 默认取 Authorization。 |
| apiKeyValue | API Key 类型的密钥值,系统生成,不可手填。 |
| username / password | Basic 类型的账号口令。密码在列表 / 详情里回显成 ******;保存时若密码仍是 ****** 或留空,后端会保留库里的原密码,不会把密码清空。 |
| expiresAt | 过期时间,格式 yyyy-MM-dd HH:mm:ss。留空表示永不过期;填了就按这个格式解析,当前时间早于它才算有效。 |
选项 + 后果:多条规则是或的关系,任一条匹配即通过;只要还有一条没过期,就按"有活跃规则"处理。如果一个应用的所有规则都过期了,调用会返回「应用鉴权已过期」(401),而不是普通的「应用鉴权失败」——文案本身就在提示原因是"过期"而非"key 错",下游一看便知该续期了。
验证:加完一条新规则并重新发布应用后,用新规则约定的位置与 key 发一次调用能通过,即说明该规则已进入运行时;把老规则的 expiresAt 设成已过去的时间并重新发布,再用老 key 调用返回「应用鉴权已过期」,即说明到期下线也生效。
重要提示: 一条鉴权规则都不加也能保存并发布。这种客户端在运行时仍要求请求带上正确的 AppID、客户端已发布、且已被授权访问这条路由,但不再校验任何凭据,直接放行——等价于"只凭 AppID 就能调用"。AppID 通常不是秘密(会出现在日志、配置、对接文档里),对外接口不要这么配;只有在内网可信调用方、凭据由前置系统统一管控时才考虑。
注意:
- API Key 只在新建规则时自动生成,编辑时不会重新生成(保留原值)。要换 key 只能用详情页的重置。
- 把一条规则的类型从 Basic 切成 API Key 时,系统会清空原来的用户名 / 密码。
expiresAt若填成非法格式(解析失败),系统会当作"永不过期"并记一条告警,不会拦截。因此配置到期下线时,务必确认时间格式为yyyy-MM-dd HH:mm:ss,否则以为设了到期、实际根本没生效。- HTTP 头名大小写不敏感,但头的值区分大小写——key 抄错一个大小写就会判鉴权失败。
5.3 第二步:把"转发到哪"管起来——上游服务
场景切入。有了调用身份,还得告诉网关"这条 API 最终打到哪台机器"。如果把目标地址直接写死在每条路由里,机器一搬家就要逐条改路由。上游服务就是为了把"目标地址"抽出来单独管。
上游服务管理页面解决什么问题:登记「转发到哪」。一条上游 = 请求协议 + 负载算法 + 一组目标节点(host:port:weight)+ Host 处理方式 + 超时。路由必须绑一个上游,上游必须先发布,引用它的路由才发得出去。
是什么。上游服务(Upstream): 路由转发的目标服务。通俗来讲,它回答的是"这条 API 最终打到哪里"这个问题。上游是一组静态节点(host:port:weight 列表),支持负载均衡、协议选择、Host 透传或重写、超时设置。本章对外打分路由复用的是「打分flow-run上游」(id 2078815446612643840,指向工作流服务)。
为什么。把"转发到哪"和"路由规则"解耦。多条路由可以共用一个上游;上游节点变了,只改这一处,所有引用它的路由自动跟着变,不用逐条改路由。举个例子——信贷打分流和股票打分流可以共用同一个"工作流服务上游",哪天工作流服务换了机器,只改这一处的节点,两条对外路由同时生效。
怎么做。
涉及该操作的功能权限:上游服务的新建(
gateway:upstream:add)、编辑(gateway:upstream:edit)、发布(gateway:upstream:publish);取选项列表GET /upstream/options对应列表权限(gateway:upstream:list)。
- 打开数据网关 → 上游服务管理,点击左上角新建按钮,展示编辑页面。
- 填写服务信息与节点(见下表)。
- 点击保存,再点操作列发布(changeStatus),把上游推为已发布。
| 参数名称 | 描述 |
|---|---|
| serviceName / serviceCode | 上游名称与服务 ID。服务 ID 全局唯一,是路由引用它的凭据。 |
| protocol | 请求协议:HTTP / HTTPS,逐取值见下。 |
| balanceType | 负载算法:轮询 / 随机,逐取值见下。 |
| nodes[] | 目标节点列表,每个节点填 host、port、weight。至少保留一个有效节点;端口范围 1~65535;权重必须大于 0,否则保存被拒。 |
| preserveHost / rewriteHost | 转发 Host 的处理方式,逐取值见下。 |
| timeoutMs | 请求超时(毫秒),当前默认不单独生效,见下方重要提示。 |
| status / description | 发布状态(draft / published / offline,含义见 5.5)与描述。 |
请求协议(protocol)逐取值——只约束"网关到上游"这一段:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| HTTP | 网关以 http:// 拼接目标地址转发到节点。默认值(留空也回落成 HTTP)。内网上游选它,绝大多数情况都是这个。 |
| HTTPS | 网关以 https:// 拼接目标地址转发到节点。上游只提供 TLS 接入时用。后果: 节点端口要填 TLS 端口(通常 443);网关侧没有提供自签证书信任的配置入口,上游若用自签证书会直接握手失败。 |
说明: 上游协议只有这两个取值。路由编辑页里的访问协议(含 WebSocket)是另一回事——那个约束的是"客户端到网关"这一段,见 5.4.2。
负载算法(balanceType)逐取值:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| 轮询 | 默认值。实际是按权重的轮询:把各节点权重求和,用一个按路由维护的自增计数器对总权重取模,落在哪个节点的权重区间就选谁;所有节点权重都非正时退化成等权轮询。节点性能不一致时靠权重分配比例,性能一致就把权重填成一样的。后果: 计数器是单个网关实例进程内的,多实例各自计数,整体分布只在统计意义上接近设定比例。 |
| 随机 | 在节点列表里等概率随机选一个,完全忽略权重。节点同构、只想要最简单的打散时用。 |
注意: 选了随机,界面上填的节点权重就不起任何作用了,而界面上不会有任何提示。想让权重生效必须用轮询。
转发 Host 的两种处理方式:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| 透传(preserveHost 开) | 把客户端原始的 Host 头原样带给上游。默认值。上游按 Host 做虚拟主机路由、需要看到对外域名时用。后果: 打开后「重写域名」输入框被禁用。 |
| 重写(preserveHost 关) | 把请求的 Host 头改写成填写的「重写域名」再转发。上游是按内网域名做虚拟主机匹配时用。后果: 关掉透传后「重写域名」变成必填,留空保存会被拒。 |
- 验证:上游发布后,在其被引用的路由上发一次调用,能正常打到目标服务即说明配置正确;若报 502/504 或空响应,回到本表检查协议、端口、Host 策略。
注意: 转发到外部公网域名时,不要透传 Host(
preserveHost)。因为客户端来的 Host 往往是网关自己的地址,透传过去会让对方站点找不到正确的虚拟主机——典型表现是空响应、301/302 重定向到错误站点。正确做法是设rewriteHost为真实域名。
重要提示: 关于超时,上游字段
timeout_ms的建表默认值是 3000ms,但默认不生效——动态路由目前统一吃全局 10 秒超时(路由级超时开关默认关闭)。不要以为改了这个字段就改了超时。若上游协议/端口/Host 配错,典型报 502/504 或空响应。
注意:
- 路由发布了,但它引用的上游没发布,运行时会直接跳过这条路由——表现为 404(像是"路由不存在")。排查 404 时,先确认上游也已发布。
- 把一个上游取消发布,会连带把挂在它上面的已发布路由打成不可用(快照里找不到上游的路由会被跳过),表现同样是 404。停一个上游前先看清有几条路由在用它。
- 上游被任何一条路由引用时不允许删除,系统会直接拒绝。要删先把引用它的路由改到别的上游或删掉。
5.4 第三步:把"暴露什么"管起来——路由
路由管理页面解决什么问题:登记「对外暴露什么」。左侧是纯管理维度的分组树,右侧是路由列表。一条路由 = 匹配条件(协议 + 方法 + 路径 + 高级匹配)+ 转发配置(上游 + 转发方式 + 超时 + 重试 + 请求头处理)+ 可选的 API 参数契约 + 可选的治理模版绑定。新建走一个五步向导:基础信息 → 匹配规则 → 转发配置 → API 参数(仅 API 路由)→ 绑定插件模版。
5.4.1 路由分组:纯管理维度的目录树
场景切入。路由一多,列表就成了大杂烩。分组树给后台一个可折叠的抽屉,把"信贷类""股票类"路由各归各处,找起来快。但要先说清:它只是抽屉标签,不参与线上放行。
是什么。路由分组: 把路由按业务归类到一棵分组树里,便于组织和检索。它是纯管理维度,只影响后台的展示与查找,不参与运行时匹配、不参与鉴权、不做隔离。
怎么做。
涉及该操作的功能权限:路由分组的新建(
gateway:routeGroup:add)、编辑(gateway:routeGroup:edit)、删除(gateway:routeGroup:remove)、列表(gateway:routeGroup:list)。
- 打开数据网关 → 路由管理,左侧是分组树(数据来自分组树接口
GET /route/group/tree)。 - 在分组树上维护分组,支持四个动作:
| 动作 | 说明 |
|---|---|
| 新增根分组 | 在树的顶层建一个分组,只需填名称与排序号。 |
| 新增子分组 | 在选中的分组下建下级分组,层级不限。 |
| 编辑分组 | 改名称、改排序号。 |
| 删除分组 | 该分组有子分组、或分组下还挂着路由时,系统直接拒绝删除。先把下级和路由挪走再删。 |
- 建路由时,在路由编辑页第 1 步选择所属分组。
- 查路由时,点分组树上的任一节点,右侧列表按该分组及其所有子孙分组过滤。
验证:新建一个分组后刷新分组树,能在指定父节点下看到它;把一条路由的分组指到它并保存,分组下即出现这条路由。
注意: 分组不参与运行时匹配、不参与鉴权、不做隔离,不要指望用分组做接口隔离——隔离靠的是"绑定授权应用"(见 5.6),不是分组。
5.4.2 路由注册:一条对外 API 的完整定义
场景切入。前面把"谁能调""转发到哪"都备齐了,现在到了主角:定义一条真正对外的 API——/score/credit,让下游 POST 几个特征进来、拿回一句决策。
是什么。路由(Route): 对外暴露的一条 API 定义,是把内部服务或打分流安全暴露成外部端点的核心单元。一条路由说清楚:走哪个上游、什么路径、什么方法、要不要鉴权、路径怎么重写、请求头注入什么、匹配规则是什么。所有的转发行为都在这里定义。本章对外路由是 /score/credit(id 2080160876843847680)。
路由有两种类别(category),这是最需要先分清的开关,用一对反义概念记牢:
- gateway(纯转发): 只做转发,允许通配符、允许保留/去前缀,不校验参数。像"直通管道",进什么转什么。
- api(带元数据): 必须配 API 参数(apiConfig,见 5.4.3),运行时会校验请求参数与响应结构;不允许通配符
*/**,且 WebSocket 不能走 api 路由。像"带安检的闸机",不合规的入参当场拦下。
为什么。路由是整个对外开放的落点——把"内部怎么算"藏在网关后面,对外只留一个干净的路径与方法。下游因此零学习成本:它看到的只是 POST /score/credit,看不到背后是模型还是规则,也改不动内部编排。
怎么做。
涉及该操作的功能权限:路由的新建(
gateway:route:add)、编辑(gateway:route:edit)、发布(gateway:route:publish)。
- 打开数据网关 → 路由管理,点击左上角新建按钮,进入五步向导。
- 第 1 步 基础信息:填路由名称,选所属分组、路由类型、访问协议、请求方法、对外路径。
- 第 2 步 匹配规则:需要按请求头 / 查询参数再细分时,逐条加高级匹配规则(见下)。
- 第 3 步 转发配置:选上游、转发方式、超时、重试,以及请求头处理(对外打分路由必须在这里注入网关凭证,见下方重要提示)。
- 第 4 步 API 参数:只有 API 路由才有这一步,见 5.4.3。
- 第 5 步 绑定插件模版:需要限流 / 黑白名单 / 加解密等治理动作时绑一个模版,见 5.11.2。
- 点击保存,再回列表点操作列的发布(changeStatus → published)。
路由主体字段:
| 参数名称 | 描述 |
|---|---|
| routeName | 路由名称,列表里靠它辨认。 |
| category | 路由类型:gateway(网关路由)/ api(API 路由),逐取值见下。 |
| protocol | 访问协议(客户端到网关这一段):HTTP / HTTPS / WebSocket,逐取值见下。 |
| method | 请求方法:ALL / GET / POST / PUT / DELETE / PATCH / HEAD,逐取值见下。 |
| path | 对外路径(如 /score/credit)。 |
| needAuth | 是否走应用鉴权。1. 为 true 时,调用必须带合法身份并被授权;2. 为 false 时是公开路由,完全跳过鉴权(见 5.7)。 |
| upstreamId | 转发目标上游。必须是已发布的上游,否则路由发不出去。 |
| rewriteMode + rewriteValue | 转发方式(路径改写)与重写值,逐取值见下。 |
| timeoutMs / retryCount | 请求超时与重试次数,注意"填了不一定生效",见下。 |
| matchRules[] | 高级匹配规则:来源、参数名、操作符、匹配值,用于同路径多条件分流,逐取值见下。 |
| headerRules[] | 请求头处理:转发前设置固定请求头,见下。 |
| apiConfig | API 路由的参数契约,见 5.4.3。 |
| pluginTemplateId | 绑定的治理模版,见 5.11.2。 |
路由类型(category)逐取值——这是最需要先分清的开关:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| gateway(网关路由) | 只做匹配和转发,不校验报文,请求体和响应体原样透传。路径允许 * 和 ** 通配。整段路径批量代理到某个上游时用(例如 /report/**),也用于不需要网关代为把关参数的内部接口。后果: 网关路由不消费 API 参数配置,即使库里存了参数契约也不生效。 |
| api(API 路由) | 转发前按参数契约校验 Header / Query / Path / Body(缺必填参数返回 400、类型不符 400、Content-Type 不符 415),响应回写前按"响应期望"校验响应体并执行字段后处理。对外正式发布的单个接口,需要网关代为挡住畸形请求、并对响应字段做脱敏 / 格式化时用。后果: 1. 路径不允许 * 或 **,新建和发布两处都会拦;2. 访问协议选 WebSocket 时直接拒绝保存;3. 响应校验是白名单式的,详见 5.4.3 里关于状态码的重要提示。 |
访问协议(protocol)逐取值——它只约束"客户端到网关"这一段,网关到上游用的是上游服务自己的协议:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| HTTP | 请求的 Forwarded / X-Forwarded-Proto / URI scheme 归一后必须是 http 才命中这条路由。默认值。网关前面没有 TLS 卸载、或调用方直接以 http 访问时用。 |
| HTTPS | 只有归一后为 https 的请求才命中。网关对外只允许 https 接入时用。后果: 网关部署在做了 TLS 卸载的反向代理后面时,必须由前置代理透传 X-Forwarded-Proto: https,否则这条路由永远匹配不上。 |
| WebSocket | 当前版本建议不要选,理由见下方重要提示。 |
重要提示: 「WebSocket」这个取值名不副实。WebSocket 握手请求到达网关时 scheme 仍是 http / https,除非前置代理显式送
X-Forwarded-Proto: ws,否则这条路由的匹配条件永远不成立;而且实际转发地址的 scheme 取自上游服务的协议,上游只有 HTTP / HTTPS 两个取值,最终仍以 http / https 发出,并不会走 WebSocket 转发。此外选它的路由不会下发重试,API 路由也禁止选它(保存直接报错)。要做 WebSocket 代理,当前版本请在平台网关侧另行处理。
请求方法(method)逐取值:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| ALL | 不限方法,任何方法都能命中。整段代理(网关路由 + 通配路径)时用。后果: 若同时配了重试次数,可重试方法会被展开成 GET、HEAD、POST、PUT、PATCH、DELETE、OPTIONS 全套——等于对 POST / PUT 这类非幂等方法也开了重试,上游要自己保证幂等。 |
| GET | 只有 GET 命中;配了重试时只重试 GET。查询类接口用。 |
| POST | 只有 POST 命中;配了重试时会显式把可重试方法设成 POST(不显式设的话,框架默认只重试 GET,等于配了没用)。绝大多数对外业务接口、含打分类接口都用它。 |
| PUT | 只有 PUT 命中,重试方法同为 PUT。更新类接口。 |
| DELETE | 只有 DELETE 命中。删除类接口。 |
| PATCH | 只有 PATCH 命中。局部更新类接口。 |
| HEAD | 只有 HEAD 命中。探活 / 元信息探测。 |
转发方式(rewriteMode)逐取值——决定对外路径怎么变成发给上游的路径:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| 直接转发 | 原样把客户端请求的完整原始路径发给上游,一个字符都不改。默认值,上游路径和对外路径完全一致时用。后果: 保存时会把「重写值」强制清空。 |
| 去掉前缀 | 重写值填的是层数(正整数)。运行时按 / 切段,从左侧去掉指定层数的路径段,剩下的重新拼成路径;全去光则变成 /。例如层数填 1,/order/detail → /detail。对外用 /业务前缀/... 做命名空间、上游没有这层前缀时用。后果: 层数必须是大于 0 的整数,留空或填 0 保存会被拒。 |
| 路径重写 | 用重写值这个字面量整体替换掉转发路径(自动补前导 /)。一条对外路径固定映射到上游另一条固定路径时用,例如 /open/score → /internal/score/run。本章案例正是用它把 /score/credit 重写成 /flow/{flowId}/run-api(打分流的同步对外端点)。后果: 它不是正则替换,没有捕获组,也不保留原路径的任何部分——/a/{id} → /b/{id} 这种带路径参数的映射做不到,带路径参数的接口选它会把参数丢掉。重写值为空时保存被拒。 |
| 置零路径 | 转发路径固定成 /,原路径全部丢弃(查询串仍然保留)。上游只在根路径上提供一个入口、靠请求体区分动作时用。后果: 保存时会把「重写值」强制清空。 |
高级匹配 - 来源(source)逐取值——同一条路径要按请求特征再分流时用:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| Header | 请求必须带这个头,且头值匹配由操作符生成的规则,路由才命中。按渠道标识、版本号、灰度标记这类请求头分流时用。 |
| Query | 请求必须带这个查询参数,且参数值匹配。按 URL 上的参数分流时用。 |
| Cookie | 当前未实现,见下方重要提示。 |
| IP | 当前未实现,见下方重要提示。按 IP 控制访问请改用「IP 黑白名单」插件(见 5.11.2)。 |
重要提示: 「Cookie」和「IP」两个来源未实现,且是静默失效:下拉里能选、能保存进库,但运行时组装匹配条件时没有这两个分支,这条规则被悄悄丢弃——不报错、不落日志,路由的表现就像"没配过这条匹配"。配了这两种来源却发现分流不生效时,不必怀疑写法,是功能本身没有。
高级匹配 - 操作符(operator)逐取值——决定"匹配值"怎么和实际值比:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| 全等匹配 | 实际值必须与填写值完全相等(填写值整体转义,不会被当正则解释)。默认值,精确路由用它。 |
| 前缀匹配 | 实际值以填写内容开头即命中。版本号、租户前缀这类按开头分流的场景。 |
| 后缀匹配 | 实际值以填写内容结尾即命中。 |
| 包含匹配 | 实际值含有填写内容即命中。值里带多段信息、只关心其中一段时用。 |
| 不存在匹配 | 名不副实,实际语义是"该参数必须存在,值任意",见下方注意。 |
| 正则匹配 | 把填写值原样当正则用,不做任何转义。需要复杂条件(多值枚举、字符类)时用。后果: 填写值语法错误时,只有这一条路由在数据面装配时被忽略并打一条告警日志,表现为该路径 404,其余路由照常加载,不会影响整份路由刷新;但 404 这个表象不容易让人联想到正则写错,写之前先自测正则。 |
注意: 「不存在匹配」这个名字与实际行为完全相反。它下发的规则是"值匹配任意内容",而底层在该头 / 该参数不存在时直接判不命中——合起来的语义是「该参数必须存在,值是什么都行」。想表达"只要带了这个头就命中、不关心值",用它正合适;想表达"该参数不存在时才命中",当前没有任何取值能做到。
请求头处理(headerRules)——不是下拉,但是一组容易误解的行为约定:
| 项 | 说明 |
|---|---|
| 唯一行为:新增 / 覆盖 | 转发前把填写的 Header 名设置成填写的值——原来没有就是新增,原来有就是整体覆盖(不是追加)。给上游补渠道标识、内部路由标记等固定请求头时用。 |
| 四个凭据头的例外 | Authorization、ClientID、X-App-Id、X-API-Key(忽略大小写)有特殊保护:客户端请求里已经带了非空值时,这里配的规则会被跳过,不覆盖调用方的原始凭据。想用这条规则给上游统一注入一个固定 Authorization,只有在调用方没带这个头时才会生效。 |
注意: 请求头处理只对请求生效,并且没有"删除请求头"的能力,也没有"响应头处理"。全平台目前不提供删除请求头的入口。
请求超时(ms)与重试次数——填了不一定生效:
| 项 | 说明 |
|---|---|
| 请求超时(ms) | 开关打开时:优先用路由的超时,路由没配就回落上游的超时。运行时确实把 0 或负数当成"不限、回落全局配置",但这个逃生口在界面上走不通——输入框最小值是 1,后端保存也要求大于 0(路由报「请求超时必须大于0」、上游报「超时时间必须大于0」),真要填 0 只能改库。当前部署里这个开关是关着的,见下方重要提示。 |
| 重试次数 | 大于 0 时下发重试:只对上游 5xx 重试(4xx 属客户端问题,不重试),并按路由自身的请求方法显式设置可重试方法。WebSocket 路由不下发重试。上游偶发 5xx、且接口幂等时才配。 |
重要提示: 路由和上游上填的请求超时当前不生效——路由级超时开关默认关闭,所有动态路由实际吃的是全局 10 秒响应超时。这个开关默认关是有意为之:这两列的建表默认值都是 3000,存量数据基本都是没人动过的 3000,无条件打开会把所有已发布接口从 10 秒一刀切收紧到 3 秒,像同步跑完整条打分流这类接口会立刻开始超时。要启用必须先由运维逐条核对存量值。
说明: 重试有两个连带影响,排障时要知道:1. 访问日志写在重试环内,重试 N 次会写 N+1 条访问日志(换来的好处是每次重试都会重新选一个上游节点,天然有节点级容灾);2. 鉴权、限流、熔断都在重试环外,重试不会重复扣限流配额、也不会重复计熔断次数——不要怀疑"限流被重试绕过"。
- 验证:发布后经对外网关调用该路径,能命中并转发即说明路由生效;若 404,回到 5.3、5.5 检查上游与快照。
重要提示: 对外打分路由必须在请求头处理里注入网关凭证
X-Gateway-Auth: DitWfApi2026Gateway。否则打分流的对外端点会报"网关调用凭证校验失败"——这把共享密钥只有网关持有,任何绕开网关直连打分流的请求都过不了这道校验(机制详见 5.9)。
注意:
- API 路由不允许通配符
*/**,访问协议也不能选 WebSocket;需要通配,请用网关路由。- 相同"协议 + 方法 + 路径"的路由撞车会被拦两道:保存时报「相同协议、方法、路径的路由已存在」,关闭租户隔离的部署里发布时再拦一次(「路由发布失败:当前已关闭租户隔离,相同协议、方法、路径的路由已发布」)。只有跨租户的历史脏数据能绕过这两道,落到数据面装配时"只打一条告警、不拦截"。规划对外路径时仍应避免撞车。
- 删除一条路由后,系统会强制回写受影响租户的运行时快照,不需要再手工发布别的路由。
5.4.3 API 路由参数建模(apiConfig):给接口定契约
场景切入。有的对外接口不能"傻转发"——比如年龄传成负数、月收入传成字符串,若不拦,白白打到上游浪费一次调用,还可能把脏数据喂进模型。这时就把路由类别设成 api,给它配一份参数契约。
是什么。当路由类别是 api 时,必须带 apiConfig——声明请求的 header/query/path/body 参数,以及响应的期望结构。通俗来讲,它让一条 API 路由不只是"傻转发",而是有了一份契约:非法入参在网关这一层就被挡下,响应结构不符合预期也能被发现。
为什么。为后续的 API 文档、Mock、契约校验打底。入参不合法就不必打到上游浪费一次调用;响应结构漂了能第一时间暴露,而不是等下游反馈"返回怎么少了个字段"才发现。
怎么做。
涉及该操作的功能权限:路由的新建 / 编辑;api 类别缺 apiConfig 在保存时就被拦,路由根本存不进去。
在路由编辑页第 4 步 API 参数 面板配置:
| 参数名称 | 描述 |
|---|---|
| Header / Query / Path 参数 | 各位置的参数声明:参数名、是否必填、数据类型、说明。数据类型逐取值见下。 |
| Body 请求体 | 先选请求体类型(none / form-data / raw),raw 再选 json 或 xml;逐取值见下。 |
| 请求参数树 | raw 类型下用一棵树描述嵌套结构,逐层声明字段名、类型、必填、区间、格式。 |
| 预定义响应期望 | 按上游可能返回的每种状态码各建一条,声明结构并可对字段做后处理。这是最容易踩坑的一块,务必读下方关于状态码的重要提示。 |
| 请求 / 响应示例 | 纯文档性质,便于对齐与联调,不参与校验。 |
参数数据类型逐取值(用于 Header / Query / Path / form-data 参数):
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| String | 不做任何格式校验,任何值都通过。默认值,文本参数用它。 |
| Number | 按高精度数值解析,解析失败返回 400「参数类型不匹配」。金额、数量等数值参数用。后果: 整数和小数都算通过,不区分。 |
| Boolean | 值必须是 true 或 false(忽略大小写),否则 400。开关型参数用。后果: 1/0/yes/no 一律判为不匹配。 |
| Object | 值必须能被解析成 JSON 对象,否则 400。在查询参数或表单字段里塞了一段 JSON 时用。 |
| Array | 值必须能被解析成 JSON 数组,否则 400。后果: 指的是"这串文本是不是合法 JSON 数组",不是指"重复出现的同名参数"。 |
请求体类型逐取值:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| none | 不校验请求体,也不做 Content-Type 协商。默认值,GET 这类没有请求体的接口用。 |
| form-data | 按声明的参数逐项校验:必填项缺失 400,类型不符 400。同时做 Content-Type 协商——客户端显式带了 Content-Type 且既不是 multipart/form-data 也不是 application/x-www-form-urlencoded 时,直接返回 415。表单提交类接口用。 |
| raw / json | 先确认报文是合法 JSON(不是则 400「Body参数格式错误: 需要合法的JSON」),再按请求参数树逐层校验必填、类型、空值、区间、格式。Content-Type 显式带了但不含 json 时返回 415。绝大多数对外 POST 接口用它。后果: 请求体为空但参数树里有必填节点时,返回「Body参数缺失」。 |
| raw / xml | 按 XML 解析并用参数树校验(已禁用外部实体展开)。Content-Type 显式带了但不含 xml 时返回 415。对接只发 XML 的老系统时用。后果: "报文格式是否合法"的前置检查只对 json 生效;XML 的合法性检查发生在参数树校验里,参数树留空时畸形 XML 会被直接透传给上游。 |
注意: form-data 的校验实现是按
a=1&b=2这种表单编码解析报文的,真正的 multipart 文件上传报文取不到字段值——必填校验会误判成"缺参数"。走文件上传的接口,请用网关路由(gateway 类型),不要配成 API 路由。
form-data 参数的「内容类型」列——可选 application/octet-stream、application/json、application/xml、text/plain、text/html 五个值,用来标注这个表单字段里装的是什么(二进制文件、一段 JSON、一段 XML、纯文本、HTML 片段)。
注意: 这一列不参与任何运行时校验,五个取值都只是接口文档性质的描述,填错不会导致请求被拦、也不会被纠正。
参数树节点类型逐取值(请求参数树与响应期望树共用):
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| string | JSON 场景要求值是字符串;XML 场景一律通过。配了区间时,按字符串长度比较。 |
| number | JSON 要求值是数值,XML 要求文本能解析成数值。配了区间时,按数值本身比较。 |
| integer | 要求数值去掉尾零后没有小数位。计数、ID 这类整数字段用。后果: 1.0 会被判为整数通过。 |
| boolean | JSON 要求布尔值,XML 要求文本是 true/false。 |
| object | JSON 要求值是对象,然后按子节点逐个下钻校验;XML 要求该元素至少有一个子元素。配了区间时,按成员个数比较。 |
| array | JSON 要求值是数组;配了区间时按元素个数比较;打开「元素唯一」会检查数组内不许有重复元素。数组里的每个对象元素都会按子节点逐个校验。 |
| null | 要求该值为 null。几乎用不到——上层遇到 null 会先走"非必填就跳过"的分支,实际很难触发。 |
字符串节点的 Format 逐取值(类型选 string 时才出现):
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| (空) | 不做格式校验。默认值。 |
| date-time | 要求能按带时区偏移的日期时间解析,形如 2026-08-05T10:00:00+08:00,否则报「格式不匹配」。后果: 不接受 2026-08-05 10:00:00 这种常见的无时区写法,会判失败——上游或调用方用的是无时区格式时,不要选它。 |
| date | 要求能按 yyyy-MM-dd 解析。日期字段用。 |
按 ^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$ 校验。后果: 是宽松校验,不检查顶级域。 | |
| hostname | 当前未实现:下拉里有,但运行时没有这个分支,落到"一律通过",选了等于没选。 |
| ipv4 | 要求解析结果是 IPv4 地址。 |
| ipv6 | 要求解析结果是 IPv6 地址。 |
| uri | 要求能按 URI 解析。后果: 这是非常宽松的检查,几乎任何不含非法字符的字符串都能通过,别把它当作"是不是一条可用链接"的校验。 |
说明: 运行时还支持
uuid格式(标准 8-4-4-4-12 且版本位 1~5),但前端下拉里没有放出这个选项,当前只能通过接口直连写入。
数值节点的两个区间开关(类型选 number / integer 时出现):
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| 可等于最小值 / 可等于最大值 关(默认) | 边界值判不通过:值恰好等于最小值时报「小于最小值」,恰好等于最大值时报「大于最大值」。确实要开区间时才保持关闭。 |
| 开 | 边界值判通过,即闭区间。绝大多数场景应该开。 |
注意: 这两个开关默认是关的。也就是说填了"最小值 = 1"之后,值正好为 1 的请求会被 400 拒掉——这几乎不是配置者的本意。配数值区间时记得同时把这两个开关打开。另外这两个开关只对 number / integer 显示;string 的长度区间、array 的元素个数区间走同一套比较逻辑,但界面上没有对应开关,等于恒定为开区间。
响应期望:每条期望由"状态码 + 类型 + 字段树"组成。类型两个取值:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| json | 按 JSON 解析上游响应并用字段树校验,通过后再执行字段后处理。默认值,上游返 JSON 时用。 |
| xml | 按 XML 解析并校验、处理。上游返 XML 时用。后果: 字段后处理改写 XML 后会重新序列化,缩进、声明头这些格式会变。 |
重要提示(API 路由最容易踩的坑): 网关拿上游实际返回的状态码,去响应期望列表里找字符串完全相等的一条,找不到就把整个响应改写成 502「响应状态码不符合预期」。默认只有一条 200 期望,所以上游返 400 / 401 / 404 / 500 时,调用方收到的一律是 502,看不到上游的真实错误。上游有几种状态码,就必须逐个建期望。要注意界面上做不到"只填状态码、字段树留空":新建一条期望时,系统固定塞进一个必填的「根节点」(object 类型、不允许为空),而这个根节点没有删除按钮、删不掉。字段树非空就会真校验——上游返纯文本报「响应参数格式错误: 需要合法的JSON」、返 JSON 数组报「响应类型不匹配」、返
{}报「响应不能为空」,三种情况仍然被改写成 502。想让调用方看到上游的真实错误,只能按该状态码的真实报文结构把根节点(及必要的子节点)描述对。
响应字段后处理逐取值(只有响应期望的字段树有这一列,请求树没有):
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| 不处理 | 原样返回。默认值,绝大多数字段用它。 |
| 姓名脱敏 | 按中文姓名策略脱敏(保留姓、名字打码)。响应里带客户姓名、对外不该看全名时用。后果: 按字符串处理,值不是中文姓名时结果会很奇怪。 |
| 手机号脱敏 | 中间四位打码。后果: 非 11 位手机号的结果不可预期。 |
| 身份证脱敏 | 按身份证策略脱敏。 |
| 邮箱脱敏 | 按邮箱策略脱敏。 |
| 日期格式化 | 「处理参数」填目标格式(默认 yyyy-MM-dd HH:mm:ss)。先依次尝试按带时区、无时区、纯日期三种方式解析原值。上游返回的时间格式和对外约定不一致时用。后果: 解析不出来就原样返回、不报错——配了看不出错,要自己验证。 |
| 数字保留位数 | 「处理参数」填保留小数位数(默认 2),四舍五入。金额、比率统一小数位时用。后果: 返回的是字符串不是数字,字段类型在调用方那侧会变;解析失败同样静默返回原值。 |
| 截取字符 | 「处理参数」填"开始位置,长度"(如 0,4)。只填开始位置时截到末尾;开始位置超出长度返回空串。只对外露出前几位(如卡号前 4 位)时用。后果: 位置是从 0 开始的字符下标,不是从 1。 |
| 自定义脚本 | 在隔离的 JS 沙箱里执行脚本,可用 value(当前字段值)与 ctx(字段名、路径、路由、方法、请求路径、完整响应体),用 return 返回处理后的值;沙箱禁止访问任何宿主对象。内置的几种处理满足不了时才用。后果: 有硬性 300 毫秒超时,超时或脚本报错会让整个响应被改写成 502(不是跳过这个字段);脚本线程池默认只有 2 个线程,高并发接口上要谨慎。 |
验证:配好 API 参数并发布后,故意传一个缺必填字段或类型不符的请求,网关直接返回参数校验失败(未打到上游),即说明契约已在网关这一层生效;再让上游返一个非 200 的状态码,确认已按预期建了对应期望、而不是收到 502。
注意:
- 类型选了 API 路由却没配 API 参数,保存时就直接报「API路由必须配置API参数」,路由根本存不进去——不是等到发布才拦。
- 运行时已经生效的校验:Header / Query / Path / Body 参数校验、响应状态码与结构校验、响应字段后处理。
- 字段后处理只在响应侧生效,请求侧的字段转换尚未接入。Mock、接口文档自动生成仍属未落地项。若只想纯转发不校验,把路由类型设成 gateway。
5.5 第四步:发布生效——三级发布与运行时快照
场景切入。配好了应用、上游、路由,直接去调却还是 404——因为线上根本没看到这些改动。原因是:管理端所有配置都是"先存后发",不点发布就只躺在管理库里。
是什么。管理端所有配置都是"先存后发"。应用、上游、路由三者都各自独立发布;发布这个动作,会把当前租户的配置展开成一份运行时快照写入分布式缓存,并广播通知让对外网关热加载。
为什么。这正是控制面与数据面解耦的落点:对外网关运行时只认快照里展开好的东西(插件链 pluginChain、授权的 appId 白名单、重写规则),不直接读管理态的关系表。因此发布即秒级生效,而没发布的改动线上完全看不到。好处是线上稳定——正在编辑一半的半成品配置不会漏到线上,只有明确"发布"才生效。
三个对象共用的状态(status)逐取值——路由、上游服务、客户端的状态列取值和含义完全一致:
| 取值 | 是什么 / 什么时候用 / 切到它会发生什么 |
|---|---|
| draft(草稿) | 只在管理库里存在,不会进运行时快照,对外调不通。新建时的默认值。配置还没定稿时就停在这一档。 |
| published(已发布) | 通过列表上的发布按钮切到该状态时,系统先做发布前校验,再版本号 +1、回源数据库重建整租户快照、写进缓存并广播给数据面,数据面收到后重载路由。配置定稿、要真正对外生效时点它。 |
| offline(已下线) | 从运行时快照中摘除,对外立即不可调用。列表上的取消发布按钮就是把 published 切成 offline。临时停服或接口下线时用。 |
发布前校验(点发布按钮才会做):路由要求它引用的上游已发布、绑定的治理模版已发布、API 路由的路径里没有通配符,关闭租户隔离的部署里还会再查一次同"协议 + 方法 + 路径"的已发布路由有没有撞车;上游要求至少有一个合法节点。任一条不满足,发布被拒并给出对应文案。
重要提示(三个页面都有的坑): 编辑页里的状态下拉本身也能直接选「已发布」并保存,但保存走的是普通更新,不经发布服务——不做发布前校验、也不重建快照。结果是列表显示"已发布"、按钮变成"取消发布",而数据面里根本没有这条配置,调用一律 404 或 401,看着状态却是对的。要真正生效,必须先存成草稿或下线态,再点列表操作列上的发布按钮。路由、上游、客户端三处都是这样,排查"状态明明是已发布却调不通"时先想到它。
发布链路(系统自动完成):改业务状态为 published → 生成该租户的运行时快照 → 写发布记录(gw_publish_record)→ 写快照到缓存 → 写版本号 → 广播发布事件。对外网关订阅该事件,收到即按租户重载并立即换用新路由;此外每 5 秒轮询一次版本号兜底,即使广播消息偶发丢失也能补偿。
怎么做。分别在应用、上游、路由的列表页点发布 / 上线(changeStatus)。发布后可通过对外网关的运行时状态接口观测版本:GET :39302/runtime/status,返回 {tenantCount, versions}(见 5.12.2)。
验证:发布前记下 /runtime/status 的版本号,发布后再看,版本号递增即说明新快照已被网关加载;此时再调对外路径,应能命中。
重要提示: 生效有四个前提,缺一不可:1. 路由已发布;2. 它关联的上游已发布;3. 快照已写入缓存;4. 网关已加载到该快照。缺上游发布 → 404。改了鉴权、应用、路由、插件绑定中的任何一项,都要重新发布对应对象刷新快照,否则运行时还是旧的。
说明: 超级管理员在空租户下保存时,系统会把租户标识归一化为
default,避免空租户的快照被跳过而"发了等于没发"。
5.6 第五步:把授权收在资源上——绑定授权应用(默认拒绝)
场景切入。身份验证过了,是不是就万事大吉?不是。信贷业务方的 key 明明合法,却不该能调股票的接口。"key 合法"和"有权访问这条接口"是两码事,后者要靠把授权收在每一条路由上。
是什么。授权应用白名单: 每一条开了鉴权(needAuth=true)的路由,都必须显式绑定被授权的应用。没有绑定任何应用的、已发布的鉴权路由,一律拒绝访问。这是一条 fail-closed(默认拒绝) 的红线,专门防止"任意一把合法 apiKey 就能通吃所有接口"。
为什么。身份对了(apiKey 有效)不等于有权访问某一条路由。授权点收在**资源(路由)**这一层,而不是"只要 key 合法就放行"。默认拒绝的意思是:没明确授权,就一律不放——宁可错拦,不可错放。案例中要演示"股票业务方调信贷打分接口被拒",靠的就是这条白名单——/score/credit 只绑了风控消费方App(RISK-CONSUMER-001)。
怎么做。
涉及该功能权限:仅路由的发布(改绑定后须重新发布刷新快照)。绑定关系本身不经"路由编辑"产生——编辑路由不会写入或改动绑定表。
绑定关系落在 gw_route_app,它是这条授权关系的唯一真源。当前该关系没有产品化的写接口(控制面能力缺口):既不在路由编辑/保存里,也没有独立的绑定 REST 接口或前端入口——保存路由只写路由本体与插件绑定,不碰 gw_route_app。因此绑定目前只能由运维直接向 gw_route_app 插入一行(routeId, appId),再重新发布该路由刷新快照。发布时系统读 gw_route_app 生成路由元数据里的 authorizedAppIds,运行时鉴权链据此与当前请求的 appId 比对。
行为对照(选项 + 后果):
| 情形 | 结果 |
|---|---|
路由在 gw_route_app 里无任何绑定 | 403「当前接口未绑定授权应用」(旧版曾放行导致通吃,现已收紧为默认拒绝)。含义是:新建一条需要鉴权的路由并发布后,在补上授权记录并重新发布之前,任何调用方都会拿到 403。 |
| 绑了,但当前 appId 不在白名单 | 403「当前应用未被授权访问该接口」。这就是防"任一有效凭据通吃所有已发布路由"的那道闸。 |
| 绑了,且当前 appId 在白名单 | 放行,进入转发。 |
两种 403 都会同时记一条告警日志(按路由在进程内去重,不会刷屏),排查时可以在网关日志里找到对应记录。
验证:给 /score/credit 绑定风控消费方App并重新发布后,用风控消费方 appId(RISK-CONSUMER-001)调用能出分;用股票消费方 appId(STOCK-CONSUMER-001)调用返回 403「当前应用未被授权访问该接口」——白名单生效,授权闭环成立。
重要提示: 改了绑定关系后,必须重新发布路由刷新缓存快照才生效;否则白名单还是旧的,演示越权时会得到与预期不符的结果。
5.7 鉴权判定全景:五态 401 + 两态 403
场景切入。下游同事发来一句"调你接口报错了",若只回一句"鉴权失败",他还是不知道是自己没带头、key 抄错、还是没被授权。网关把失败原因拆得足够细,就是为了让下游一看文案就能自查。
是什么。对外网关对每一条 needAuth 路由,按固定顺序判定身份与授权;失败时返回带中文原因的统一结构,鉴权类失败用 401、授权类失败用 403。判定顺序是:先取 tenantId(缺失即返回「缺少租户标识」)→ 取 appId(先看请求头 X-APP-ID,再看查询参数 appId)→ 查应用是否存在且已发布 → 校验凭证 → 校验路由授权白名单。
为什么。把"没带身份 / 身份不存在 / 凭证错 / 凭证过期 / 未授权"拆成不同的码和文案,下游一看就知道是自己没带头、还是 key 错、还是没被授权,不用猜,也不用来回问。
401 的五种状态:
| 文案 | 触发条件 |
|---|---|
| 缺少应用标识 | X-APP-ID 头和 appId 查询参数都没带。 |
| 应用不存在或未发布 | appId 查不到对应的已发布应用(常见于草稿态应用)。 |
| 应用鉴权失败 | 应用有生效的鉴权规则,但请求带的凭证不匹配(key 错、口令错)。 |
| 应用鉴权已过期 | 应用有规则,但全部过期 / 无生效规则。 |
| 缺少租户标识 | 需鉴权路由上取不到 tenantId 时返回。该校验在 needAuth 路由上无条件执行(与是否开启租户隔离无关);但网关会先从路由元数据回落 tenantId,故仅当路由元数据本身缺 tenantId(异常数据)时才会出现。 |
403 的两种状态:「当前接口未绑定授权应用」「当前应用未被授权访问该接口」(见 5.6)。
默认头名与取值:身份标识默认头 X-App-Id、查询参数 appId;apikey 默认头 X-API-Key;basic 默认头 Authorization。
验证:依次构造四种非法请求(不带头 / 用不存在的 appId / 带错 key / 用全过期应用),各自应精确返回对应的 401 文案;再用一个合法但未授权的 appId,应返回 403——文案与码一一对上,即说明判定链齐全。
注意:
- needAuth=false 的路由完全跳过鉴权,是公开路由。
- HTTP 头名大小写不敏感(协议本身如此),但头的值区分大小写——key 抄错一个大小写就会判"鉴权失败"。
- 只给了 key、没给
X-APP-ID,报的是「缺少应用标识」而不是「鉴权失败」——两个头都要带。- 返回是标准的失败结构,HTTP 状态码就是 401 / 403。
5.8 边界须知:平台网关 vs 对外消费网关
场景切入。5.1 已点过三个网关,这里从"边界"角度再钉一遍,因为这条边界一旦破了,后台就等于裸露到公网——它直接决定接口安不安全。
是什么。对外消费网关只做一件事:从缓存快照里动态装配业务路由。它不承载任何内部服务的前缀转发。内部服务的前缀(/system、/data-management、/model……)只在平台网关上按去前缀规则转发,绝不透传到外网。
为什么这条边界不能破。对外只暴露被"显式发布 + 绑定授权"的业务路由;内部管理接口一旦从外网入口可达,等于把后台裸露到公网。案例演示时,内部联调用平台网关(去掉一段前缀打到各服务),消费方接入永远只给对外消费网关。
验证:拿一条内部前缀路径(如 /system/...)去打 39302,应当 404(对外网关根本不认内部前缀);同一路径经 38080 才能命中——两个入口的可达范围一验便分明。
重要提示:
38080(平台网关)走去前缀转发到内部服务,例如/model/...会打到模型运行时;建模用的 model 在/data-management/model/...,两者路径相似但去向完全不同,别混。39302(对外消费网关)只认已发布的网关路由;消费方永远只调39302。- 对外消费网关默认关闭租户隔离,不要求下游带租户标识头。
5.9 打分流对外发布的关键机关:run-api 共享密钥与服务 token 注入
场景切入。外部消费方没有平台账号,可打分流内部的每一步(调模型、调规则、取数)都要一个"人头"去承接权限判定——身份从哪来?若什么都不做,链路会断在流内部。这一节讲的就是"外部无身份也能安全出分"的那套机关。
是什么。对外发布的打分流,用的是流的 run-api 同步端点。它一次性解决三件事:1. 校验网关注入的共享密钥 X-Gateway-Auth,证明"这次调用确实经过了网关";2. 把执行身份切成一个专用的打分服务账号,并用它现铸一枚 token 注入到流的 token 输入;3. 只返回流的 END 节点声明的输出。
这里要分清两个 run 端点,别搞混:
/flow/{id}/run:异步,返回实例 id,需要平台登录态。施工自测用。/flow/{id}/run-api:同步,返回流声明的输出,入参是扁平 Map。对外发布用。
用一对反义概念记牢:run 是"异步、要登录、返回实例号",run-api 是"同步、免登录、返回结果"。
为什么需要这套机关。共享密钥 X-Gateway-Auth 用来证明请求"确实经过网关"(这把密钥只有网关持有),把"绕开网关直连打分流"的路堵死;服务 token 由端点代为注入,解决"外部无身份导致链路断在流内部"的问题。
重要提示: 打分服务账号是最小权限身份,不是"看什么都明文"的特权身份——它的角色与权限按该账号的真实画像现取,取不到就直接拒绝执行,绝不回落超管。所以要让打分流读到中台数仓的某张表,必须像给普通人那样把这张表显式授权给这个服务账号;读到明文还是掩码,同样按第二章 2.5.2 的口径判定(演示环境给它的那条授权没配列脱敏,故读到明文)。忘了授权的表现是流程跑到取数那一步失败,别往模型上找原因。
怎么做。
涉及该操作的功能权限:路由的编辑 / 发布(改 headerRules 后须重新发布);扩展对外输出字段需要对应打分流的编辑 / 发布权限。
对外路由设 rewriteMode=路径重写,把对外路径重写成 /flow/{flowId}/run-api;在 headerRules 注入 X-Gateway-Auth: DitWfApi2026Gateway。run-api 只回 END 节点 outputBindings 声明的输出(本章案例是 admission_score、decision、approveProb)。
验证:经网关调对外路径能拿到 END 声明的三个输出,即证明"共享密钥校验 + 服务 token 注入 + 输出裁剪"三件事都跑通了;若报「网关调用凭证校验失败」,回到路由 headerRules 确认 X-Gateway-Auth 已注入。
注意:
- 不注入
X-Gateway-Auth头,无论直连还是经网关都报「网关调用凭证校验失败」——这把密钥只网关持有,直连 run-api 无解。- run-api 的入参是扁平 Map(不是
inputs:[]包装)。- 想扩展对外返回的字段,要改 END 节点的
outputBindings并重新发布流,否则新字段不会出现在响应里。
5.10 下游消费:一次 HTTP 调用拿到风控结论
场景切入。前面所有配置的目的,就为了这一刻:业务系统的同事不看任何内部文档,只发一个 HTTP POST,把申请人的几个特征传进来,拿回"通过还是拒绝"。
是什么。下游业务系统接入的成本被压到极低:一次 HTTP POST,带两个身份头 + 原始特征,就拿回出分结论,完全不需要懂内部的模型和规则编排。这也是整个产品的落点——把数据变成"一次 HTTP 调用就能拿到的风控结论"。
怎么做。经对外消费网关 :39302 调 /score/credit。消费方只传原始特征(年龄、月收入、负债率、逾期次数),其中"通过概率"由流内部的模型自算,不用外部传。
必带的两个头:
| 参数名称 | 描述 |
|---|---|
| X-APP-ID | 应用标识,即 appId(如 RISK-CONSUMER-001)。缺了报「缺少应用标识」。 |
| X-API-Key | 明文 apiKey。从 GET /gateway-admin/app/{id}/sensitive 取。 |
请求体字段:age、monthly_income、debt_ratio、overdue_count。
一次成功调用示例:
bash
curl -X POST http://<gw>:39302/score/credit \
-H "Content-Type: application/json" \
-H "X-APP-ID: RISK-CONSUMER-001" \
-H "X-API-Key: <取自 /app/{id}/sensitive 的明文 key>" \
-d '{"age":35,"monthly_income":15000,"debt_ratio":0.3,"overdue_count":0}'
# → {"code":200,"msg":"操作成功","data":{"outputs":{"decision":"通过","admission_score":"90.0","approveProb":"0.744778"},"instanceId":"...","success":true,"status":"SUCCESS"}}验证:返回 code:200 且 outputs 里带 decision/admission_score/approveProb,即打通了从下游 HTTP 到内部出分的完整链路。
注意:
- 出分概率在模型出参处统一 round 到最多 6 位小数(消除浮点长尾与科学计数法的超长小数);看到更长的多半是老数据或没走新出口。
- 字段命名以实际建流入参为准:
monthly_income、debt_ratio、overdue_count。- 经
39302调用,不是38080。
5.11 进阶配置
5.11.1 OpenAPI 导入路由(校验 → 预览 → 提交)
场景切入。一个已有几十个接口的服务要整体接进网关,一条条手建既慢又易错。若它本来就有一份接口描述文档,直接导入成路由即可。
是什么。把一份 OpenAPI 3.0.x 的 JSON 文档批量导入成网关路由(草稿态),再逐条发布,免去一条条手建。
怎么做。
涉及该操作的功能权限:路由的新建(
gateway:route:add,三个接口都需要)。
- 打开数据网关 → 路由管理,选择导入 OpenAPI。
- 三步向导依次执行:校验(
.../route/import/openapi/validate)→ 预览将生成的路由(/preview)→ 提交落库为草稿(/commit)。第 1 步要先定两个策略,决定这批路由落在哪、以及撞车了怎么办。 - 对提交后的每条路由逐个发布(changeStatus → published);发布前对应上游要先发布。
目录策略逐取值——决定导进来的路由挂在哪个分组下:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| 按 tags 建目录 | 取每个接口的第一个 tag 作为目录名,在所选父目录下自动建同名子目录;接口没有 tag 时落到「默认目录名」(留空则用文档标题,再没有就叫「OpenAPI导入」)。默认值。文档里的 tag 划分本来就合理时选它,导进来目录结构就是现成的。后果: 只看第一个 tag,多 tag 接口的其余 tag 被忽略。 |
| 全部导入同一目录 | 忽略 tag,所有接口都放进「默认目录名」这一个目录。文档 tag 划分很乱、或接口不多时选它。 |
冲突策略逐取值——决定撞上已有路由(同"协议 + 方法 + 路径")时怎么办:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| 跳过已存在 | 系统里已有同协议 + 方法 + 路径的路由时,跳过该条不导入,计入"跳过数"。默认值。增量补充接口、不想动已有配置时用。 |
| 覆盖草稿 | 冲突路由处于草稿状态时,用文档内容覆盖它;处于已发布 / 已下线状态时仍然跳过,并在结果里追加一条"已跳过非草稿冲突路由"的提示。同一份文档反复导入调试时用。 |
注意:
- 选「覆盖草稿」时,覆盖走的是路由更新接口,会做归属校验:冲突的草稿路由若属于别人(当前账号看不见),导入会在那一条上报错中断,已处理的前几条不会回滚。遇到这种情况改用「跳过已存在」重跑。
- 文档自身内部重复的接口(同协议 + 方法 + 路径出现两次),无论选哪种冲突策略都直接跳过。
验证:提交后在路由列表能看到一批草稿态路由;逐条发布并各调一次,能命中转发即导入闭环成立。
注意:
- 只接受 OpenAPI 3.0.x 的 JSON 文本;YAML、3.1、URL 都不支持。
- 导入生成的是草稿,要逐条发布;上游要先发布。
- 所有导入路由共用同一个上游(请求里传的
upstreamId),不会按文档里的多个 server 拆分上游——若不同接口该打到不同上游,需导入后手工分开调整。
5.11.2 插件体系:模版编排 + 内置插件
插件模版页面解决什么问题:把限流、IP 名单、加解密、熔断、访问日志这些治理动作,组合成可复用的治理模版,再由路由绑定一个模版。左侧是只读的内置插件库(共 8 个,不能自定义插件),右侧是模版列表。
场景切入。限流、黑白名单、加解密这些治理动作,若写死在每条路由里,改一次要改一片。把它们做成可复用的插件,一处配置、多条路由复用,才是可持续的做法。
是什么。治理能力被做成插件。用法是三层:插件定义 → 插件模版(把多个插件组合并排序)→ 路由绑定一个模版。发布时,系统把模版展开成路由快照里的插件链(pluginChain),运行时只执行快照里的链。
为什么。把限流、黑白名单、加解密、熔断、日志这些治理动作从主链路里拆出来,做成可复用、可排序、可绑定的单元——一处配置,多条路由复用。举个例子:所有对外打分路由共用一个"标准治理模版"(限流 + 黑白名单 + 访问日志),要收紧限流阈值,只改这个模版一处即可。
内置插件库(8 个,不可自定义)逐个说明:
| 插件 | 执行阶段 | 是什么 / 什么时候用 / 加了会发生什么 |
|---|---|---|
| IP 黑白名单 | 鉴权前 | 取客户端 IP(依次看 X-Forwarded-For 首段、X-Real-IP、连接远端地址),按名单模式判定;白名单不命中、或黑名单命中,都直接返回 403 并落一条插件轨迹日志。地址支持单 IP、CIDR(a.b.c.d/前缀)、区间(起-止)三种写法,保存时逐行做格式校验。接口只允许固定几个来源 IP 调用,或要临时封禁某个来源时用。 |
| 限流 | 鉴权前 | 按"维度值"在缓存里做固定窗口计数,超限返回 429 + 自定义拒绝文案。对外接口防刷、保护单节点上游时用。 |
| 请求解密 | 转发前 | 客户端发密文,网关用引用的密钥把请求体解成明文再发给上游。对称算法用密钥的"公钥/共享密钥"字段,非对称用私钥。要求调用方加密上送、上游只处理明文时用。 |
| 请求加密 | 转发前 | 客户端发明文,网关用密钥的公钥/共享密钥把请求体加密后再发给上游,由上游解密。网关到上游这一段要过不可信网络时用。 |
| 响应解密 | 响应后 | 上游返回密文,网关先解密,再交给 API 路由的响应校验和字段处理。解密后按内容首字符猜 Content-Type({/[ 判 JSON,< 判 XML,其余纯文本)。上游返加密报文、但网关还要按响应期望校验字段时用。 |
| 响应加密 | 响应后 | 上游返明文,网关校验 / 处理完字段之后统一加密再返回客户端,响应 Content-Type 被改成 text/plain。对外要求密文回包时用。后果: 客户端拿到的是一串编码文本,接口文档里的示例响应不再适用,要提前和调用方约定解密方式。 |
| 熔断降级 | 转发前 | 给下游调用套一个超时;按统计窗口累计总请求数和失败数(HTTP ≥500、超时、异常都算失败),达到最小请求数后失败率超过阈值就打开熔断开关(存活时间 = 统计窗口),熔断期间的请求直接按降级策略返回。上游不稳定、要防止把整条调用链拖垮时用。 |
| 访问日志 | 完成后 | 控制访问日志的级别、采样率、是否记录报文摘要;日志异步写入缓存流,由控制面消费落库。要给某条路由记完整日志、或高频接口要降采样时用。 |
注意(几个和直觉不一样的地方):
- IP 黑白名单只支持 IPv4。IPv6 客户端会因解析异常被当成"不匹配"——白名单模式下一律 403。
- 限流执行失败时是放行不是拒绝。计数依赖缓存,缓存异常时请求直接放过,等于没有限流。这是有意的"失败放行"设计,但要知道它的含义:缓存挂了,限流就形同虚设。
- 请求加密和请求解密在同一阶段,同一模版里两个都加会按顺序号先后依次作用在同一份报文上——除非确有需要,不要同时加。
- 访问日志插件所在环节位于重试环内,一次请求若触发 2 次重试会写 3 条日志。
- 没有配置访问日志插件的路由,仍会按基础级别记日志,这个插件只是用来"加详细"或"降采样"的。
执行阶段逐取值(阶段由插件自带,只读,配置者改不了):
| 取值 | 是什么 / 这一阶段跑什么 |
|---|---|
| 鉴权前(pre_auth) | 在客户端鉴权之前执行,含 IP 黑白名单和限流。同阶段内按顺序号升序执行,任一插件判拒立刻返回,后面的不再跑。 |
| 转发前(pre_forward) | 准备发往上游之前执行,含请求加密 / 请求解密和熔断。这个阶段名有一处名不副实: 请求加解密所在的环节排在鉴权环节之前,也就是说还没通过鉴权的请求,报文就已经被解密 / 加密过了;只有熔断确实排在鉴权之后。 |
| 响应后(post_response) | 上游响应回写给客户端之前执行,含响应解密(在响应校验前)和响应加密(在响应校验后)。 |
| 完成后(after_completion) | 请求完全结束后执行,只有访问日志一个插件。 |
通俗来讲,阶段决定"什么时候插一脚",顺序号决定"同一阶段谁先谁后"。
注意(顺序号并非处处有效):
- 熔断虽然登记在"转发前",实际由独立环节实现,不受顺序号控制,执行位置固定。
- 响应解密与响应加密的相对顺序写死在代码里——解密永远在响应校验之前,加密永远在响应校验之后,给它们排顺序号没有意义。
- 访问日志同样由独立环节实现,位置固定。
限流插件参数——维度决定"按谁计数",三个取值:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| appId | 按调用方客户端标识分别计数,各家配额互不影响。默认值。对外开放、多个消费方共用一条接口时用,防止某一家把配额吃光。需要鉴权的路由上,维度值取自鉴权通过后写入的可信 appId。后果: 不需要鉴权的路由用这个维度时,维度值来自调用方自报的请求头,可被伪造;且取不到 appId 时直接返回 400「缺少应用标识」。 |
| IP | 按客户端 IP 分别计数(IP 解析口径和 IP 黑白名单一致),在鉴权之前执行。防爬防刷、以及给未鉴权流量兜底时用。后果: 调用方在统一出口后面时,整个机构共用一个配额。 |
| 路由 | 维度值取路由自身,等价于"这条路由的全局总配额",所有调用方共享同一个计数器,在鉴权之前执行。保护脆弱上游、不管谁调都不许超总量时用。后果: 一旦被某一家打满,其他调用方一起被拒。 |
阈值与窗口保存时就要求填,且都必须大于 0,否则报「限流插件配置非法:阈值必须大于0」/「窗口秒数必须大于0」,存不进去。运行时另留了一份 200 次 / 60 秒的回落值,但只有历史存量数据、或绕过界面直连接口写入的配置才会落到它,不要照着"留空即用默认值"去配。缓存键包含租户 + 路由 + 维度 + 维度值,因此配额是按路由各算各的,不同路由不共享。
注意: 一条路由只能绑一个模版,一个模版里同一个插件只能加一次,所以做不到"按不同维度叠加两条限流"(例如同时限 appId 和限 IP)。需要多重限流时,当前只能取其一,或在上游侧再做一层。
IP 黑白名单的名单策略逐取值:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| 白名单 | 只有命中名单里任意一条规则的 IP 才放行,其余一律 403「当前 IP 不在允许访问范围内」。默认值。接口只对少数固定来源开放时用。 |
| 黑名单 | 命中名单里任意一条规则的 IP 返回 403「当前 IP 已被禁止访问」,其余放行。临时封禁个别来源时用。后果: 规则为空、或取不到 IP 时一律放行,等于插件没生效(空名单在界面保存时已被拦,只有历史存量或直连接口写入的数据才会出现)。 |
重要提示: 取不到客户端 IP(含 IPv6 解析失败)时,白名单模式会把所有请求拒掉。加了这个插件就必须把名单填全,并确认调用方都是 IPv4 来源,否则一发布接口就全线 403。名单留空这一档在保存时已经被挡住了(报「IP 黑白名单插件配置非法:地址列表不能为空」,还会逐行做 IPv4 格式校验),只有历史存量、或绕过界面直连接口写入的空名单,才会真的落到"白名单全线 403"。
加解密插件的三个参数:
| 参数 | 取值与说明 |
|---|---|
| 报文位置 | 只有 Body 一个取值,对请求体 / 响应体整体做加解密,留空也按 Body 处理。 |
| 输出编码(仅加密方向) | base64:密文以 Base64 文本输出,默认值,通用性最好,不确定就选它。hex:密文以十六进制文本输出,对端约定用十六进制串时选。两边编码不一致会直接解不开,要和调用方明确约定。 |
| 失败策略(仅解密方向) | 当前是只读标签,恒为「直接拒绝」:解密失败(引用密钥不存在、算法不支持、密钥内容为空、密文格式不对)一律中断请求并返回错误。不提供"透传原文"——解密失败还把原文放出去,是安全方向相反的选择。 |
注意: 报文位置历史上曾经能选 Header / Query,而运行时对非 Body 的配置是静默跳过整个插件——不报错、不落日志、报文原样透传,"响应加密"会让人以为发出去的是密文、实际是明文。现在前端只放 Body、后端写入口也强制只接受 Body,但历史存量模版里可能还存着旧值,升级后需要人工核一遍。同理,输出编码曾经有个
raw选项,实际等同 base64,现已移除。失败策略这一档不必担心存量:即使库里还存着"透传原文",运行时也从来不读这个值,解密失败照样一律中断。
熔断插件的降级策略逐取值:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| 固定响应 | 熔断打开期间和上游超时时,都返回 503 + 填写的「降级文案」(默认「服务暂时不可用,请稍后再试」)。默认值,想给调用方一个统一、可读的降级提示时用。后果: 两种情况返回同一个状态码,调用方从状态码上分不出是熔断还是超时。 |
| 直接透传错误 | 熔断打开期间返回 503「服务熔断中,请稍后再试」;上游超时时返回 504「上游服务响应超时」。希望调用方能从状态码上区分这两种情况时用。后果: 选它之后「降级文案」输入框会隐藏、填的文案失效。 |
注意: 「直接透传错误」名不副实——它并不会把上游的真实响应体透传出去,返回的仍是网关自己造的固定文案,只是状态码和措辞与「固定响应」不同。想让调用方看到上游原始错误,这两个取值都做不到。
说明: 熔断的计数和开关放在共享缓存里,多个网关实例共享;当前没有半开探测,窗口到期自动放行下一批请求。
访问日志插件的三个参数:
| 参数 | 取值与说明 |
|---|---|
| 日志级别 | 基础:只记请求标识、客户端标识、路由、方法、路径、查询串、客户端 IP、目标地址、状态码、耗时、成功标记、错误信息、访问时间。默认值,也是没配这个插件时的默认行为,绝大多数接口用它。完整:在基础字段之外额外记录请求头摘要(Authorization、x-api-key、apikey、api_key、password、secret、token 这几个字段名会被替换成 ******)。排障期临时打开,或对合规要求高的少量接口长期打开。 |
| 采样率(%) | 界面上只能填 1~100:1~99 表示按百分比随机丢弃整条日志,100(默认)表示全量记录。高频接口降噪时用。采样跳过时会另写一条插件轨迹日志说明被跳过了。后果: 运行时另认一档"小于等于 0 = 整体关闭该路由的访问日志",但页面输入框的最小值就是 1,填不出 0 或负数——想整体关掉某条路由的访问日志,当前只能绕过界面直连接口或改库。 |
| 记录 Body 摘要 | 开关。只有日志级别是完整时才真正生效。 |
注意(Body 摘要的两个坑):
- 日志级别是基础时,「记录 Body 摘要」被强制关掉,界面上勾了也不记,且没有任何提示。
- 即使级别选了完整,Body 摘要读的是"其它环节顺手缓存下来的请求体"。网关路由若既没配 Body 位鉴权、也没配请求加解密,报文根本没被缓存过,这一列仍然是空的。API 路由因为要校验报文,通常能记到。
模版状态逐取值:
| 取值 | 是什么 / 什么时候用 / 切到它会发生什么 |
|---|---|
| 草稿 | 绑定了草稿模版的路由不能发布,会报「路由发布失败:请先发布绑定的治理模版」。模版还在调参时保持这一档。 |
| 已发布 | 这就是模版的"发布"动作本身——模版没有独立的发布按钮,把状态下拉改成已发布并保存即可。随后还要重新发布绑定它的路由,配置才会进快照。 |
怎么做。
涉及该操作的功能权限:插件的新建(
gateway:plugin:add)、编辑(gateway:plugin:edit)、删除(gateway:plugin:remove)、列表 / 查询(gateway:plugin:list/gateway:plugin:query)。
- 打开数据网关 → 插件模版,点新建模版,填模版名称、模版编码(唯一)、状态、备注。也可以对已有模版点复制,生成一份"原名-副本""原编码_copy"的草稿副本。
- 打开模版的编排面板——面板按四个执行阶段分成四列,从「添加插件到模版」下拉里选插件加进对应阶段,然后配参数、开关启停、调顺序号。
- 模版调好后把状态改成已发布并保存。
- 在路由编辑页第 5 步绑定这个模版,再重新发布该路由。
- 加解密插件要引用密钥时,面板上有到密钥管理的跳转入口(见 5.11.3)。
重要提示: 「路由覆盖配置」(某条路由在模版基础上做局部覆盖)当前做不到。这个字段能存进库,发布时也确实被写进了快照报文,但数据面的路由模型里压根没有这个字段——快照一到对外网关就被丢弃,后面组装路由元数据自然也带不上,运行时没有任何一处读它,界面上更没有编辑入口。要让某条路由的治理参数与众不同,只能复制一份模版改参数再绑。
验证:给路由绑定含限流的模版并重新发布后,快速连打超过阈值的请求应被拦下(429);绑定含 IP 黑白名单的模版后,名单外的 IP 应被拒(403)——治理动作按模版生效即闭环。
重要提示(限流的两阶段安全设计): 限流分两个阶段跑。IP / 路由维度的限流在鉴权之前执行;但 appId 维度的限流被推迟到鉴权之后才执行。原因是:鉴权之前
X-APP-ID还没校验,若此时就按 appId 限流,攻击者可以伪造别家 appId 定向消耗别家配额。代价是:鉴权失败的请求不再计入 appId 配额,这类"洪水"改由 IP 维度限流和黑白名单兜底。
注意: 模版被任何路由绑定时不允许删除,系统会直接拒绝。要删先把路由的绑定解掉。
5.11.3 加解密密钥管理
场景切入。请求解密、响应加密这类插件要用到密钥。密钥不能散落在各处硬编码,得集中管理,并且明文只在必要时受控取出。
密钥管理页面解决什么问题:集中管理加解密插件引用的密钥。支持四种算法,可由系统直接生成真实密钥对;列表里公私钥一律掩码,取明文要单独走取明文接口。
是什么。管理加解密插件所用的密钥:新建、生成、查看、编辑、复制。敏感明文单独走取明文接口,写接口做传输加密。页面顶部是统计卡(总数 / 已发布 / 非对称 / 对称),下面是明细表。
怎么做。
涉及该操作的功能权限:密钥的新建(
gateway:key:add)、编辑(gateway:key:edit)、生成(gateway:key:generate)、查询(gateway:key:query)、列表(gateway:key:list)、删除(gateway:key:remove)。
- 打开数据网关 → 密钥管理,点新建,填密钥名称、密钥编码(唯一)、算法、用途、状态、公钥 / 共享密钥、私钥、备注。
- 不想自己准备密钥材料时,选好算法后点生成,系统按所选算法真实生成并回填。
- 编辑时系统会走取明文接口回填真实密钥;复制密钥内容会把名称 / 编码 / 算法 / 用途 / 公私钥明文拼成一段文本复制到剪贴板(同样走取明文接口)。
- 取明文走
GET /key/{id}/sensitive;新增、编辑、生成、取明文这几个接口都对请求 / 响应体做加密传输。
算法逐取值——它同时决定「生成」按钮会生成什么、以及加解密时用哪一段:
| 取值 | 是什么 / 什么时候用 / 选了会发生什么 |
|---|---|
| RSA | 非对称。生成时产出 PEM 格式公私钥对,加密方向用公钥、解密方向用私钥,保存时公钥和私钥必须同时填。用在"客户端持公钥加密、网关持私钥解密"(请求解密)这类经典场景。后果: 非对称算法有明文长度上限,大报文整体加密会失败,且当前没有"对称密钥 + 非对称包裹"的混合加密实现。 |
| AES | 对称。生成时产出 32 位随机共享密钥,存在"公钥 / 共享密钥"字段里,私钥留空;加解密两个方向都用这一个值。报文较大、两边可以安全交换共享密钥时优先选它。后果: 保存时只要求"对称密钥不能为空",不校验长度,手工填了不合法长度的密钥要到运行时加解密才报错。 |
| SM2 | 国密非对称,行为与 RSA 一致(公钥加密、私钥解密,生成 PEM 公私钥对,两者必填)。有国密合规要求且报文较小时用。后果: 同 RSA,有明文长度上限。 |
| SM4 | 国密对称,行为与 AES 一致,生成 16 位随机共享密钥。有国密合规要求的常规报文加解密用它。后果: 同 AES,不校验密钥长度。 |
用途逐取值——六个取值全是纯标注,只是帮助配置者在插件的密钥下拉里认出这把钥匙是干什么的(下拉标签是"名称 · 算法 · 用途"):
| 取值 | 打算把这把密钥给谁用 |
|---|---|
| 网关请求解密 | 给「请求解密」插件用。 |
| 网关响应加密 | 给「响应加密」插件用。 |
| 上游请求加密 | 给「请求加密」插件用。 |
| 上游响应解密 | 给「响应解密」插件用。 |
| 签名生成 | ——当前没有对应功能,见下方注意。 |
| 签名验签 | ——当前没有对应功能,见下方注意。 |
注意:
- 用途不参与任何校验。把标注成"签名验签"的密钥选给「响应加密」插件,系统照样正常加密,不会提示不匹配。它只是个便于辨认的标签,选错不影响功能,系统也不会给出任何提示。
- 「签名生成」「签名验签」没有对应功能——内置的 8 个插件里没有任何签名 / 验签插件,标成这两个用途的密钥在网关里无处可用。
重要提示: 密钥的状态是装饰性的。重建运行时快照时,系统把该租户下的密钥全量下发,不看状态——也就是说草稿态的密钥照样能被插件正常引用并生效,列表统计卡里的"已发布"计数只是个统计口径,不代表运行时的开关。不要指望把密钥改成草稿来停用它。
验证:生成一把密钥后,加解密插件引用它并重新发布,对一条配了请求解密的路由发一次加密请求能被正确解出,即说明密钥与插件对接成功。
注意:
- 明文密钥只在
/{id}/sensitive返回,列表页公私钥一律掩码显示。- 删除密钥不做引用检查。引用关系写在插件配置里查不出来,所以系统不会拦下这次删除——删掉一把还在被插件引用的密钥,引用它的路由在运行时会直接报错。删之前先自行核清楚哪些模版在用它。
- 加解密只支持上表四种算法,不开放自由脚本式加解密;大报文的混合加密(对称密钥 + 非对称包裹)仍是后续项。
5.12 可观测与运维
5.12.1 访问日志链路
场景切入。线上出了问题,第一句要问的是"这次调用到底发生了什么"——谁调的、打到哪、状态码多少、花了多久。访问日志就是回答这些的账本。
是什么。对外网关每次请求完成后,组装一条访问日志事件,异步写入分布式缓存的流式结构;网关后台用消费组读取后落库到 gw_api_access_log。数据面不直接写数据库。
为什么。避免数据面直连数据库、保留削峰能力、让日志消费与转发解耦——高峰期日志先在缓存里排队,后台按自己的节奏落库,转发主链路不被写库拖慢。
日志落库表字段(节选):requestId、appId、routeId、routeName、requestMethod、requestPath、clientIp、targetUrl、statusCode、costMs、successFlag、errorMsg、accessTime 等。相关开关:单条 body 记录长度上限默认 2000、日志流长度上限默认 100000。
记多细、记不记、按多大比例记,由访问日志插件上的三个参数控制(日志级别 / 采样率 / 记录 Body 摘要),逐取值见 5.11.2。没绑这个插件的路由按"基础级别、全量记录"走。
验证:发一次对外调用后,在 gw_api_access_log 里应新增一行,appId、statusCode、costMs 与本次调用对得上,即说明日志链路通。
注意:
- 网关侧每次请求会有一次同步写入缓存流的开销。
- 日志已对常见敏感请求头 / 体字段脱敏。
- 敏感 body 只在"已缓存"时才记录(如鉴权触发过 body 缓存),不会为记日志额外强读 body。
- 采样率已经能在访问日志插件里配(见 5.11.2),但"整体关闭"页面上配不出来——输入框限定 1~100,要关只能直连接口或改库;另外尚未提供的是前端查询页面与命中轨迹可视化——当前日志只落库到
gw_api_access_log,要看只能查库。- 查询串里的内容不做掩码,是原样落库的。凭据别放在查询参数上(见 5.2.2)。
5.12.2 运行时状态与手动重载
场景切入。改了路由却怎么调都 404,不知道是"没发出去"还是"网关没收到"。这两个运维接口就是给这类排查用的探针。
是什么。对外网关暴露两个运维接口:看当前加载了几个租户快照及版本号;手动全量或按租户重载快照。
怎么做(运维直接调用):
GET :39302/runtime/status→{tenantCount, versions}。POST :39302/runtime/reload→ 全量重载;POST /runtime/reload/{tenantId}→ 按租户重载。
联调排查 404 的推荐顺序:路由发布了吗 → 上游发布了吗 → path/method 一致吗 → /runtime/status 有版本吗。若是 5xx,则是命中了路由但上游失败,去查协议 / 端口 / Host / 超时。
验证:发布后 /runtime/status 的版本号应递增;若怀疑广播丢消息导致没加载,手动 reload 后版本号应对齐管理端最新版本。
重要提示: 这两个运维接口当前未加鉴权("运维接口鉴权"是后续项),生产环境必须靠网络层(防火墙 / 内网限制)控制访问,不要暴露到公网。
5.12.3 运行记录两级展示(打分流)
场景切入。一笔申请出分异常,要看是模型算错了还是规则判错了。运行记录把每次运行拆成"整体一行 + 各节点明细",一眼定位是哪个节点出的问题。
是什么。打分流每次运行,在运行记录里以主从两级展示:顶层一次运行一行(倒序排列),展开可看每个节点的执行明细;任意一个节点失败,整条流程即判失败。
怎么做。在工作流 / 打分流 → 运行记录里查看。顶层看流程名、运行类型、状态、起止时间;展开看节点名、类型、状态、错误。本章打分流 4 个节点(START → 模型 → 规则 → END)全部 SUCCESS,输出 {admission_score:90.0, decision:通过, approveProb:0.7448}。
验证:一次对外调用后,运行记录顶层应新增一行且状态 SUCCESS;展开看到四个节点依次 SUCCESS,输出与响应体一致,即说明流内部执行与对外返回对得上。
说明: 建节点时给中文名,避免顶层显示内部 key。异步端点
/run与同步对外端点/run-api都会产生运行记录;对外消费走 run-api。
注意: 这是打分流的运行记录,与网关访问日志(5.12.1)是两套东西,别混——一个记流的节点执行,一个记网关的每次请求。
5.12.4 定时任务健康(支撑打分数据新鲜度)
场景切入。打分再准,喂进去的数据若是三天前的,结论也没意义。数据的新鲜度靠两类定时批撑着——它们健不健康,直接决定出分靠不靠谱。
是什么。打分模型和规则依赖数仓有干净、及时的数据。两类定时批负责数据新鲜度:数仓每日加工(贴源层→明细层→汇总层)与资产每日扫描。
怎么做。在数据集成 / 定时任务里查作业列表、触发器与运行记录。案例中:信贷数仓每日 02:30 加工(cron 0 30 2 * * ?),资产扫描每天 04:30。看健康的两个信号:触发器是否处于生效状态,以及最近几次运行是否 SUCCESS。
验证:触发器状态为生效、最近几次运行均 SUCCESS,即认为数据新鲜度有保障;若打分结果与预期偏离,先回这里看昨夜的加工批是否成功。
注意:
- 分析型数据源内存紧张时,可能出现"任务报成功但写入 0 行"的假成功;需为其释放内存后重跑。
- 同步引擎的覆盖写模式不会真正清空目标表,靠明细层 / 汇总层的唯一键做幂等兜底。
5.13 端到端走一遍(信贷评分)
背景。某信贷业务方需要在放款前对每笔申请做准入判断。风控团队已经在平台内把能力建好,现在要把它变成一个业务系统一次 HTTP 调用就能用的对外 API,并且保证只有被授权的消费方能调。要产出的对外接口是 /score/credit。
准备。数仓已有干净数据、打分流已建成、消费方应用「风控消费方App」(appId=RISK-CONSUMER-001,授权 /score/credit)已注册并发布、apiKey 已取;为演示跨应用越权,另注册「股票消费方App」(appId=STOCK-CONSUMER-001,授权 /score/stock)。
一笔信贷申请的完整旅程(六大能力串起来):
| 步骤 | 模块 | 动作 | 产出 |
|---|---|---|---|
| 1 | 数据中台 | 每天 02:30 定时把外部关系型业务库同步进数仓 → 加工成信贷宽表(贴源层→明细层→汇总层,各 80 行) | 数仓有干净数据 |
| 2 | 决策引擎 | 定义指标 → 训练评分卡模型(输出通过概率)→ 配规则集(4 条 R01–R04)→ 编排打分流(START→模型→规则→END) | 打分流 credit_scoring_flow |
| 3 | 数据网关 | 打分流发布成对外路由 /score/credit(重写到 /flow/{fid}/run-api,注入 X-Gateway-Auth),绑定「风控消费方App」 | 对外 API 就绪 |
| 4 | 下游 | 经对外网关 :39302 带 X-APP-ID + X-API-Key 提交原始特征 | 见下方四画像对照 |
规则集 4 条(按优先级,命中即返回):R01 高风险拒绝(逾期次数≥3 或 负债率>0.7 → 20 分 / 拒绝);R02 优质通过(通过概率≥0.7 且 负债率≤0.4 → 90 分 / 通过);R03 人工复核(通过概率≥0.5 → 65 分 / 人工复核);R04 兜底拒绝(通过概率<0.5 → 40 分 / 拒绝)。规则串在模型之后消费通过概率——先模型、后规则、链式。
验证结果:四画像出分对照(可作验收基线):
| 画像 | age / monthly_income / debt_ratio / overdue_count | 通过概率 | 准入分 | 决策 | 命中规则 |
|---|---|---|---|---|---|
| 优质 | 35 / 15000 / 0.30 / 0 | 0.745 | 90 | 通过 | R02 优质通过 |
| 高逾期 | 45 / 20000 / 0.20 / 4 | 0.008 | 20 | 拒绝 | R01 高风险拒绝(逾期≥3) |
| 高负债 | 40 / 8000 / 0.75 / 1 | 0.208 | 20 | 拒绝 | R01 高风险拒绝(负债率>0.7) |
| 边缘 | 29 / 9000 / 0.50 / 1 | 0.288 | 40 | 拒绝 | R04 兜底拒绝(概率<0.5) |
三个要点:1. 通过概率随风险单调下降(优质 0.745 > 高负债 0.208 > 高逾期 0.008);2. 概率在模型出参处统一 round 到最多 6 位小数(消除浮点长尾与科学计数法);3. 本案例的四个特征由下游请求体直传,打分流全程不读数仓——若换成需要取中台数据的打分流,记得先把相关表授权给打分服务账号(见 5.9)。
权限剧情验证(发布前必须全绿,与网关授权强相关):
- 风控消费方App(
RISK-CONSUMER-001)调/score/credit→ 出分;调/score/stock→ 403「当前应用未被授权访问该接口」(股票路由的白名单只绑了股票消费方App)。 - 股票消费方App(
STOCK-CONSUMER-001)调/score/stock→ 放行(鉴权通过、转发至股票打分流);调/score/credit→ 403「当前应用未被授权访问该接口」(信贷路由的白名单只绑了风控消费方App)。 - 只给 X-API-Key、不给 X-APP-ID → 401「缺少应用标识」。
验证闭环:四画像出分与上表逐行对齐、三条权限剧情全部命中预期码与文案,即认为"配置—发布—授权—消费"整条链路验收通过。
说明: 上述参数、阈值与出分仅适用于本节举例,不代表任何行业规范或国家标准;实际业务中的评分规则、阈值请以本机构的风控政策与官方规范为准。
5.14 常见问题 FAQ
Q:下游拿到任意一把 apiKey,是不是就能调所有接口? 不能。每一条对外路由都必须显式绑定授权应用,未绑定一律拒绝(默认拒绝);跨应用不能互调。身份合法 ≠ 有权访问某条路由。
Q:我查一张表看到了明文,是不是脱敏失效了? 多半不是。当前口径是默认明文:资产属主本人看自己建的表永远是明文;分享给别人时没配脱敏、且没被登记为敏感列的列也是明文;超级管理员在免脱敏白名单里同样是明文。要让某人看不到某一列,必须在分享那一刻给这一列配规则(第三章 3.7.4),或把这一列登记为敏感列走兜底(3.7.2)。真正的异常只有一种:配了规则却仍是明文。另外,字段级脱敏只管中台自建数仓,外部数据源不归它管。
Q:能不能按"行"限制某个用户只看到部分数据?当前版本不能。 授权数据结构上留了"行级过滤条件"这个字段,但页面上没有入口、执行侧也没有任何消费方——写进去既不生效也不报错,请勿依赖。替代做法:用列级脱敏遮内容,或把该看的行加工成一张独立的表再分享(见第三章 3.7.2 末尾)。
Q:改了角色 / 权限没生效? 权限有一层缓存(有效期较长)。裸改库后需要清对应用户的权限缓存并重新登录。
Q:出分小数为什么有时很长? 已统一为最多 6 位小数(在模型出参处 round,消除浮点长尾与科学计数法);看到更长的是老数据或没走新出口。
Q:加了新菜单是不是要人人手工授权? 不用。管理员菜单由套餐自动下发,模块全权角色跟着模块走,只有细粒度职责才单独配。
几个高频报错的速判:
注意:
- 401「缺少应用标识」= 只给了 key、没给
X-APP-ID(两个头都要带)。- 404 多半是上游没发布,或 path/method 不一致。
- 5xx 多半是上游协议 / 端口 / Host 配错。
- run-api「网关调用凭证校验失败」= 路由 headerRules 没注入
X-Gateway-Auth。
5.15 附录:环境与端口(开发环境实测)
三个"网关"最容易混,端口对错就打不通。以下端口为开发环境实测,生产以运维《运行说明》为准。
网关与核心服务端口:
| 角色 / 服务 | 端口 | 职责 |
|---|---|---|
| 平台网关 | 38080 | 内部前缀转发(去一段前缀到各服务),施工调内部 API 用它 |
| 对外消费网关(api-gateway) | 39302 | 只放已发布的业务路由,消费方调它(默认配置端口 9302,开发环境覆盖为 39302) |
| 网关后台(gateway-admin) | 39301 | 后台管理(路由 / 上游 / 应用 / 插件 / 密钥) |
| 内容服务 | 39101 | — |
| 模型运行时 | 39102 | — |
| 实验室 | 39103 | — |
| 决策核心 | 5000 | — |
| 工作流 | 38084 | 打分流 run / run-api 所在 |
| 数据管理 | 39315 | 建模的 model 在 /data-management/model/... |
| 数据治理执行器 | 39313 | — |
| 分析型数据源前端节点 | 9030 | 中台自建数仓(列式存储,引擎级按人脱敏依赖它,每用户一个账号) |
平台网关前缀(均去掉一段前缀后转发):/auth、/system、/datasource、/resource、/workflow、/scheduler、/content、/model、/lab、/gateway-admin、/data-management、/data-governance-executor。
网关配置库(独立于业务库):gw_route(路由)、gw_upstream(上游)、gw_app(应用)、gw_route_app(路由↔应用授权关系)、gw_api_access_log(访问日志)、gw_publish_record(发布记录)等。
注意:
/model有歧义:平台网关下/model/...走模型运行时(模型 execute),而建模用的 model 在/data-management/model/...,别混。- 对外消费网关默认端口在开发环境被覆盖为 39302。
- 具体地址随部署环境不同,一律以运维《运行说明》为准。
说明(能力边界,避免夸大): 数据网关当前是"动态网关可运行版"——8 个内置插件(IP 黑白名单、限流、请求加密 / 解密、响应加密 / 解密、熔断降级、访问日志)已打通管理 + 发布 + 运行,插件不可自定义;响应字段后处理(含自定义脚本,沙箱、单字段 300ms 超时)已在运行时执行,请求侧字段转换尚未接入。已知未实现或名不副实、配了不生效的点,配置前先看清:客户端的「额外参数」(5.2.1)、高级匹配的 Cookie / IP 来源(5.4.2)、访问协议里的 WebSocket(5.4.2)、Format 里的 hostname(5.4.3)、路由的插件「覆盖配置」(5.11.2)、密钥的「用途」与「状态」(5.11.3)、路由与上游上填的请求超时(5.4.2)。此外尚未落地的项包括:Mock、API 文档自动生成、发布回滚、熔断半开、运维接口鉴权、访问日志查询页与命中轨迹可视化、路由↔应用授权关系的产品化写入口(5.6)。规划中的能力不代表当前版本已提供。