第一章:Go语言ETL流水线设计(企业级日志分析系统落地全链路拆解)
现代企业日志分析系统需应对TB级日志吞吐、亚秒级延迟与高可用性三重挑战。Go语言凭借其轻量协程、零成本抽象及静态编译特性,成为构建高性能ETL流水线的理想选择。本章聚焦真实生产环境——某云原生SaaS平台日志分析系统,日均处理120亿条Nginx/K8s/应用混合日志,端到端延迟稳定控制在800ms以内。
核心架构分层
- Extract层:基于
fsnotify监听日志目录增量文件,配合bufio.Scanner流式读取,避免内存暴涨;支持断点续传(通过记录inode+offset元数据至本地BoltDB) - Transform层:采用
go-fsm实现状态机驱动的字段解析,统一处理JSON、TSV、自定义协议日志;关键字段(如trace_id、http_status)经正则预编译缓存,提升37%解析吞吐 - Load层:双写策略——实时写入Kafka(分区键为
service_name保证时序),批量写入ClickHouse(每5秒或10MB触发flush)
关键代码片段
// 日志行解析器(含字段标准化与类型转换)
func ParseLogLine(line string) (map[string]interface{}, error) {
parts := strings.Fields(line)
if len(parts) < 6 { return nil, fmt.Errorf("invalid log format") }
// 预定义字段映射(生产环境使用sync.Map缓存正则)
fields := map[string]interface{}{
"timestamp": parseISO8601(parts[0]), // 转换为time.Time
"method": parts[2], // HTTP方法
"status": parseInt(parts[4]), // 状态码转int
"duration": parseFloat(parts[5]), // 响应时间转float64
}
return fields, nil
}
// 注:parseInt/parseFloat内置错误容忍(非法值返回0),避免单条脏数据阻塞流水线
容错与可观测性保障
| 组件 | 机制 | 生产效果 |
|---|---|---|
| 数据丢失防护 | 每个Worker维护本地WAL日志 | 故障恢复后零数据丢失 |
| 异常日志隔离 | 单独topic接收parse失败日志 | 便于离线诊断与重放 |
| 性能监控 | Prometheus暴露goroutines数/TPS | Grafana看板自动告警阈值 |
流水线通过pprof持续压测调优,GC暂停时间稳定低于1.2ms,CPU利用率峰值不超过65%,满足SLA 99.99%可用性要求。
第二章:日志采集与解析层架构实现
2.1 基于Go net/http与gRPC的多协议日志接入设计
为统一纳管异构日志源,系统构建双协议接入网关:HTTP用于轻量级设备(如IoT传感器)的JSON日志推送,gRPC用于高吞吐服务(如微服务边车)的结构化流式日志传输。
协议适配层设计
- HTTP端点
/v1/logs接收application/json请求,经中间件校验签名与限流 - gRPC服务
LogIngestor提供StreamLogs(LogEntry) returns (Ack)双向流接口
核心路由分发逻辑
func (s *Ingestor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Protocol") == "grpc" { // 兼容gRPC-Web透传
s.grpcGateway.ServeHTTP(w, r) // 复用gRPC网关
return
}
s.handleHTTPLog(w, r) // 标准HTTP日志处理
}
该逻辑实现协议透明路由:X-Protocol 头区分原始协议语义,避免重复解析;grpcGateway 复用 grpc-gateway 生成的反向代理,降低维护成本。
协议能力对比
| 特性 | HTTP/1.1 | gRPC/HTTP2 |
|---|---|---|
| 吞吐量 | ≤5k QPS | ≥50k QPS |
| 延迟 | 20–200ms | |
| 序列化 | JSON(文本) | Protocol Buffers |
graph TD
A[日志源] -->|HTTP POST| B{Ingestor Router}
A -->|gRPC Stream| B
B --> C[Protocol Decoder]
C --> D[统一LogEntry]
D --> E[异步写入Kafka]
2.2 高吞吐日志解析引擎:正则+AST语法树双模匹配实践
传统单一定制正则解析在面对嵌套结构(如 JSON 字段、多层引号包裹)时易出现回溯爆炸与语义丢失。我们构建双模协同引擎:轻量级正则快速定位日志片段,再交由 AST 语法树进行结构化校验与语义还原。
匹配策略分流机制
- 正则层:提取时间戳、IP、状态码等扁平字段(毫秒级响应)
- AST 层:对
message字段递归解析 JSON/XML/Key-Value 嵌套体,保留原始结构上下文
# 示例:AST 解析器核心逻辑(基于 Lark)
from lark import Lark
parser = Lark(r'''
?start: kv_pair+
kv_pair: KEY "=" STRING ";"
KEY: /[a-zA-Z_][a-zA-Z0-9_]*/
STRING: /"[^"]*"/
%ignore " "
''', parser='lalr')
逻辑说明:
parser='lalr'启用线性时间解析;%ignore " "忽略空格提升容错;?start支持零或多个键值对,适配日志中可选字段场景。
性能对比(万条/秒)
| 模式 | 吞吐量 | CPU 占用 | 结构还原准确率 |
|---|---|---|---|
| 纯正则 | 42k | 89% | 63% |
| 正则+AST双模 | 38k | 71% | 99.2% |
graph TD
A[原始日志流] --> B{正则预筛}
B -->|命中模式| C[提取基础字段]
B -->|含复杂message| D[AST语法分析]
C & D --> E[统一Schema输出]
2.3 结构化Schema动态推导与字段类型自动识别算法
面对多源异构数据(如CSV、JSON、日志流),传统硬编码Schema严重制约ETL灵活性。本算法采用两阶段推导机制:先基于采样数据进行统计型类型初判,再结合上下文语义规则校准。
类型推断核心逻辑
def infer_dtype(sample_values: list) -> str:
if not sample_values: return "string"
# 统计数值比例与格式一致性
numeric_ratio = sum(1 for v in sample_values if is_numeric(v)) / len(sample_values)
if numeric_ratio >= 0.95 and all(is_integer(v) for v in sample_values):
return "integer" # 整型需高置信度+全整数
elif numeric_ratio >= 0.8:
return "double"
elif all(is_iso_datetime(v) for v in sample_values):
return "timestamp"
return "string"
逻辑分析:
sample_values为字段前N行(默认100)非空样本;is_numeric()支持科学计数法与负号;is_integer()排除小数点和指数符号;阈值0.95防止浮点误差干扰整型判定。
类型置信度映射表
| 推断类型 | 最低置信阈值 | 关键校验条件 |
|---|---|---|
integer |
0.95 | 全样本无小数点/指数符 |
double |
0.80 | 至少一个含小数点或e/E |
timestamp |
0.90 | ISO 8601 格式匹配且时区可析 |
推导流程概览
graph TD
A[原始字段样本] --> B[清洗:去空/去噪]
B --> C[统计特征提取]
C --> D{数值占比 ≥ 0.8?}
D -->|是| E[精度校验 → double/integer]
D -->|否| F[正则模式匹配 → timestamp/string]
E & F --> G[输出带置信度的Schema字段]
2.4 日志乱序、重复、断连场景下的幂等性处理与水位线机制
数据同步机制
在分布式日志采集(如 Kafka + Flink)中,网络抖动或消费者重启易引发事件乱序、重复投递或连接中断。此时仅靠消息队列的 at-least-once 语义无法保障端到端一致性。
幂等写入实现
采用「业务主键 + 版本号」双校验策略:
// 基于 MySQL 的幂等插入(ON DUPLICATE KEY UPDATE)
INSERT INTO orders (order_id, status, version, updated_at)
VALUES (?, ?, ?, NOW())
ON DUPLICATE KEY UPDATE
status = VALUES(status),
version = GREATEST(version, VALUES(version)),
updated_at = NOW();
逻辑分析:
order_id为唯一索引;GREATEST(version, VALUES(version))确保高版本覆盖低版本,抵御乱序;NOW()更新时间戳便于水位线对齐。参数version由上游按事件时间单调递增生成。
水位线协同设计
| 组件 | 作用 | 依赖来源 |
|---|---|---|
| Source Watermark | 基于事件时间生成延迟容忍水位 | Kafka record timestamp |
| Operator State | 缓存窗口内未确认事件 | RocksDB 后端状态 |
graph TD
A[Kafka Partition] -->|乱序/重发| B[Flink Source]
B --> C[WatermarkGenerator]
C --> D[KeyedProcessFunction]
D --> E{state.contains? order_id}
E -->|否| F[写入DB & 更新watermark]
E -->|是| G[比对version → 跳过或覆盖]
2.5 实时采样与降噪策略:基于滑动窗口的采样率自适应调控
在动态负载场景下,固定采样率易导致数据冗余或关键瞬态丢失。本节引入滑动窗口驱动的自适应采样机制,兼顾实时性与信噪比。
核心调控逻辑
采样率 $f_s$ 按窗口内信号方差 $\sigma^2_w$ 动态调整:
- $\sigma^2_w
- $0.01 \leq \sigma^2_w
- $\sigma^2_w \geq 0.5$ → 升频至峰值率(2 kHz),并触发中值滤波降噪
自适应采样伪代码
def adaptive_sample(buffer, window_size=64, fs_base=100):
variance = np.var(buffer[-window_size:]) # 计算滑动窗口方差
if variance >= 0.5:
return resample(buffer, fs_base, 2000) # 升频 + 插值重采样
elif variance >= 0.01:
return buffer # 原样输出(500 Hz)
else:
return decimate(buffer, q=5) # 降频:500→100 Hz
逻辑分析:
buffer[-window_size:]确保仅评估最新窗口;resample()使用FIR抗混叠插值,decimate()内置低通+下采样,避免频谱混叠;q=5对应5倍降频比,匹配标称到基础采样率映射。
降噪策略协同效果
| 窗口方差区间 | 采样率 | 主要降噪方式 | 适用场景 |
|---|---|---|---|
| 100 Hz | 移动平均(N=8) | 稳态低功耗监测 | |
| 0.01–0.5 | 500 Hz | IIR高斯滤波 | 常规工况 |
| ≥ 0.5 | 2 kHz | 中值滤波(k=5) | 冲击/阶跃事件捕获 |
graph TD
A[原始信号流] --> B[滑动窗口计算σ²]
B --> C{σ² < 0.01?}
C -->|是| D[100 Hz + MA]
C -->|否| E{σ² < 0.5?}
E -->|是| F[500 Hz + IIR]
E -->|否| G[2 kHz + Median]
第三章:数据转换与富化核心逻辑
3.1 Go泛型驱动的可插拔式转换规则引擎设计与DSL实现
核心抽象:泛型规则接口
type Rule[T any, R any] interface {
Apply(input T) (R, error)
Validate() error
}
T为输入类型,R为输出类型;Apply执行类型安全的转换逻辑,Validate保障规则配置合法性。泛型约束使单个引擎可统一调度 string→int、map[string]any→User 等异构规则。
DSL语法示意
| 关键字 | 含义 | 示例 |
|---|---|---|
when |
条件表达式 | when .status == "active" |
then |
转换动作 | then .id * 1000 |
as |
输出类型声明 | as int64 |
规则注册与执行流程
graph TD
A[DSL文本] --> B[Parser解析为AST]
B --> C[泛型Rule实例化]
C --> D[Registry中心注册]
D --> E[Runtime按需调用Apply]
引擎通过 Registry.Register[User, UserProfile]() 实现零反射、强类型插拔。
3.2 地理位置/IP归属地/UA解析等富化服务的并发安全封装
富化服务需在高并发下保障线程安全与资源隔离,避免共享缓存污染或解析器状态竞争。
并发控制策略
- 使用
sync.Pool复用解析器实例(如geoip.CityReader),规避频繁初始化开销 - 对 IP 归属地查询采用读写锁(
RWMutex)保护本地缓存 - UA 解析器(如
uap-go)通过context.WithTimeout控制单次解析上限(≤50ms)
安全封装示例
type Enricher struct {
cache *lru.Cache
mu sync.RWMutex
pool sync.Pool // *user_agent.Parser
}
func (e *Enricher) ParseUA(uaStr string) *UserAgent {
p := e.pool.Get().(*user_agent.Parser)
defer e.pool.Put(p)
return p.Parse(uaStr) // 无状态调用,线程安全
}
sync.Pool避免 GC 压力;Parse()是纯函数式接口,不修改Parser内部状态,故可复用。
性能对比(QPS@16核)
| 方式 | QPS | P99延迟 |
|---|---|---|
| 全局单例 Parser | 8,200 | 124ms |
| Pool + 每请求新实例 | 11,500 | 41ms |
graph TD
A[HTTP Request] --> B{Enricher.Parse}
B --> C[Get from sync.Pool]
C --> D[Parse UA/IP/Geo]
D --> E[Return result + Put back]
3.3 基于context与errgroup的日志批次转换超时与熔断控制
在高吞吐日志处理链路中,单次批次转换(如 JSON → Protobuf)可能因序列化异常、字段爆炸或 GC 暂停而阻塞。单纯依赖 time.AfterFunc 难以协同取消,需结合 context.Context 的传播性与 errgroup.Group 的并发错误聚合能力。
超时控制:Context WithTimeout 封装
ctx, cancel := context.WithTimeout(parentCtx, 500*time.Millisecond)
defer cancel()
// 批量转换任务注入 ctx,任一子 goroutine 超时即触发整体 cancel
WithTimeout创建可取消上下文,超时后自动调用cancel(),所有监听该 ctx 的 goroutine(如json.UnmarshalContext)将收到ctx.Err()并快速退出,避免资源滞留。
熔断协同:errgroup.Group 管理并发批次
g, gCtx := errgroup.WithContext(ctx)
for i := range batch {
idx := i
g.Go(func() error {
return convertOne(batch[idx], gCtx) // 显式传入子 ctx
})
}
if err := g.Wait(); err != nil {
return fmt.Errorf("batch convert failed: %w", err)
}
errgroup在任意子任务返回非-nil error 或父 ctx 被取消时立即终止其余任务,并聚合首个错误。gCtx继承超时信号,实现“超时即熔断”的语义一致性。
| 控制维度 | 机制 | 效果 |
|---|---|---|
| 超时 | context.WithTimeout |
单批次整体截止时间约束 |
| 熔断 | errgroup.Wait() |
首错/超时即中止剩余任务 |
| 可观测性 | ctx.Err() 类型判断 |
区分 context.DeadlineExceeded 与业务错误 |
graph TD
A[Start Batch Convert] --> B{ctx.Done?}
B -->|Yes| C[Cancel all workers]
B -->|No| D[Launch N convert tasks via errgroup]
D --> E[One fails or times out?]
E -->|Yes| C
E -->|No| F[All succeed → return result]
第四章:分布式调度与可靠传输保障
4.1 基于Redis Streams + Go Worker Pool的轻量级任务分发模型
传统轮询或长轮询在高并发任务分发中易造成连接开销与延迟抖动。Redis Streams 提供了天然的持久化、多消费者组、消息确认(XACK)与失败重投能力,结合 Go 原生 goroutine 池,可构建低延迟、可伸缩的任务管道。
核心架构优势
- 消息不丢失:Stream 持久化 +
AUTOCLAIM自动故障转移 - 负载均衡:Consumer Group 中各 worker 独立
XREADGROUP,Redis 自动分片派发 - 弹性伸缩:Worker Pool 复用 goroutine,避免频繁启停开销
工作流示意
graph TD
A[Producer: XADD task:stream] --> B[Redis Stream]
B --> C{Consumer Group}
C --> D[Worker-1: XREADGROUP]
C --> E[Worker-2: XREADGROUP]
D --> F[XACK upon success]
E --> F
Go Worker Pool 核心片段
func NewWorkerPool(stream, group, consumer string, concurrency int) *WorkerPool {
pool := &WorkerPool{
client: redis.NewClient(&redis.Options{Addr: "localhost:6379"}),
stream: stream,
group: group,
worker: make(chan *redis.XStream, 1024), // 预分配缓冲通道
}
for i := 0; i < concurrency; i++ {
go pool.workerLoop(consumer) // 并发消费,共享同一 consumer 名
}
return pool
}
workerLoop 中调用 XREADGROUP BLOCK 5000 COUNT 10 STREAMS task:stream >,> 表示只读取未分配消息;BLOCK 5000 避免空轮询,COUNT 10 批量拉取提升吞吐。通道缓冲区 1024 平衡内存占用与背压响应速度。
| 组件 | 关键参数 | 说明 |
|---|---|---|
| Redis Stream | MAXLEN ~10000 |
防止无限增长,保留近期任务 |
| Consumer Group | NOACK=false |
启用手动 ACK,保障至少一次语义 |
| Go Worker | concurrency=8 |
匹配 CPU 核数,避免调度争抢 |
4.2 Exactly-Once语义实现:Kafka事务生产者与Checkpoint持久化协同
数据同步机制
Flink Kafka Producer 通过两阶段提交(2PC)协调事务生命周期:预提交(prepareCommit)将 offset 写入 checkpoint,真正提交(commitTransaction)在 Kafka 中标记事务完成。
核心配置对齐
确保以下参数严格一致,否则 EOS 保障失效:
enable.idempotence=true(幂等性基础)isolation.level=read_committed(消费者端隔离)transaction.timeout.ms ≤ checkpoint interval(防事务超时回滚)
事务生命周期协同流程
env.enableCheckpointing(5000);
FlinkKafkaProducer<String> producer = new FlinkKafkaProducer<>(
"topic",
new SimpleStringSchema(),
props,
Optional.of(new FlinkKafkaPartitioner<String>() { /* ... */ })
);
producer.setSemantic(Semantic.EXACTLY_ONCE); // 启用EOS
该配置使 Flink 在每次 checkpoint 触发时调用
snapshotState(),将当前 Kafka transaction ID 与待写入 offset 封装进 checkpoint state;恢复时通过restoreState()重建事务上下文,避免重复或丢失。
状态一致性保障
| 阶段 | Kafka 动作 | Checkpoint 状态 |
|---|---|---|
| 预提交 | 写入未提交消息 | 持久化 offset + transaction ID |
| 完成 checkpoint | 提交 Kafka 事务 | 标记 checkpoint 为 completed |
| 故障恢复 | 中止旧事务,启新事务 | 加载最新 completed checkpoint |
graph TD
A[Checkpoint Trigger] --> B[Producer.snapshotState]
B --> C[Write offset + txnID to state backend]
C --> D[Notify JobManager success]
D --> E[JobManager commit checkpoint]
E --> F[KafkaProducer.commitTransaction]
4.3 失败消息的分级重试策略:指数退避+死信队列+人工干预通道
当消息消费失败时,粗暴重试会加剧系统抖动。需分层应对:
- 一级响应(自动重试):应用指数退避(
base_delay × 2^attempt),避免雪崩; - 二级响应(隔离观察):连续3次失败后路由至死信队列(DLQ),保留原始消息与错误上下文;
- 三级响应(人工介入):DLQ消息触发告警,并同步写入工单系统,提供“一键重投”与“跳过”控制台。
def exponential_backoff(attempt: int) -> float:
"""计算第 attempt 次重试的等待秒数(单位:秒)"""
base = 1.0 # 基础延迟(秒)
cap = 60.0 # 最大延迟上限
return min(base * (2 ** attempt), cap)
逻辑分析:attempt=0 首次失败后立即重试(1s),attempt=5 时延为32s,attempt≥6 后恒定60s,防止无限拉长处理周期。
| 重试阶段 | 触发条件 | 动作 | 责任主体 |
|---|---|---|---|
| 自动重试 | 1–3 次失败 | 指数退避 + 原队列重入 | 消费者服务 |
| 死信归档 | 第4次失败 | 消息投递至 dlq.topic | Broker |
| 人工处置 | DLQ积压 > 5 条 | 企业微信告警 + 工单创建 | 运维平台 |
graph TD
A[消息消费失败] --> B{尝试次数 ≤ 3?}
B -->|是| C[指数退避后重投]
B -->|否| D[转发至死信队列DLQ]
D --> E[触发告警 & 创建工单]
E --> F[运维人员在控制台干预]
4.4 ETL流水线可观测性:OpenTelemetry集成与关键指标埋点规范
ETL流水线的稳定性高度依赖实时、一致的可观测能力。OpenTelemetry(OTel)作为云原生观测标准,为数据管道提供了统一的遥测采集框架。
数据同步机制
在Flink或Airflow任务中注入OTel SDK,自动捕获Span生命周期与自定义指标:
from opentelemetry import trace, metrics
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
# 初始化指标提供器(每30秒推送到后端)
exporter = OTLPMetricExporter(endpoint="http://otel-collector:4318/v1/metrics")
reader = PeriodicExportingMetricReader(exporter, export_interval_millis=30_000)
provider = MeterProvider(metric_readers=[reader])
metrics.set_meter_provider(provider)
meter = metrics.get_meter("etl-pipeline")
records_processed = meter.create_counter(
"etl.records.processed",
description="Total number of records successfully processed per task",
unit="1"
)
逻辑分析:该代码初始化了基于HTTP协议的OTLP指标导出器,
export_interval_millis=30_000确保低频但稳定的指标上报,避免对高吞吐ETL任务造成GC压力;etl.records.processed遵循OpenTelemetry语义约定,支持跨系统聚合。
关键埋点规范
| 指标名 | 类型 | 标签(Labels) | 业务意义 |
|---|---|---|---|
etl.task.duration |
Histogram | task_name, status |
端到端执行耗时分布 |
etl.records.failed |
Counter | task_name, error_type |
失败记录数,驱动重试策略 |
流水线追踪上下文传播
graph TD
A[Source Kafka] -->|inject traceparent| B[Spark Streaming]
B --> C{Transform Logic}
C -->|propagate context| D[Sink Delta Lake]
D --> E[OTel Collector]
E --> F[Prometheus + Grafana]
第五章:总结与展望
核心技术栈落地成效复盘
在某省级政务云迁移项目中,基于本系列所实践的 GitOps 流水线(Argo CD + Flux v2 + Kustomize)实现了 93% 的配置变更自动同步成功率。生产环境集群平均配置漂移修复时长从人工干预的 47 分钟压缩至 92 秒,CI/CD 流水线平均构建耗时稳定在 3.2 分钟以内(见下表)。该方案已支撑 17 个业务系统、日均 216 次部署操作,零配置回滚事故持续运行 287 天。
| 指标项 | 迁移前 | 迁移后 | 提升幅度 |
|---|---|---|---|
| 配置变更平均生效延迟 | 28.5 min | 1.5 min | ↓94.7% |
| 环境一致性达标率 | 61% | 99.2% | ↑38.2pp |
| 安全策略自动注入覆盖率 | 0% | 100% | — |
生产级可观测性闭环验证
在金融风控中台集群中,通过 OpenTelemetry Collector 统一采集指标、日志、链路三类数据,接入 Grafana Loki + Tempo + Prometheus 构建的统一观测平台。当某次 Kafka 消费延迟突增时,平台在 14 秒内完成根因定位:consumer-group-3 在 topic-fraud-events 分区 12 上因 GC 导致心跳超时被踢出组。运维人员依据自动关联的 JVM 堆内存监控图谱(含 G1GC 日志解析结果)立即扩容 Pod 内存配额,故障恢复耗时 3 分 17 秒,较历史平均缩短 6.8 倍。
# 实际生效的 SLO 监控规则片段(Prometheus Rule)
- alert: HighKafkaConsumerLag
expr: max_over_time(kafka_consumer_group_lag{group=~"consumer-group-.*"}[5m]) > 10000
for: 2m
labels:
severity: critical
annotations:
summary: "High consumer lag detected in {{ $labels.group }}"
边缘场景适配挑战与突破
针对某智能工厂 AGV 调度边缘节点(ARM64 + 512MB RAM),传统 Istio Sidecar 因内存占用超标无法部署。团队采用 eBPF 替代方案:使用 Cilium 的 HostServices 功能实现服务发现,并通过 bpftool 编译轻量级 L7 过滤程序嵌入内核。实测内存占用仅 18MB,且支持 TLS 1.3 握手拦截与 JWT token 校验。该模块已在 3 类工业网关设备上完成灰度验证,累计处理 2300 万次调度请求,P99 延迟稳定在 8.3ms。
下一代基础设施演进路径
未来 12 个月将重点推进两项工程:一是基于 WebAssembly System Interface(WASI)构建无依赖函数沙箱,已在 CI 流水线中替代 Python 脚本执行安全扫描任务,启动时间从 1.2s 降至 18ms;二是试点 Service Mesh 数据平面与 eBPF XDP 层直通,在裸金属集群中实现 0.3μs 级别网络策略匹配延迟。Mermaid 图展示当前混合架构中流量走向:
graph LR
A[IoT 设备] -->|MQTT over TLS| B(Cilium XDP)
B --> C[Envoy Proxy]
C --> D[WebAssembly Filter]
D --> E[业务容器]
E --> F[(TiDB Cluster)]
F -->|gRPC+QUIC| G[AI 推理服务] 