Please enable Javascript to view the contents

Agentgateway 实现分析 Part3 - Http Proxy

 ·  ☕ 3 分钟

book-cover-mockup

本文尝试分析 Agentgateway 中 Http Proxy 主流程的实现细节。让读者了解 Agentgateway 在 L7 层作为 Http Proxy 的工作原理和实现方式。Agentgateway 本质上是一个 HTTP Proxy ,只是在 HTTP 之上增加了对 AI(LLM/MCP/A2A) 状态化协议的支持。所以分析 HTTP Proxy 层的主流程,就是分析 Agentgateway 的主流程。

本文摘自我在编写的开源书 《Agentgateway 内幕》Http Proxy 主流程 一节的内容,经过整理和补充后发布。如想了解更多细节,请参阅该书。

下饭菜 🍚

2020 年开始的业余微信订阅号 Mark 的滿紙方糖言 ,最近关注数突破 1000 🎉。 而相同的知乎号 Mark Zhu 现在是 905 关注。什么? 写了 5 年 111 篇文章,才 1000 。如果我是个小编大概已经被炒几次了。不过对于一个枯燥的技术类博客,我觉得还算合格了。喜的是有人看,忧的是看的人多了, censorship 就得上来了。从有工作时业余写写,到现在失业时作为一个保持技术更新和个人状态的手段。到现在为止,写微信公众号/博客/开源书还未为我带来多少实利。Anyway, 就当一个 Hobby 吧。因为如果没有这些修行,大概在今年的各种 Unlucky 下,已经颓废了。

Agentgateway 介绍

Agentgateway 是一个开源且跨平台的数据平面,专为 AI agent 系统设计,能够在 agent、MCP 工具服务器与 LLM 提供者之间建立安全、可扩展、可维护的双向连接。它弥补了传统网关在处理 MCP/A2A 协议中存在的状态管理、长会话、异步消息、安全、可观测性、多租户等方面的不足,提供统一接入、协议升级、工具虚拟化、身份验证与权限控制、流量治理、指标与追踪等企业级能力,还支持 Kubernetes Gateway API、动态配置更新以及内嵌开发者自服务门户,帮助快速构建和扩展 agent 化 AI 环境。我认为现阶段的 agentgateway 更像一个 AI Agent 应用的 outbound bus(外部依赖总线) 。

Http Proxy 分析

Agentgateway 配置文件

本节分析的 Http Proxy 主流程,基于以下 Agentgateway 配置文件

https://github.com/labilezhu/pub-diy/blob/main/ai/agentgateway/ag-dev/devcontainer-config.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
config:  
  logging:
    level: debug
    fields:
      add:
        ... 
  adminAddr: "0.0.0.0:15000"  # Try specifying the full socket address

  tracing:
    otlpEndpoint: http://tracing:4317
    # otlpProtocol: http
    randomSampling: true
    clientSampling: true
    fields:
      add:
        span.name: '"openai.chat"'
        # openinference.span.kind: '"LLM"'
        llm.system: 'llm.provider'
        llm.params.temperature: 'llm.params.temperature'
        # By default, prompt and completions are not sent; enable them.
        request.headers: 'request.headers'
        request.body: 'request.body'
        request.response.body: 'response.body'

        llm.completion: 'llm.completion'
        llm.input_messages: 'flattenRecursive(llm.prompt.map(c, {"message": c}))'
        gen_ai.prompt: 'flattenRecursive(llm.prompt)'
        llm.output_messages: 'flattenRecursive(llm.completion.map(c, {"role":"assistant", "content": c}))'
binds:
- port: 3100
  listeners:
  - routes:
    - policies:
        urlRewrite:
          authority: #also known as “hostname”
            full: dashscope.aliyuncs.com
          # path:
          #   full: "/compatible-mode/v1"
        requestHeaderModifier:
          set:
            Host: "dashscope.aliyuncs.com" #force set header because "/compatible-mode/v1/models: passthrough" auto set header to 'api.openai.com' by default
        backendTLS: {}
        backendAuth:
          key: "sk-abc"

      backends:
      - ai:
          name: qwen-plus
          hostOverride: dashscope.aliyuncs.com:443
          provider:
            openAI: 
              model: qwen-plus
          policies:
            ai:
              routes:
                /compatible-mode/v1/chat/completions: completions
                /compatible-mode/v1/models: passthrough
                "*": passthrough

