Day 10 / 共 20 天 · 第 2 周 SDK 与配置存储
一次操作的端到端流转
第 2 周收官。今天把前面所有零件串成一条链:点"新建路由"后,请求怎么穿过 Controller→Service→Converter→K8s,最终变成一个带 higress.io/destination 注解的 Ingress。这是全课的"任督二脉"。
📍 你在整门课的位置(第 2 周 收官 · 四层串成一条链)
D01 全景→
D02 启动→
D03 分层→
D04 REST→
D05 横切→
D06 SDK→
D07 存储→
D08 客户端→
D09 模型→
D10 流转
💡 一句话兜住今天(一次操作 = 一次点外卖)
前 9 天的零件今天串成一条链——就像点外卖的全流程:你在 App 下单(前端
POST)→ 前台接单核对(Controller 守门)→ 后厨备餐、还配上小料(Service 编排:建 Ingress 顺带写鉴权)→ 翻译成出餐单(Converter 把 Route 变成带注解的 Ingress)→ 放进取餐柜(写 K8s)。Console 的活到"放进取餐柜"就结束,之后是上一站 Higress 骑手把餐送到 Envoy。今天用第一人称把这条链走一遍,你就打通了全课的"任督二脉"。L01
追一条路由的诞生
1
Controller:
POST /v1/routes → RoutesController.add 校验入参2
Bean 注入:
routeService 来自 SdkConfig.routeService()(Day 02)3
Service:
RouteServiceImpl.add 转换 + 编排4
Converter:
route2Ingress 把 Route 变成 V1Ingress + 注解5
K8s:
createIngress → NetworkingV1Api.createNamespacedIngress下面 L02-L06 逐段拆这条链的每一步。
🧍 现在你是那个 POST 请求,第一人称走完全程
你带着 body
{name:"demo", domains:["example.com"], services:[{svc-a,80},{svc-b,20}]} 撞进后端 →
① 前台(Controller)核对:名字非空 ✔、不带 .internal ✔、validate() 权重和=100 ✔,放行 →
② 后厨(Service)接手:先叫翻译官把你转成 Ingress,写进 K8s,再顺手写一份鉴权资源 →
③ 翻译官(Converter)把你身上的高级能力全编码成 higress.io/* 注解,后端目的地写进 destination 注解 →
④ 取餐柜(K8s):一个带注解的 Ingress 落库。到这里你就"变成了一份 K8s 档案",Console 的活结束。| 步 | 在哪一层 | 这一刻发生什么 | 产物 / 关键调用 |
|---|---|---|---|
| ① | Controller | 三步校验 + 委派 | routeService.add(route) |
| ② | Service | 转换 + 编排(建 Ingress + 写鉴权) | RouteServiceImpl.add :121 |
| ③ | Converter | Route → V1Ingress + 注解 | route2Ingress :231 |
| ④ | K8s | 打标签 + 补 ingressClass + 写入 | createNamespacedIngress |
图注:全课"任督二脉"——记住这条链,任何一个功能都能定位到它在哪一层展开。
L02
① Controller 守门
RoutesController.java:77-86(Day 04 见过):校验名字非空、拒绝 .internal 后缀、route.validate(),再 routeService.add(route)。
读法:控制器不碰业务,只"守门 + 转交"。它拿到的
routeService 是 Day 02 SdkConfig 把 SDK 造的对象注册成的 Bean。L03
② Service 编排
RouteServiceImpl.java:121-136:
public Route add(Route route) {
V1Ingress ingress = kubernetesModelConverter.route2Ingress(route); // :123 转换
V1Ingress newIngress;
try {
newIngress = kubernetesClientService.createIngress(ingress); // :126 写 K8s
} catch (ApiException e) {
if (e.getCode() == 409) throw new ResourceConflictException(); // 撞名 → 409
throw new BusinessException(...);
}
writeAuthConfigResources(route, ...); // :134 顺带写鉴权(allow list)
return kubernetesModelConverter.ingress2Route(newIngress); // 回读返回
}
为什么创建路由还要"顺带写鉴权"?
如果这条路由配了鉴权(只允许某些消费者访问),鉴权信息不是存在 Ingress 里,而是存在一个"ROUTE 作用域的 key-auth 插件实例"的 allow list 里(第 3 周 Day 13 详讲)。所以
add 除了建 Ingress,还要 writeAuthConfigResources 同步鉴权资源——这就是 Service 层"一个操作牵连多个资源"的编排价值。L04
③ Converter 转换
KubernetesModelConverter.route2Ingress(:231-241)依次填充:
fillIngressMetadata(...) // 名字、标签(域名→higress.io/domain_xxx 标签)
fillIngressSpec(...) // path 规则、backend
fillIngressCors(...) // CORS 注解
fillIngressAnnotations(...) // 其余高级能力注解 + nginx 注解兼容
fillIngressLabels(...)
读法:Higress 的路由高级能力(CORS、重写、限流、header 控制…)全部编码成 Ingress 的 annotation。原生 Ingress 只能表达"路径→服务",Higress 用注解把它扩展成一个功能完整的路由。这个转换器就是那套注解约定的实现(2102 行,Higress 教程里的注解体系在这落地)。
L05
destination 注解(最关键的一个)
fillIngressDestination(:1647-1680)把 route.services 拼成 higress.io/destination 注解:单服务写 name:port,多服务每行写 <weight>% name:port version。而 Ingress 的 backend 固定指向 McpBridge 引用(DEFAULT_MCP_BRIDGE_BACKEND)。
📝 举个例子:输入的 services 数组 → 输出的 destination 注解
输入
services:[{name:"svc-a",port:8080,weight:80},{name:"svc-b",port:8080,weight:20}](Day 09 的模型)
→ fillIngressDestination 逐行拼 → 输出下面这段注解(多服务每行 <weight>% host:port)。你在界面拖的那个"80/20 灰度"滑块,最终就凝固成这几行文本。metadata:
annotations:
higress.io/destination: |
80% svc-a.default.svc.cluster.local:8080
20% svc-b.default.svc.cluster.local:8080
labels:
higress.io/domain_example.com: "true"
higress.io/resource-definer: higress
为什么真正的后端写在注解里,而不是 Ingress 的 backend?
原生 Ingress 的 backend 只能指一个 K8s Service。但 Higress 要支持多后端加权、指向注册中心的服务、指向 LLM 上游……这些原生 backend 表达不了。于是 Higress 把 backend 固定指向一个"占位"引用,真正的目的地写进
higress.io/destination 注解。这正是 Higress 相对原生 Ingress 的核心设计——你在上一站 Higress 教程见过控制器怎么读这个注解。L06
④ 写 K8s
createIngress(:386-391):renderDefaultMetadata(打 resource-definer 标签)→ fillDefaultIngressClass → new NetworkingV1Api(client).createNamespacedIngress(...)。
到这一步,Console 的工作就结束了——一个带注解的 Ingress 已经写进 K8s。接下来是上一站 Higress 控制器的活:它 watch 到这个 Ingress → 翻译成 Istio 的 VirtualService/Gateway → 经 MCP-over-xDS 喂给 istiod → istiod 生成 xDS 给 Envoy → 流量生效。
L07
update / delete 对称
- update(
:138-154):走replaceIngress(replaceNamespacedIngress,靠 resourceVersion 乐观锁)。 - delete(
:156-165):走deleteIngress,并wasmPluginInstanceService.deleteAll(ROUTE, name)清理该路由的插件实例。
读法:delete 的"级联清理"很重要——删路由时把它挂的插件实例一起删掉,否则会留下孤儿配置。create/update/delete 三者对称,都遵循"改 Ingress + 同步关联资源"的模式。
L08
今日小结 + 动手(第 2 周收官)
🧠 第 2 周你应该能回答
- "创建路由"的四层链路(Controller→Service→Converter→K8s)?
- 为什么 add 要顺带
writeAuthConfigResources? - 路由的高级能力为什么全编码成注解?
higress.io/destination注解为什么存后端而不用 Ingress backend?- delete 为什么要级联清理插件实例?
✋ 动手
cd /Users/bitmart/work/codes/github/higress-group/higress-console/backend/sdk/src/main/java/com/alibaba/higress/sdk
sed -n '121,165p' service/RouteServiceImpl.java
sed -n '231,241p' service/kubernetes/KubernetesModelConverter.java
sed -n '1647,1680p' service/kubernetes/KubernetesModelConverter.java
下周预告 · 第 3 周:AI / 插件 / 消费者——这是 Console 最精彩的部分:一条 AI 路由如何"一对多"展开成 Ingress + 多个插件 + EnvoyFilter,LLM Provider 如何变成 ai-proxy 配置,消费者鉴权、Wasm 插件、服务来源、MCP 怎么管理。