一部桌面话机连着两条线路,上面一条亮着通到对面,下面一条在半路断开,断口上夹着一张写地址的纸条

一、注册成功只说明四格灯里亮了第一格

把通话服务部署到带安全组的云上主机之后,最容易被误读的读数就是“注册成功”。终端向服务侧报到、服务侧回了一个肯定应答,这只证明信令面在某个方向走得通,既不证明会话能建立,更不证明双方的声音能真正对流。现场最常见的形态就是这样:状态页写着已注册,用户拿起话筒,听筒里安静得像没有人。

可用的判定要把一条链路拆成四个各自独立的健康检查项,每一项只回答自己的问题。

检查项 回答的问题 观测对象 注册状态能否覆盖
注册或握手成功 终端能否被服务侧认出并维持在线 协议交互的应答与刷新维持 本身即此项
呼叫建立成功 双方能否协商出一致的媒体参数 协商结果里双方声明的地址与端口 不能
媒体流双向可达 两个方向是否都有实际媒体包在流动 媒体端口上的收发计数 不能
可懂度达标 听到的内容是否连贯清楚 丢包、抖动与缓冲深度的统计 不能

多数监控只做了第一格。原因很实在:注册状态是服务侧自己维护的一个字段,读一次就得到一个是或否,成本最低。后面三格都要跨出服务边界去测量,成本高一个档位,于是长期被省略。省略的代价就是监控全绿而客户说没声音。

我们的做法是把后三格写成各自能独立失败的探测任务。呼叫建立用一对真实终端定时做一场空会话,只验证协商能否落定;媒体可达在媒体端口上统计双向流量,单向有数就判不达;可懂度用链路侧的丢包与抖动读数近似,超阈值就记一次劣化。任何一格单独变红都要能报警,而不是只在注册掉线时才响。这四格也顺便定义了“通”这个字在该被怎样使用:不写“线路已通”,写清通到了第几格。

二、一致性缺口:中间设备改了头部,没改内容

根子不在通话服务本身,而在它前面那层做地址转换的中间设备。这类设备的职责是把报文外层头部的地址换掉,让包能在公网与内网之间流转;它不去读也不去改报文携带的内容。而会话描述协议恰恰把媒体地址写在内容里。于是被改写的那一层和被保留的那一层各说各话:外层已经是可达地址,内容里还写着主机自己看到的内网地址。对端照内容里的地址去发包,包落在没人应答的地方,注册与呼叫建立一切正常,媒体永远不通。

识别这类问题只有一个动作,就是抓包,把外层头部声明的地址与载荷里写的地址放在一起比对,判断两者是否同义。一致就排除,不一致就正中要害。这个判据不限于通话服务。凡是把自己的可达地址写在内容里告诉别人的服务,都有同一个缺口:消息中间件回给客户端的连接地址、对象存储回给调用方的访问地址、数据库代理回给应用的连接串,形态不同,机理一致。我们把它写进这类服务的首查项清单,排在放行规则与证书有效期之前,理由是它一次抓包就能定论,而规则和证书往往要试好几轮才知道无关。

值得强调的是这个缺口的责任归属。它不是配置写错了,也不是代码有缺陷,而是“哪一层负责改写地址”这件事没有明确的主人。地址转换设备认为自己在做该做的事,服务认为自己声明的就是自己。中间的缝隙没人认领,最后由终端用户用一段静音来承担。治理方式因此也是两层:一层是把对外声明显式化,另一层是把核验动作固定到发布流程里。

三、对外地址是两个参数,必须成对声明

第一个处置面是显式声明。服务需要被告说自己该对外说什么地址,而这个声明在两个面上是分开的:一个管信令,说明控制交互对外用哪个地址;一个管媒体,说明实时传输的媒体地址用哪个。两个参数分属两个面,各配一次,谁也不顶替谁。

最常见的半成品状态是只配了信令那一个。表现很有欺骗性:注册好了,呼叫能建立了,唯一的问题仍然是没有声音,因为媒体面还在对外广播自己的内网地址。从工程角度看这是半修复,不是修复。要算修好,得同时满足三件事:两个参数都写了对外地址的值;两个值出自同一份配置版本,而不是一个走环境变量、一个抄在文件里;改完之后从链路外部再抓一次包,确认载荷里那处地址确实已被换成声明的值。