- port: 3101
  listeners:
  - routes:
    - policies:
        cors:
          allowOrigins:
            - "*"
          allowHeaders:
            - mcp-protocol-version
            - content-type
            - cache-control
        requestHeaderModifier:
          add:
            Authorization: "Bearer abc"            
      backends:
      - mcp:
          targets:
          - name: home-assistant
            mcp:
              host: http://192.168.1.68:8123/api/mcp           

触发 LLM 请求

1
2
3
4
5
6
7
8
curl -v http://localhost:3100/compatible-mode/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "any-model-name",
    "messages": [
      {"role": "user", "content": "Hello!"}
    ]
  }'  

返回:

* Host localhost:3100 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:3100...
* Connected to localhost (::1) port 3100
* using HTTP/1.x
> POST /compatible-mode/v1/chat/completions HTTP/1.1
> Host: localhost:3100
> User-Agent: curl/8.14.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 104
> 
* upload completely sent off: 104 bytes
< HTTP/1.1 200 OK
< vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers, Accept-Encoding
< x-request-id: 120e5847-3394-923c-a494-8eb9f81cb36e
< x-dashscope-call-gateway: true
< content-type: application/json
< server: istio-envoy
< req-cost-time: 873
< req-arrive-time: 1766635349727
< resp-start-time: 1766635350601
< x-envoy-upstream-service-time: 873
< date: Thu, 25 Dec 2025 04:02:30 GMT
< transfer-encoding: chunked
< 
* Connection #0 to host localhost left intact
{"model":"qwen-plus","usage":{"prompt_tokens":10,"completion_tokens":20,"total_tokens":30,"prompt_tokens_details":{"cached_tokens":0}},"choices":[{"message":{"content":"Hello! ٩(◕‿◕。)۶ How can I assist you today?","role":"assistant"},"finish_reason":"stop","index":0,"logprobs":null}],"object":"chat.completion","created":1766635351,"system_fingerprint":null,"id":"chatcmpl-120e5847-3394-923c-a494-8eb9f81cb36e"}

Http Proxy 主流程图

1. L4 连接 accept 流程图

通过 vscode Debug ,可以看到 Http Proxy 主流程如下图所示:

图:Http Proxy 主流程

用 Draw.io 打开

图中带 ⚓ 图标,双击链接到本地 vscode 的源码。见书中的 源码导航图链接到 vscode 源码 一节

可见,主要的 http proxy 逻辑放在 Gateway 结构体中。其中有两个关键 spawn 点:

  1. Gateway::run() 中,为每个监听端口 spawn 一个 Gateway::run_bind() async future。这个任务负责监听端口,accept 新连接。
  2. Gateway::run_bind()accept 新连接后,每个连接 spawn 一个 Gateway::handle_tunnel() async future。 这个任务负责处理每个连接的所有事件。
  • 如果连接的 tunnel 协议是 Direct(即直接连接) ,就调用 Gateway::proxy_bind() 交由 HTTPProxy 模块处理 。

2. L7 HTTP 层流程

  1. Gateway::proxy() 调用 hyper-util 的 HTTP Server 模块,读取和解释 HTTP 请求头。解释完成后回调 到 HTTPProxy::proxy()

3. L8 AI Proxy Route 层

  1. HTTPProxy::proxy_internal() 执行各种 Policy 和 Route 。直到 HTTPProxy::attempt_upstream() 向 upstream(在当前配置下是 LLM AI Provider backend) 发起调用。

4. Upstream(backend) call

  1. HTTPProxy::make_backend_call() 调用 hyper-util 的 HTTP Client 模块,构建并发送 HTTP 请求到 upstream。其中有连接池的管理逻辑。
分享

Mark Zhu
作者
Mark Zhu
我在找工作 | I'm open to work