ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

中转组呼业务统计模块:从数据采集到可视化实践

中转组呼业务统计模块:从数据采集到可视化实践 1. 项目背景与核心需求拆解做通信行业的人对“组呼”这个词肯定不会陌生。但在真正的现网运行环境里组呼并不是一个孤立的呼叫类型它牵扯到话务模型、资源调度、用户感知和计费稽核一整套链路。这次要分享的XNMS项目里的“业务统计-中转组呼”模块恰好就是这条链路上最关键的一环。先说清楚XNMS是什么。它不是某个标准化协议的名字而是我们在实际项目中搭建的一套网络管理系统Network Management System的内部代号。这套系统承载的是对交换网元、信令链路、话务路由和各类语音增值业务的统一管理与监控。而“中转组呼”业务简单理解就是多个成员在同一个逻辑组内其中任意一个成员发起呼叫系统自动将语音分发到组内其他所有成员完成一对多的集群通信。中转的意思是指呼叫通过交换平台进行媒体转发区别于直通模式下不经网络的脱网对讲。这个模块解决的核心问题可以从四个维度来理解。第一个维度是可视化管理。组呼业务跑在网元上过去要查“某一时刻到底有多少组呼呼叫、平均通话时长是多少、各组成员激活情况如何”只能登录网元命令行一条一条敲指令效率极低。XNMS把散落在各网元上的呼叫明细和统计计数集中采集上来统一呈现在业务统计界面里省掉了大量的登录操作。第二个维度是异常发现与故障定位。统计不只是看数字更要能从数字里看出问题。举个例子某个中转组呼的话务接通率突然从98%掉到70%通过时间粒度拆解和按组维度的聚合对比可以快速锁定是哪个组、哪个时段出现异常再结合信令跟踪定位到具体网元或中继电路。第三个维度是资源规划与容量评估。集群通信有一个特点——忙时话务极其集中。早高峰的调度呼叫、突发事件的应急指挥都会在短时间内把话务量顶到峰值。如果没有历史话务统计做支撑网络扩容就只能凭感觉要么浪费投资要么容量不足。业务统计模块输出的忙时话务模型数据是容量规划最直接的依据。第四个维度是运营分析与计费稽核。组呼虽然通常是包月或按通道计费但面向集团客户时经常需要提供详细的话务报表验证服务质量。统计模块生成的各类报表既是运营分析的工具也是和客户对账的依据。一句话总结这个模块的定位把网元里一组组冷冰冰的原始话单和计数器变成运维人员和运营人员看得懂、用得上的业务视图。这个项目的适用对象很明确主要是三类人通信系统运维工程师需要日常巡检、故障排查、话务分析网络规划与优化工程师需要利用话务统计数据做容量评估和组网调整产品经理和运营人员需要理解业务使用情况向客户输出服务报告。如果你正在做类似的网管系统、话务统计模块或者刚接触集群通信的中转组呼业务这篇文章应该能给你不少可以直接落地的参考。2. 数据模型与统计口径设计2.1 组呼业务的完整数据链路做业务统计第一步不是写代码而是搞清楚数据从哪来、长什么样、经过哪些处理节点。中转组呼的完整数据链路大致是这样的组呼终端发起呼叫请求后信令经过基站子系统、核心网交换中心最终到达中转台或者集群交换控制节点。交换节点完成呼叫鉴权、组成员寻址、媒体通道分配后建立主叫到组内所有被叫成员的语音通路。通话结束后交换节点生成CDRCall Detail Record呼叫详细记录记录里包含主叫号码、组号码、呼叫开始时间、结束时间、时长、释放原因、媒体资源占用等信息。同时网元内部还有一组性能计数器按照预设周期通常是15分钟或1小时累计各类事件次数比如组呼尝试次数、组呼接通次数、组呼失败次数等。XNMS的采集模块需要同时处理两类数据话单数据CDR每次呼叫一条记录信息粒度细可用于分析每次呼叫的行为特征性能统计数据Performance Data按时间周期聚合的计数数据量小适合做趋势分析和告警监控。这两类数据的统计口径完全不同。话单数据是“事后”的只有在呼叫结束并产生完整话单之后才能统计性能统计数据是“实时”的在每个统计周期结束时即可输出。在设计统计模块时必须把这两者分开处理否则会出现时间窗口错位的问题。2.2 核心统计字段与指标定义在建设这个模块时我首先梳理了现网实际的业务场景最终确定了以下核心统计字段字段名称类型说明组号码字符串逻辑组的唯一标识是统计聚合的主键之一主叫号码字符串发起组呼的成员号码呼叫起始时间时间戳呼叫建立的时刻呼叫结束时间时间戳呼叫释放的时刻通话时长整数结束时间减去起始时间单位秒接通状态枚举接通/未接通/失败/释放异常等组成员数量整数呼叫时组内的成员总数应答成员数量整数实际应答的成员数媒体资源标识字符串使用的媒体通道或中继电路标识释放原因枚举正常释放、超时释放、资源不足、信令错误等基于这些字段定义了几组核心指标第一组是呼叫量指标。组呼尝试次数指所有发起的组呼呼叫请求总数组呼接通次数指成功建立媒体通道并进入通话状态的呼叫数组呼接通率用接通次数除以尝试次数。这里有一个细节需要注意集群组呼的“接通”判定和普通点对点呼叫不一样。点对点呼叫只要被叫应答就算接通而组呼只要媒体通道建立成功、组内至少有部分成员能够接收到语音就算接通。因为有些成员可能处于关机、脱网或占线状态如果要求全部成员应答才判定接通那接通率永远达不到100%。第二组是时长指标。组呼平均通话时长用总通话时长除以接通次数组呼最长/最短通话时长用于识别异常呼叫。比如某组呼通话时长仅为1秒大概率是误呼叫或终端问题如果出现持续数小时不释放的组呼则可能存在媒体通道吊死的情况需要重点关注。第三组是资源与容量指标。最大并发组呼数统计在任意时间点同时存在的组呼数量峰值组呼资源占用率用并发组呼数除以系统可用的组呼媒体资源总数。这两个指标对容量规划至关重要。第四组是按维度的聚合指标。按组聚合看每个组的呼叫量、接通率趋势按时间段聚合看忙时/闲时分布按中继电路聚合看媒体资源的使用均衡度。2.3 统计口径的取舍与坑这里特别想聊一个容易被忽视的问题统计周期怎么选。性能统计的周期行业惯例一般是15分钟、1小时、1天三档。15分钟粒度能敏锐反映短时话务波动适合做实时告警和忙时分析1小时粒度适合做日常报表1天粒度适合做月度趋势对比。但有一个原则必须坚持多粒度数据必须同源。也就是说15分钟粒度的数据累加后必须能精确等于1小时粒度数据1小时粒度累加后必须能精确等于1天粒度数据。如果各粒度分别从原始话单/计数器中独立聚合中间任何一次数据源修正或迟延上报都会造成数据对不上最终导致报表逻辑混乱。我在项目里采用的方式是底层统一从原始话单和原始计数器中聚合出15分钟粒度数据存为“基础统计表”1小时和1天粒度的数据都从15分钟基础表中进一步聚合生成。这样既保证了同源又避免了多任务重复扫描原始数据的性能浪费。另外一个容易被坑的地方是时区与时间戳的处理。现网设备上报的时间戳有时是设备本地时间有时是UTC时间如果采集时不统一转换为系统标准时间跨时段聚合时会出现数据漂移。我在采集层做了统一转换全部转为东八区时间并标记时区信息后续所有聚合都基于统一时区杜绝了因时区差异导致的虚高或虚低。3. 统计系统架构与模块设计3.1 整体架构与数据流向业务的完整性和运维的便捷性是XNMS这一系列项目里反复强调的两个方向。业务统计-中转组呼模块在整体架构上我按“采集-解析-存储-聚合-展示”五个层次来设计。采集层负责对接网元。通过FTAM、SFTP、FTP或者厂商私有接口定期获取网元生成的原始话单文件和性能计数器文件。采集任务需要支持断点续传和文件完整性校验防止因网络抖动造成漏采。解析层负责把原始文件转换成结构化数据。这一步的工作量往往被低估。不同厂商的网元话单格式千差万别有的是固定长度二进制有的是XML有的是CSV还有的是自定义文本格式。解析程序必须针对每种格式编写适配器并加上字段长度校验、枚举值合法性校验脏数据直接进异常库。存储层负责数据落库。考虑到数据量中转组呼在中等规模网络下一天的话单量在几十万到数百万条之间性能统计文件则很小。我采用MySQL作为主存储并对核心表按天分区保证查询性能可控。聚合层是业务统计的核心逻辑所在。定时任务每隔15分钟拉取新增的原始数据完成从明细到统计表的聚合计算并输出轻度汇总结果。这里的关键设计是“增量计算”每次都只处理上一个批次之后新增的数据避免全量重算。展示层面向最终用户。提供实时统计面板、历史趋势查询、报表导出、异常标注等功能。这一层最考验易用性数据再准确如果交互不友好运维人员依然不愿意用。3.2 数据库表结构设计要点统计模块的表结构我主要分了三类第一类是明细表。存储解析后的原始话单记录表名类似t_cdr_transit_group_call。每条呼叫一条记录字段尽量按照原始话单的字段映射。这张表的数据量增长很快查询时必须带上分区键。实际上日常业务统计不直接查这张明细表而是查统计表只有需要下钻到单次呼叫时才使用。第二类是基础统计表。表名类似t_stat_group_call_15min。每条记录代表“某个组在某个15分钟内的统计汇总”。核心字段包括组号码、统计日期、统计时段、尝试次数、接通次数、接通率、总通话时长、最大并发数、失败原因分类计数等。这张表是后续所有报表的数据源也是数据量最大的一张统计表。每天288个时段假设有500个活跃组一天的记录数就是14.4万条一个月400多万条依然需要分区管理。第三类是日汇总表。表名类似t_stat_group_call_daily。按天聚合每个组每天一条记录主要字段是全天汇总的呼叫量、接通量、总时长、忙时时段等用于快速响应天级报表和月度趋势查询。字段设计的核心原则是冗余必要聚合结果。比如接通率虽然可以由接通次数除以尝试次数实时算出来但在统计表中直接冗余存储查询时可以少一次除法、少一个维度判断对大数据量的报表查询性能有明显帮助。同理忙时时段如“该组最忙的小时段”也在聚合时提前算好存入字段避免展示层临时计算。3.3 定时任务与调度策略定时任务是统计模块的心脏。调度设计不合理轻则统计延迟重则数据错乱。我最终采用了三层调度策略来保证稳定性第一层是采集触发任务。每5分钟扫描一次网元文件目录发现新的话单文件或计数文件立即触发解析任务。这个任务要负责文件下载、完整性校验、文件归档。下载失败或校验失败的文件会进入重试队列重试3次仍失败的走人工告警。第二层是增量聚合任务。每15分钟运行一次处理上一个15分钟窗口内已完成解析的明细数据聚合写入15分钟统计表。这个任务必须保证幂等即使因为异常重复执行也不能产生重复统计。我采用了“先删除后插入”的策略在写入前先删除统计日期统计批次内的旧数据再插入新计算结果既保证幂等又保证数据新鲜。第三层是天级汇总任务。每天凌晨1点运行汇总前一天全天数据写入日汇总表。选择凌晨1点是为了避开话音业务忙时通常忙时集中在上午9点到晚上10点同时留足前一日数据到达的时间余量。三层任务之间相互独立又通过数据状态表衔接。每批数据从“已采集”到“已解析”再到“已聚合”的状态流转都有记录出问题时可以直接定位到具体环节。4. 核心实现从SQL聚合到可视化展示4.1 15分钟基础聚合SQL示例聚合层最核心的操作是把明细数据按“组号码时间段”维度汇总。下面是一段在项目里实际使用的MySQL聚合SQL核心逻辑我给读者精简并加上了注释INSERT INTO t_stat_group_call_15min ( stat_date, stat_slot, group_number, attempt_count, connect_count, call_duration_sum, max_concurrent, fail_reason_json ) SELECT DATE_FORMAT(call_start_time, %Y-%m-%d) AS stat_date, FLOOR(TIMESTAMPDIFF(MINUTE, DATE_FORMAT(call_start_time, %Y-%m-%d 00:00:00), call_start_time) / 15) 1 AS stat_slot, group_number, COUNT(*) AS attempt_count, SUM(CASE WHEN connect_status CONNECTED THEN 1 ELSE 0 END) AS connect_count, SUM(call_duration) AS call_duration_sum, MAX(concurrent_count) AS max_concurrent, CONCAT({, normal_release:, SUM(CASE WHEN release_reason NORMAL THEN 1 ELSE 0 END), ,, resource_exhausted:, SUM(CASE WHEN release_reason RESOURCE_EXHAUSTED THEN 1 ELSE 0 END) , }) AS fail_reason_json FROM t_cdr_transit_group_call WHERE call_start_time #{lastBatchTime} AND call_start_time #{currentBatchTime} GROUP BY DATE_FORMAT(call_start_time, %Y-%m-%d), FLOOR(TIMESTAMPDIFF(MINUTE, DATE_FORMAT(call_start_time, %Y-%m-%d 00:00:00), call_start_time) / 15) 1, group_number;这段SQL有几个细节值得说。首先是stat_slot的算法。把一条呼叫的开始时间转换到当天零点然后求分钟差再除以15取整加1得到1到288之间的时段编号。这个编号比直接存时间范围更便于做时段对齐和查询。实际使用中我用一张数字辅助表来生成0到287的slot序列逻辑更清晰。其次是fail_reason_json字段。我把失败原因计数以JSON格式存储在一个字段里而不是拆成多个列。好处是新增失败原因类型时不需要改表结构坏处是跨数据库的统计分析不太方便。考虑到我们的展示层就是自己写的Java应用解析JSON完全没有问题这个设计是划算的。最后是并发数。表结构里有一个concurrent_count字段它不是在CDR里直接给出的而是由采集层在网元侧通过性能计数器获取后关联到每个批次的数据上。如果直接把CDR里的某个字段当作并发数很可能不准确。4.2 忙时与接通率聚合逻辑日汇总表生成时有一个比较特殊的计算逻辑就是“忙时时段”的确定。我的实现方式是先从15分钟统计表里查出每个组的24个时段按小时呼叫量总和然后找出最大值对应的时段。-- 先按小时汇总 SELECT group_number, HOUR(stat_time) AS hour_slot, SUM(attempt_count) AS hour_attempt_count FROM t_stat_group_call_15min WHERE stat_date #{targetDate} GROUP BY group_number, HOUR(stat_time); -- 再从结果中取每个组的最大值时段 -- 实际使用时用子查询 ROW_NUMBER() 窗口函数处理这一段用窗口函数做是效率最高的。MySQL 8.0及以上版本原生支持窗口函数如果你的环境还在用老版本推荐在Java应用里做这个计算而不是靠复杂的SQL硬凑。接通率在日汇总里直接算好存储connect_rate connect_count / attempt_count保留4位小数展示层转成百分比格式。这里注意一点如果尝试次数为0接通率需要特殊处理不能简单除以0。我统一置为100%并在报表里用灰色标注“无话务”避免误导。4.3 可视化面板的指标组织后台数据算好了前台展示也不能拖后腿。中转组呼统计的可视化面板我按三个层级组织第一层是全局总览。一屏展示今天的组呼尝试总数、接通率、平均通话时长、当前并发数以及一张24小时的话务趋势折线图。这层解决的是“今天整体情况怎么样”的问题运维人员每天早上看一眼就能快速判断业务是否正常。第二层是按组分诊。提供组列表每个组显示今日呼叫量、接通率、忙时时段可以点击下钻查看该组的15分钟粒度趋势图和失败原因分布饼图。这层解决的是“哪个组有问题”的问题异常定位主要在这一层完成。第三层是明细追溯。从统计结果可以一键跳转到原始话单查询页面按组号码、主叫号码、时间段筛选查看单次呼叫的完整记录。这层解决的是“具体是哪次呼叫、什么原因导致异常”的问题。三级下钻的设计让不同角色的用户都能找到自己需要的信息又不至于一上来就被海量明细淹没。4.4 报表导出与定时推送除了在线查看业务运营上还有两个高频需求日报/月报导出以及异常预警推送。日报导出我采用异步任务的方式用户在界面点“生成日报”系统后台异步生成Excel文件生成完毕后弹窗提示下载。文件里的sheet结构是汇总页、按组明细页、按小时趋势页。Excel的格式模板用Apache POI在Java里动态生成避免依赖前端组件。异常预警则是每天固定3个时间点9点、15点、21点扫描统计表发现接通率低于设定阈值默认85%、或尝试次数较昨日同时间段下降超过50%的组自动生成告警消息推送给值班运维人员。定期扫描而不是实时告警是为了减少误报——组呼业务的波动性较大单点时刻的突降往往是正常现象一段时间的持续异常才有告警价值。5. 常见问题与排查实战记录5.1 接通率异常偏低该如何定位项目上线后遇到最典型的案例是某一天运维反馈“XX集团组的组呼接通率从前一天的96%降到了74%”。如果只看日汇总根本找不到原因。我按照三层排查法快速定位第一步先看趋势。把该组最近7天的小时粒度接通率拉出来发现下降并非全天性的而是集中在下午14点到16点之间其他时段完全正常。这说明不是组配置问题而是某个特定时段内出现了资源或网络干扰。第二步查看失败原因分布。把失败原因按类别计数发现最多的一类是“RESOURCE_EXHAUSTED”资源不足。这基本锁定了问题出在媒体资源或中继电路上。第三步查看并发数。把14点到16点之间的最大并发数调出来对比系统容量发现确确实实冲到了接近上限。再回看那一时段发生了什么——原来是该集团当天有个大型活动大量成员同时参与调度。事件结束后接通率自动恢复。这个案例说明统计模块的价值不只是“事后看数字”更重要的是支撑快速定位。如果连失败原因分类都没有统计排查这类问题会像大海捞针。5.2 文件采集延迟导致统计数据缺失另一个常见问题是网元侧文件延迟上报。有些网元忙时会积压话单文件到凌晨才批量补传。如果只按“当日文件”维度去做统计凌晨补传的数据归属到哪一天就说不清了。我采用的方案是采集层在解析文件时并不是以“文件到达时间”归属业务日期而是以“话单中的呼叫时间”归属业务日期。也就是说无论文件是当天到达还是次日凌晨到达只要话单里的呼叫时间属于前一天就算到前一天的统计数据里。日汇总任务设置的一定容错时间凌晨1点执行也考虑了大部分网元在凌晨0点到1点之间能够完成补传同时在采集模块里加了人工触发补充统计的按钮万一有极晚到达的文件运维人员可以手动触发重算。5.3 并发数统计不准确的问题排查有一次运维反馈并发数趋势图出现了一个尖刺数值高得离谱直觉就是数据有问题。检查之后发现网元上报的并发计数文件中存在一个计数器重置的情况。设备在重启后计数器从零重新累计而采集程序误把重启后的计数值当成了全量值导致并发数虚高。解决方案是在采集层增加“计数器单调性校验”如果当前上报值小于上次上报值判定设备重启将当前值重置为从零开始的相对增量。经过这次踩坑后续所有性能计数器的采集都加入了类似的单调性校验逻辑这一步看似不起眼但能避免大量脏数据进入统计库。5.4 常见问题速查表问题现象可能原因排查手段某组接通率突然下降组内成员大量离线、资源不足、信令拥塞查看失败原因分布、并发数趋势、组成员在线状态统计数据与网元侧对不上时区不一致、文件重复采集、统计口径不同核对时间戳转换、检查采集任务幂等性、确认接通判定口径报表生成慢统计表未分区、查询未带索引按日期分区、为组号码字段建联合索引、避免全表扫描凌晨汇总数据不完整网元补传文件未到达查看采集任务状态、手动触发补充统计、检查网元侧文件生成时间并发数出现异常尖峰设备重启计数器重置增加计数器单调性校验、过滤设备重启后的首条数据6. 项目落地实操建议与经验总结项目做到最后真正沉淀下来的不是代码而是几条在反复踩坑中总结出来的实操原则。这里挑几条最值得分享的。第一条先统一口径再写代码。业务统计系统最大的风险不是技术难点而是业务口径不清晰。做之前一定和相关干系人运维、运营、网优坐下来把指标定义对齐白纸黑字写清楚什么算尝试、什么算接通、失败原因怎么归类口径达成一致后再进入开发否则返工是必然的。第二条明细是根统计是叶。无论统计报表做得多花哨一定要把原始明细数据完整保留并保证能够从统计结果反向追溯到明细。没有明细支撑的统计系统一旦数据对不上连排查的入口都没有。第三条聚合任务一定要幂等。运行中重跑一次和跑一百万次结果必须一致。我在项目里使用的“先删除后插入”方案虽然多了一个删除步骤但换来的数据一致性非常值得。如果你用Hive或Spark做聚合可以考虑用“覆写分区”的方式达到同样的效果。第四条预留人工介入的出口。统计系统再智能也会有自动流程覆盖不到的场景比如网元补传、人工修正话单、手工补录统计。务必在界面上提供“手动触发重算”和“数据修正”功能否则每次异常都要直接改数据库长期运行风险很高。第五条报表要能讲故事。单纯把数字堆到界面上用户是看不出问题的。好的统计模块应该自动标注异常——比如接通率连续3个时段低于阈值时在趋势图上打一个标记点让用户一眼看到“这里有问题”。这种业务化的呈现比一排排精确到小数点后四位的数字有价值得多。这个业务统计模块上线后运维人员日均登录次数提升了近一倍日常巡检从原来逐台网元登录变成了打开XNMS看一块面板话务异常的平均发现时间从小时级缩短到了分钟级。对我个人来说最大的收获倒不是这些可量化的成果而是真正理解了“统计”这两个字的重量——它不只是数据库里的数字更是网络运行的体检报告每一处变化背后都有真实的业务在发生。做这类系统敬畏数据敬畏业务比敬畏技术本身更重要。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进