只看进程启动日志不算核验通过。日志能证明配置被读进来了,证明不了读进来之后对外表达成什么。同理,从服务器自己身上测一遍也不算——自己看自己总能看见那个内网地址,判定必须发生在链路的另一端。我们还要求把这两个参数当作一对纳入变更评审:改了其中一个而没改另一个的变更单,按未完成处理,评审人不接受“先改一个看看”这种提交方式。

四、安全组按面放行,不要一次全开

第二个处置面是放行规则。信令端口与媒体端口不是一类东西:用途不同,前者做会话控制,后者承载实际媒体流;协议特性不同,前者是请求应答式的控制交互,后者是持续的小包流;范围形态也不同,媒体侧往往是一段端口区间而不是单个端口。把它们笼统开成一条规则,短期省事,代价出在下一次排障。

排障依赖连通性判据分层,而分层判据的前提是规则本身分层。如果所有端口都塞进同一个放开口径,一旦不通就无法回答“控制面被挡了还是媒体面被挡了”,只能退回全放开再逐一收窄的老路,等于把一次配置问题换成几天无法定责的问题。最小放行策略的写法是按面拆:信令端口一条规则,媒体端口区间一条规则,两条各自记录用途、申请人和变更人,变更时通知另一条链路的负责人。调试期确实需要放宽的,要写明回退动作,并在当天把回退结果补进变更记录,不允许把临时口子留在生产环境上过夜。

规则改完之后还要确认它挂在这台主机实际使用的那组网络接口上,而不是同名旧规则上。这个确认动作很轻,但它决定了后面的抓包结论能不能算数。

五、中继是兜底路径,不是默认路径

穿透失败的那部分流量需要一个去处,中继网关承担的就是这个角色:把收不进来的媒体包接住再转交对方,让一部分本来起不来的会话能起来。要算清楚的是它兜的只是失败的那一部分。接入之后,这部分流量的带宽要重算,多走一跳带来的时延要重算,并发连接占用的资源同样要重算。

两个极端都不是好选择。全部走中继,等于把直连本可以省下的开销永久背上,规模一大就压不住;完全不兜,等于接受一部分网络环境的用户必然打不通,而且打不通时系统连一句解释都给不出。选择判据落在三个读数上:对端网络类型的分布,也就是有多少比例的接入方处在无法直连的环境里;实际读到的穿透失败率;中继侧能接受的带宽与成本上限。是这三个读数决定兜底比例,而不是“把中继打开”这一句话解决问题。

弱网条件下的质量项也要分面归因,否则会去修错的那一层。抖动缓冲、丢包重传、码率自适应三项都作用在媒体面与传输面,改的是听感,与信令面的注册稳定性无关;反过来,注册反复掉线也不是缓冲参数能修好的。

症状 落在哪一层 先看的证据
通话断续、只有一个方向听得见 媒体面 该方向媒体包计数与缓冲深度
呼叫建立慢或偶尔起不来 信令面 控制交互的应答与重传次数
双方都建不起媒体 地址声明或放行规则 载荷地址与外层地址是否同义
接通后维持不久就掉 穿透维持与刷新节奏 地址绑定的维持情况与刷新交互

这张表在我们内部的使用方式是:先按症状定层,再按层取证据,取到证据才动手改。跳过定层直接改参数的修复,通常只是把故障挪到了一个更难复现的时间点。

六、交付要留三件证据,而不是留一句已修复

这类故障最容易留下扯皮。交付那天在现场试通了,回到客户自己的网络里又没声音,两边各执一词。解决办法是把判据固化成材料,三件是底线。

第一件是外部抓包里载荷地址的记录,说明修复之后服务对外声明的确实是可达地址,取数位置在链路另一端而不是主机自身。第二件是安全组规则清单连同它的生效时刻,按面列出,并标明调试期放宽的那条是否已经回退。第三件是两条路径的实测对照,直连与走中继各测一轮时延与丢包读数,写明各自适用在哪类对端环境上。

三件材料的共同点是都能由对方独立复算。它们对应我们对客户的一句口径:判断通话是否可用,看媒体面有没有真的双向流动,而不是看控制台上的注册字样。把这句写进验收条款,比写十句服务稳定可靠有用。洹拓在几个云上通话项目上吃过“注册状态即健康”的亏,此后把四格检查项与三件证据定为这类项目的固定交付物;只改一格配置就报完工的做法,迟早会以另一种形式的没有声音回到我们面前。