代理能不能把请求送到该去的地方,靠的是一张看不见的清单——连接表。它记着每条请求从哪来、要去哪、走了哪条出口,任何一条记录出错,请求就可能被送错方向,或者干脆失败。这张表怎么建、怎么更新、什么时候清掉,直接决定了代理在多并发下的表现,也解释了为什么有的转发时好时坏。
连接表里到底记了什么
连接表不是一张简单的名单,每个条目都包含一组互相绑定的信息。来源标识用来把响应交回给发起方;目标地址决定请求发往哪里;出口通道决定以什么身份出去;连接状态决定这条记录还能不能继续用;存活时间决定它什么时候被清理。五样东西缺一样,转发的某一环就可能对不上,轻则响应丢失,重则请求被送到错误的地址。
| 记录项 | 记的是什么 | 更新时机 | 出错时的影响 |
|---|---|---|---|
| 来源标识 | 发起请求的客户端地址与端口 | 连接建立时写入 | 响应无法交回发起方 |
| 目标地址 | 要访问的域名或地址 | 首次请求时登记 | 请求被送错目标 |
| 出口通道 | 本次转发走哪条出口 | 分配出口时写入 | 出口与预期身份不符 |
| 连接状态 | 空闲、传输中或已关闭 | 每次收发后更新 | 复用判断失误 |
| 存活时间 | 创建时间与最近活跃时间 | 每次活动后刷新 | 过期记录长期占用资源 |
维护是一个“写、查、清”的循环
写发生在两个时候:连接建立时写入来源与目标,分配出口后补上出口通道。查发生在每次转发前,代理要先在表里找到对应记录,确定这次请求该走哪条路;找不到就视为新连接,重新建立。清发生在连接结束之后,无论空闲超时、异常断开还是主动关闭,对应条目都要从表里移除,否则就变成残留记录,把后来的请求引向失效的出口。
多并发场景下,写、查、清三个动作是同时发生的。为了不互相干扰,连接表通常按目标或来源拆成多个分片,各自独立维护;同一分片内的读写用尽量轻量的方式串行化。这也是代理在高并发下仍能保持转发稳定的原因——争用被分散到了小粒度上,而不是所有请求挤在同一把锁后面排队。
分片
连接表按目标地址或来源范围切成多份,每份独立增删,多线程同时查表时不用互相排队。分片粒度越细,争用越小,但管理成本越高。看一个代理在重负载下稳不稳,观察它在并发查表时的表现基本就有数了。
老化
记录不会永久保留。每次活动会刷新存活时间,超过约定窗口还没被使用的条目由定时任务清理,异常断开、主动关闭的连接则立即移除。老化快慢直接影响复用率:清得太勤,连接反复重建、延迟上升;清得太慢,残留记录占用内存,还可能把请求引回早已失效的出口。
连接表在代理里只是一个小小的部件,却串起了来源、目标、出口三样东西。下次遇到请求时好时坏的情况,不妨先问一句:是不是表里的旧记录,把请求引到了早已失效的出口?把这个问题想清楚,很多转发异常都能在源头找到解释——代理IP链路里的这类现象,大多不是玄学,而是维护动作没跟上。
