已经被讨论了很久,但是环顾四周,成功办理携号转网的人寥寥无几。即使这个过程是成功的,似乎也存在这样或那样的问题。 那么,有什么问题呢? 网上有好几篇文章,看起来云里雾里,模棱两可,实在是不尽如人意。在IT行业,凡事要实事求是、讲道理,要能说清楚。虽然我从来没有做过移动网络和运营商相关的业务,但是通过查资料还是可以得到很多信息的。“携号转网”之所以难,不仅是运营商的懒惰,还有技术难点。如果从软件设计的角度来看携号转网,应该会有很多新发现。 携号转网的问题其实在全球范围内都很普遍。它有一个专门的名字叫做MobileNumberPortability(MNP,手机号码迁移),主要涉及三个概念:原运营商(donor)、新运营商Recipient和号码携带请求(NPR)。相应的,其技术方案也是现成的,主要有两种技术方案。 第一种是美国、欧洲和世界上普遍使用的方案,称为recipient-led。用户换网时,先向新运营商申请,新运营商再联系原运营商,经数据验证后完成数据转移,将原号码“转移”。 第二种方案用在英国和印度,叫做donor-led。换网时,用户先向原运营商申请,获得相应的code(PAC,英国的portingauthorizationcode,UPC,印度唯一的portingcode),然后转移到新运营商,新运营商完成网络传输相应。 第二种方式看似繁琐,但可以避免上当受骗,因为原运营商有机会直接核实号码拥有者的身份。但是,这也会导致不正当竞争,因为原来的运营商可能会借此机会故意拖延,想尽各种办法来留住用户。 运营商迁移完成,但移动用户和运营商的关系只是迁移,问题还没有结束。要知道,移动通信不仅发生在运营商和用户之间,也发生在用户之间。所以需要让来电(其他用户)知道这个号码已经迁移到新运营商,这样来电和短信才能正确到达新运营商承载的用户。这个到达的专业名称是routing,即“路由”。 路由也可以通过不止一种方式实现。国际和欧洲的方案是中央号码数据库CDB(CentralDatabase)。简单的说,它就像一张大表,详细记录了每个号码属于哪个运营商。相应地,每次发生号码携带转移,都需要在CDB中增加相应的记录。运营商保留一份CDB,在拨打电话或发短信时查找,然后直接联系当前运营商获取该号码。 根据RFC3482,这个查询叫做ACQ(AllCallQuery)。组合方案称为ACQ/CDB路由,美国也采用这种方案,但美国的管理机构称为NPAC(NumberPortingAdministrationCenter)。 以上只是其中一种路由方式。英国的携号转网流程不一样,路由方式也很独特。英国不采用ACQ/CDB。即使用户携号转网,来电或短信仍会先到达原运营商,由原运营商转接至新运营商。这就是“间接路由”。(间接路由),类似于Unix中的符号链接。 该方案避免了对中心号码库的依赖,将携号转网的信息分散给运营商自行维护。问题是增加了不必要的传输。摆脱对原有算子的依赖——所谓“断骨接筋”。如果原运营商倒闭或倒闭,换网的用户仍然会受到影响,这可能是用户难以理解的。 下面说说国内的移动运营方案。根据目前国内运营商公布的携号转网流程,用户携号转网时,需先咨询原运营商资质,获得授权码,方可到新运营商办理转网手续。据此可以推测,中国应该采取捐助主导的解决方案。但由于国内手机号码可能存在捆绑套餐,切换网络时需要进行复杂的业务确认。通过询问办理过携号转网的合作伙伴了解到,这个“复杂的业务确认”过程正是原运营商试图留住用户的过程。 有意思的是,网络转账虽然是捐助者主导的解决方案,而且之前国内好像没有中心化的数据库。这件事不难理解。长期以来,似乎只有三大运营商,每个运营商都是一个独立的实体。工信部的管理职能较多,但不具备基础系统的建设和维护。 携号转网对于任何运营商来说似乎都是“弊大于利”。如果用户想转出,就相当于失去了用户;如果用户想转入(从目前看到的报道来看,携号转网的用户比例极低),还需要额外的系统建设,其实是很不划算的。所以看起来工信部是最合适也最有可能牵头的。 根据我看到的技术文档,目前我国正在采用类似ACQ/CDB的方案来完成网络转接用户的路由。具体来说,工信部维护统一的携号转网中央数据库(CNPDB),管理全国NP业务中心CSMS。中国联通、中国电信、中国移动将维护各自的LNPDB和LSMS,数据与CNPDB保持一致。 中国联通携号转网系统架构。资料来源:张伟强,杜仲彦,李松权,小璐《移动号码携带核心网部署方案探讨》 用户每次外呼,运营商首先查询自己的LNPDB,确定外呼号码对应的运营商(进行NP查询),然后进行路由相应的出站信号。整套技术方案看似没问题,但之前没有中心化的数据库,所以CNPDB的建设和整个流程的梳理还需要时间。 接线员呼叫流程。资料来源:张伟强、杜仲言、李松泉、肖璐《移动号码携带核心网部署方案探讨》 那么携号转网遇到的最大问题是什么?我认为这是一个短信路由问题。这一点也被很多转网的携号转网的经历所证实——客服会提示很多短信转网后可能收不到。为什么会这样? 目前,大量短信服务商在判断用户所属运营商时,完全按照线下约定的规则进行。比如“130是联通开头,135-139是中国移动开头,189是中国电信开头”。短信服务商收到短信数据包后,会先按照号码段划分任务,发送到不同的运营商渠道。对于携号转网的用户,首先会根据号码分配到原来的运营商频道,运营商不再对用户负责,短信也发不了——当然也可以屏蔽大多数垃圾邮件。 充电时也存在这个问题。很多充值网站会根据用户输入的手机号码自动选择运营商。看似方便,但携号转网的用户也会出错。另外,在一些需要确定用户所属运营商的场合,也会出现同样的问题。如果你输入的手机号码“貌似”是中国联通的,实际上已经转到了中国移动,系统根据号码段判断运营商,会报错,不能继续使用。 如果我们暂时抛开对运营商的评价,专注于携号转网的技术方案,会发现这其实是一个发展中非常普遍的问题:如何设计资源迁移? 狭义的迁移很简单,就是数据从donor(原资源持有者)传输到recipient(新的资源持有者)。但是一个安全的系统必须解决一个问题:如何判断这次迁移是否真的可信? 在号码携带转移的接收者主导方案中,接收者可以直接发起资源迁移请求,捐赠者将信任该请求。这看似简单明了,但有一个前提,经营者数量少,设立门槛很高,追责也很方便。如果不满足这个前提,资源持有者众多,设立门槛很低,那么直接申请从接收者到捐赠者的数据迁移就会面临安全问题。 如何解决这个问题?我们可以想一想如今互联网上流行的OAuth是怎么做的?当受赠人向捐赠人发送申请时,还有一个“捐赠人与使用人之间的确认”的附加程序。因为用户的直接参与,解决了“信任”的问题。 当然,方法不止一种。您还可以从捐助者主导的计划中学习。用户先从捐赠者那里获得许可和验证码,然后完成迁移——其实这就是域名迁移所采用的方案,解决了“多服务商”环境下建立信任的问题。 但是只做这一步不算是资源迁移方案,有能力的工程师不仅要看到眼前这一点,还要做好完整的规划,保证迁移完成后,所有相关业务都保留下来顺利不受影响。看完上面的ACQ/CDB方案,你可能会觉得“这不是显而易见的”,但现实未必如此。惨痛的教训数不胜数。 我开发了一个电商物流系统很多年前。有一天,业务人员问:“为了节省成本,同一个收件人的两件物品可以一起发货吗?”负责开发的程序员听了:“这没问题,这很简单,我将立即发送。”我做不好。” 两天内真的开发完成了。拣货、打包、出仓、登记、配送、录入确实没有问题,所以顺利上线。一开始一切都很正常,他们打算为这个“透明”的计划争功。前线投诉多,相关人员投诉。 一问,发现工程师根本没有考虑异常情况。可以同时订购两件商品,但三件或四件商品呢?合并货物的最小单位是订单还是订单?如果用户想要发票,他们应该开一张票还是两张票?与供应商结算时,运费如何分担?最麻烦的是逆向流程——如果用户想要退款或退回其中一件商品,他应该怎么做?成本如何计算? 仓促决定的后果就是要踩一个大坑才知道,“联合发货”并没有看上去那么简单,远比想象的麻烦。不是程序员或者小产品经理能搞定的,还得加上后勤、财务等一大圈人。程序员们想当然地认为“没问题”,造成了很多问题,还给大家挖了个大坑……搬走”,拍了拍头就做了,拍了屁股就搬了。设计师完全不考虑其他人,从不考虑“其他人或企业是否知道数据已移动”,也不关心其他人或其他企业以后会做什么。 在“携号转网”中》的解决方案,要解决这个问题,需要保持数据的同步更新。一种方案是提供集中记录(ACQ/CDB)的方案。这种方案职责明确,可以保持调用路径最短,但是它提出对中心节点的稳定性、响应速度和复杂能力要求高。 另一种间接路由在某种意义上可以称为“分布式”解决方案,即必须通过原始服务提供商中转。这时候传输信息片段是actu由运营商自己维护。该解决方案不需要花费大量精力来构建中心节点。缺点是责任不明确,存在不必要的转移,迁移后的用户仍会受到原运营商服务质量的影响。 迁移还会带来其他问题:如果用户多次迁移,会形成“迁移链”。链条长了不仅会影响效率,而且排错也很麻烦。这还没完,如果设计不当,可能会形成一个循环…… ***再深挖一点——所谓的“携号转网”真的跳出来了,核心是一个函数问题。 一个函数最简单的方式就是f(x)=y,这个大家都知道。对于携号转网,关键是根据手机号码查询运营商,可以看成f(x)=y,其中x为“具体手机号码”,y为“运营商”。只要掌握了这些信息,其他的就好办了。 虽然大家都默认有函数f(x)=y,但是很多人也知道函数f内部是怎么工作的,而且这个函数没有正式版,所以基本上大家自己实现:第130-133节是联通的,1??35-139的是移动的,189的是电信的... 同时,我们也知道在软件设计上提倡“暴露接口,不暴露实现”.为什么?因为接口是抽象行为的定义,比如“输入手机号,获取运营商”是一个抽象行为,它包装了f(x)=y,至于哪个手机号(x)对应哪个运营商(y),规则可能会不断变化,即使有一些例外。不过没关系,因为外部不需要知道细节,只要能安心调用这个接口,原来的外部业务流程就会继续运行。反之,如果暴露的是实现,则需要处处不断更新号码段规则。如果遇到携号转网等特殊情况,维护难度会更高。 那为什么“根据手机号查询运营商”的功能是暴露实现而不暴露接口呢?这可能有历史原因。缺乏顶层设计,一开始也没有权威的公共接口。这个接口的实现要承担巨大的负荷,还有技术上的挑战…… 所以早期的很多技术问题确实是“线下共识”。比如早期很多电商的订单号,就承载了很多信息。只需看订单号就可以识别订单日期、所属仓库、货物种类等。 但是现在,随着软件的复杂度越来越高,随时在线越来越普遍,这种“线下共识”越来越被取代。不信你可以看看各大电商的订单号。早年可以看到订货日期、当日流水号等,而现在,几乎看不到任何编号规则。但是手机号处理起来很麻烦。绑定手机号的线下规则有很多,不仅要判断运营商,还要归属…… 总之,从携号转网这个“简单直观”的事情就可以看出软件设计要解决的问题的原型往往很简单,但这些问题往往涉及的因素很多,没有一刀切的解决方案。不同的解决方案各有优缺点,必须根据具体的环境来选择——很多时候,这恰恰是架构设计最重要的因素,也是架构人才的核心竞争力。 高春晖、胡书琪对本文亦有贡献,在此致谢。
