分流的全部价值都建立在识别之上。本文说明识别要解决的具体问题、可用的判断信号、多进程应用带来的难点,以及为什么把判断放在本地反而能得到更好的隐私边界。
当系统上某个程序发起一个网络连接时,分流引擎需要回答一个问题:这个连接属于哪一类应用,应该走哪条路径。问题看似简单,实际包含三个层次的判断:这个连接是谁发起的(进程归属)、这个连接要去哪里(目标类型)、这个连接需要什么(路径取向)。
第一层决定能否把连接与应用对应起来;第二层决定能否区分同一应用内部的不同通道;第三层决定选哪条路。三层判断任何一层出错,都会导致该连接走错路径。
工程上常用的信号有四类,各有适用范围与局限。
进程归属是最直接的信号:通过连接发起方的进程标识,回溯到其所属的可执行文件。局限是它只能回答"谁发起的",回答不了"这条连接要做什么"。
目标端口与协议是辅助信号。实时音视频多用 UDP,文件同步多用 TCP 长连接,这类对应关系有统计规律,但并非绝对,不能单独作为判断依据。
目标域名特征适合区分服务类型。同一家服务商的视频分发域名与页面域名通常不同,按域名归属可以区分。局限是域名列表会随服务方调整而变化,纯白名单方式容易失效。
连接行为特征是时序信号:单连接累计流量大且持续时间长,更可能是媒体流;短时间大量并发小连接,更可能是包管理器或网页资源加载。这类信号不依赖预先维护的名单,但需要观察一段时间才能得出结论。
现代应用的进程结构越来越复杂,这是识别机制面临的最大挑战。浏览器会为每个标签页派生渲染进程,还会单独运行网络服务进程;开发工具会拉起后台索引与语言服务进程;游戏客户端会启动独立的反作弊与语音模块进程。
如果识别只按主程序的名称判断,这些辅助进程很可能被归为未知应用。更隐蔽的问题是:部分辅助进程产出的连接特征与主程序差异很大,即使识别到了进程,也可能因为按连接单独判断而被分到不同类别。
工程上的应对有两步。一是对同一可执行文件目录下的子进程做归并,把同一应用的所有进程视为一个整体。二是引入应用级的一致性原则:同一应用的多个通道可以有不同的路径取向,但不能出现"部分通道被加速、部分通道完全不走加速"这种断裂,因为这往往意味着识别逻辑出了问题。
识别需要读取进程信息与连接信息,这些数据都在客户端本地。把判断放在本地有两个直接好处。
第一是隐私边界清晰。客户端在本地比对规则、得出结论,只需要按结论选择路径;服务端看到的是某条路径上来了流量,而不知道这条流量要去哪里。判断不在服务端做,服务端就没有获取目标信息的必要。
第二是可解释性。用户可以在客户端看到某条连接命中了哪条规则、编号是多少,出现异常时能自行定位。如果判断在服务端完成,用户只能看到一个结果而看不到依据。
需要如实说明的是:本地判断不等于客户端不保留任何记录。为了帮助用户确认规则是否生效,客户端会记录规则命中次数与规则编号。区别在于这些记录描述的是规则本身的使用情况,而不是用户访问了什么。
任何识别机制都会遇到无法判断的情况:从未见过的新应用、使用了系统代理接口导致进程归属模糊的应用、使用非常规端口与私有协议的应用。这些情况不应被包装成"识别率百分之九十九"之类的数字,因为分母本身无法定义。
合理的做法是三层设计:能判断的自动判断;判断不了的走可解释的兜底策略;兜底也不满足需求的,提供手工补充规则的入口。这套组合比追求一个无法验证的识别率更有实用价值。