VoIP计费是SIP软交换运营平台的一个核心功能,计费处理流程和呼叫流程有着非常紧密的关系。运营商对计费有着各种不同的规范和常规设置,不同的国家和运营商都有着不同的设置机制,因此关于计费也存在一些不同的标准。但是,绝大部分的场景中,CDR计费都相对比较规范,但是在一些特殊场景中,软交换计费系统需要针对一些故障处理增加相应的支持来保证计费的准确性和完整性。OpenSIPS使用的Acc模块和CDR就实现了运营级计费的功能支持。在本文章中,我们主要针对OpenSIPS的ACC模块和CDR处理进行讨论,呼叫中BYE消息丢失问题的处理方式进行讨论。另外,笔者将针对关于两种常见计费问题中的网络故障和会话关闭时延的问题进行讨论,最后,笔者将通过配置CDR示例来说明OpenSIPS中的计费处理和配置文件。
关于VoIP中CDR处理的背景介绍
在PSTN时代,运营商的CDR计费本身已经存在了,随着IP语音的突飞猛进,包括现在基于VoLTE语音的部署,计费的模式也逐渐发生了变化。在目前最新的VoLTE网络中,因为一个SIP呼叫跨越了很多的节点路径,计费的流程更加长一些,涉及的各种延迟也更加一些,比较重要的参数例如,Post-dialdelay(PDD或者SRD)。这些延迟会影响计费的时长和话单的准确性。另外,每个国家可能因为环境的不同对CDR参数设置也有不同的要求,还有很多场景中需要紧急电话服务的话,电话系统必须需要支持规范的呼叫格式和对呼叫进行不同的定义。
此图例以及以下图例均来自于互联网资源
因为在SIP网络中,根据不同的SIP初始请求所产生的事务和dialog也可能完全不同,因此请求的不同也产生了不同的CDR消息记录。这些请求可以是:INVITE,REGISTER,SUBSCRIBE,OPTIONS,MESSAGE和INFO。
这些请求的返回数据都需要根据不同的请求生成一个完整的CDR记录。这些数据的处理相对就复杂很多,需要专门的CDR服务器做解析生成。另外,在CDR生成时,SIPdialog和事务都有各自的会话结束方式,这又增加了CDR生成的逻辑流程,它们通过bCDR和cCDR实现基础CDR和合并CDR的生成,并且创建了dialog和事务的连接。
研究人员TamásTóthfalusi发布了针对VoLTE的CDR生成做了比较详细的分享说明和关于CDR生成的研究成果。在其论文中比较详细说明了VoLET环境中SIP呼叫和CDR生成的研究。读者可以参考其论文做进一步的研究。
3GPP针对CDR有着非常详细的规范说明(ETSITS),包括了漫游,在线费用,离线费用,呼叫和服务费用等非常详细的说明。用户如果有兴趣的话,可以参考ETSITS3GPP规范做更多了解。
3GPP仅是针对网络环境做的关于CDR的规范说明,一些国家也专门发布了自己的运营商CDR规范说明。在英国的CDR规范说明中包括了固话,VoIP呼叫,移动端和呼入呼叫(包括多腿呼叫费用计算)。呼叫类型包括:
V=outboundvoicecall,
VOIP=VoiceoverIPcall
D=Data/ISDNCall
C=Conferencecall
N=Inboundcall(billable)
I=StandardInboundcall(usuallynotbillablee.g.Rawcalldata)
U=Unansweredcall
B=BusyCall
X=Callfailed
M=Mobilecall(madefrommobiledevice)
G=GPRSData
UK的CDR格式中规定了42个必要的呼叫参数,其中部分参数是可选参数。
UK的CDR的标准的呼入格式规范:
除了标准的呼叫计费以外,美国特别针对NG9-1-1呼叫的计费也有规范说明。此规范的目的是针对电话运营商提供的紧急呼叫中计费的规范定义。为了满足NG9-1-1的规范,很多呼叫的定义需要增加一些相应的变量设置,例如对呼叫的定义和总呼叫时长的变量设置和log事件:
通过以上简单的背景介绍,我们可以看到,生成一个完整的CDR是非常复杂的流程,这里需要数据业务数据的参与,同时需要满足不同国家的规范的要求,并且有时如果电话系统需要紧急呼叫支持的话,系统配置也要具备一定的灵活性支持其业务要求。在第二个章节,笔者将具体介绍OpenSIPS环境中的ACC模块的存储方式讨论。
OpenSIPS的Accounting事件的存储讨论
为了帮助大家了解更多关于CDR计费的基本概念和应用的场景,笔者在前面章节介绍了一些关于CDR计费的一些基本知识,行业规范和几个具体的应用广泛,其目的是帮助读者了解CDR计费以外的一些业务要求,在扩充CDR支持能力时可以对其要求有比较充分的认识。当然,因为水平资源有限,笔者也没有能力对CDR处理有非常全面的了解,这里主要是提醒读者了解更多关于简单CDR以外的内容。
如果具体到今天核心的主题-SIP网络环境或者OpenSIPS平台的话,我们主要介绍关于OpenSIPS环境中ACC模块的功能和其存储方式。OpenSIPS的呼叫ACC模块或者Accounting是一个非常重要的模块。用户在部署OpenSIPS时不可避免需要接触到ACC模块。ACC模块涉及了几个主要的概念,包括ACCevent,ACC核心内容和ACC后端存储处理,其他ACC拓展参数支持和多腿呼叫的CDR记录等几个方面的内容。
首先,我们需要明确的是ACC事件,ACC事件简单来说就是事务处理状态的事件,包括成功事务事件,失败事务事件和丢失的事务事件等。这些事件和呼叫本身有紧密的联系。我们前面的讨论中也说明,根据CDR规范,呼叫的事件都需要通过两种方式进行存储,一种是系统log的形式,另外一种是后端数据库存储的方式。OpenSIPS也同样遵守这个规范。在前面的介绍中和以前的历史文档笔者都有说明,在一个呼叫中需要通过事务处理和dialog处理,它们有着各自不同的处理方式,因此,在ACC需要支持事务和dialog实现对CDR的完整记录。OpenSIPS的ACC模块可以支持消息,事务和dialog。ACC中的消息包括多种消息,例如INVITE和BYE等。这些ACC的消息都可以通过log,数据库存储,Radius和Events形式进行存储。笔者在后续的章节会分别介绍这些处理过程。
如果读者不清楚关于事务,会话和dialog的区别的话,建议首先阅读笔者历史文章:再论SIP呼叫中的Call,Dialog和Transaction
在后续的关于CDR计费时长的讨论中,我们将使用事务处理的概念,读者一定要明确这个概念,以免混淆了整个计费的设置。
OpenSIPS可以对以上数据进行后端存储支持,支持的存储方式包括:
log,支持系统的log文件数据库,支持MYSQL,PG和甲骨文数据库等开源数据库AAA,支持Radius和Diameter(比较老的版本可能支持,读者自己查阅官方文档)EVI,event接口包括RabbitMQ,DatagramEvent
关于AccScope的具体设置支持,和其他数据库设置支持的细节,读者可以访问官方文档做进一步了解。需要注意,如果需要设置ACC模块时,用户需要经过几个流程设置:
加载模块和相应参数为初始的INVITE请求设置ACC支持为BYE请求设置ACC支持为ACC设置支持丢失呼叫配置
笔者在后续的示例配置中会体现以上核心步骤。
Accounting事件的讨论
在OpenSIPS的ACC模块中,主要支持四种事件的状态,包括事务成功事件,成功的dialog事件,丢失的呼叫事件,失败的事务事件。
在以上的事件状态中,其他几种事件相对比较容易处理,丢失的呼叫事件是一个相对比较困难处理的事件。在丢失呼叫的事件中包括两种状态,一种是丢失的事件,另外一种是成功的事件。因此,如果做CDR处理时需要同时记录这两种事件对应不同的UAS。我们经常可能遇到,如果是一个fork呼叫或者按续呼叫过程中,如果第一个呼叫失败以后会继续呼叫,继续对第二个目的地进行按续呼叫的流程。这种丢失呼叫的事件需要特别留意,否则CDR统计结果就会产生错误。
这里提醒读者,在处理这些事件中,成功的事务处理和失败事务处理会存储到同一ACC的表中,丢失的呼叫事件则会存储到missedcall的表中。当然,用户也可以自己修改表名称方便自己的部署环境。
OpenSIPS中关于多腿呼叫的计费讨论
计费是一个非常重要的功能,无论是使用开源媒体服务器,例如Asterisk还是FreeSWITCH,或者使用SIP软交换服务器,例如OpenSIPS和Kamailio或者很多商业IPPBX。很多用户忽略了一个非常复杂或非常细节的问题,那就是在呼叫经过多次转接或者停靠以后的计费处理。通常的CDR中,我们仅简单计算了从呼叫方到被呼叫方的总时长。特别是在企业IPPBX的应用场景中,我们仅考虑了呼入方到一个内部分机之间的呼叫时长,但是,读者是否考虑过如果呼入以后涉及了电话盲转或者询转,或者执行了一个分机随行以后继续呼叫一个外部电话号码或者长途固定电话。如果涉及到了其他额外呼叫流程的话,严格来说,这些呼叫都需要分段计费,有的呼叫可能是不计费的(例如呼入)并且通过分段计费最后聚会成一个完整的CDR计费。不幸的是,很多的IPPBX并没有进行如此细节的处理,仅简单笼统地计算了一个呼叫的总时长。因此,这样的处理结果不是一个正确的CDR计费流程,只能算是一个CDR统计,它不能作为话单结算数据。在如下示例中,按照目前正常的计费模式,如果A呼叫B,B然后转接到C端手机的话,从A到B是免费的,但是,从B到C端可能是付费的,这里涉及了谁将为B到C支付问题。如果按照标准的CDR计费的话,A应该对C端付费,事实上这完全是一种错误的计费方式。因此,针对复杂业务场景中,多腿分段计费是一个必要的计费方式。OpenSIPS支持多腿计费时需要重新创建一个新的acc_new_leg()进行支持。
作为一个运营级的SIP软交换,OpenSIPS和Kamailio在早期版本并没有真正实现多腿CDR的支持。在后期的OpenSIPS的发布版本中开始支持了多腿CDR记录支持。通过多腿计费保证了CDR的准确性和完整性。OpenSIPS在多腿CDR支持时增加了一个对中间跳板的记录,分别对分段处理进行存储支持。正常的CDR仅保存呼叫源和最终目的地的存储。OpenSIPS通过支持多腿CDR实现了非常完整准确的CDR计费。这里提醒读者,在ACC中,事务记录中的duration是为空的,在CDR记录中则包含了duration值。
在数据库生成的示例数据如下,包括了用户1到用户2,用户2到用户3的完整的多腿CDR记录。
OpenSIPS环境中关于BYE丢失的处理
在呼叫过程中,因为网络原因或者其他的终端原因,很多情况下我们会遇到呼叫失败的问题。呼叫失败不仅仅会影响呼叫方的体验,同时也影响计费结算结果。在一些呼叫中,我们经常会收到没有BYE请求返回的问题,或者叫BYE消息丢失的问题。没有BYE消息的话,CDR计费就产生错误。这对CDR生成是一个非常大的挑战。在以前的历史文档中,我们讨论了dialog状态和计费启动的机制,读者可以参考OpenSIPS中的dialog使用和计费的说明:
OpenSIPS学习笔记-Dialog的五种状态及配置示例
研究人员DimitrisGeneiatakis在早期发表的关于CDR算法讨论-AMechanismforEnsuringtheValidityandAccuracyoftheBillingServicesinIPTelephony中对CDR做了最简单的描述:
BYE消息是计费计算的一个基准数。
有时,如果没有及时处理BYE消息丢失的问题的话,可能CDR时长就会不断增加。本来一个呼叫可能已经在3分钟内结束了,但是因为没有收到BYE消息,最后通话时长可能是10分钟或者20分钟甚至于更长时间。这样计费就会产生很多问题。如果出现了丢失BYE消息以后,系统平台应该有一定的机制来及时正确执行挂机,保证其CDR计费出现问题。另外,如果系统平台对并发呼叫有限制的话,丢失了BYE消息以后,这样的限制设置可能会产生错误的结果。
目前,在SIP网络环境中,OpenSIPS运营平台结合其他手段来执行丢失BYE消息以后的挂机处理。各种处理方式都有其各自的特点。
使用标准的SIP定时器时,SIP会话定时器设置超时以后,结束会话或者重新发起一个re-INVITE请求或者update,经过几个周期以后对呼叫挂机。它要求客户端或者网关能够支持会话定时器设置支持。当然,如果终端和网关都在同一公司内网环境中的话,用户可以控制设备的配置,比较容易设置定时器支持。如果是运营级的应用场景的话,建议用户通过网关或者SBC来设置定时器支持。RFC对SIP会话定时器有非常明确的规范,读者可以参考RFC做进一步了解。
通过dialog的ping实现BYE消息丢失的挂机控制的话,这是一种非标准的控制手段,它需要通过SIP信令发送OPTIONS消息来实现。当然要求客户端或者网关支持OPTIONS/INVIET消息。因为目前很多终端都支持发送OPTIONS消息或者re-INVITE,相对来说是一种比较简单的处理BYE消息丢失的方式,所以笔者推荐终端,ATA和网关设置OPTIONS消息检测来实现BYE消息丢失的挂机处理。
最后一种方式是通过RTP提示来对BYE消息丢失进行处理。这种处理方式也是一种非标准的处理方式,但是,它需要通过RTP流的检测来实现。OpenSIPS不能支持RTP超时检测,所以只能通过媒体服务器端通过对双方的RTP流检测或者静音检测来实现挂机处理。如果媒体服务器检测到双方的RTP语音流无任何有效数据时,说明双方已不再继续通话,所以执行挂机处理。这种检测方式也没有对终端有任何的要求。如果以上两种处理方式不能使用时,读者也可以考虑通过媒体服务器检测RTP语音超时来实现。开源媒体软交换Asterisk和FreeSWITCH都可以支持这样的检测机制来实现BYE消息丢失的挂机。但是,这种检测机制很难非常准确及时地实现挂机处理。因此,如果要实现CDR记录的完整性和准确性,这是一个相对比较差的选择。
再次说明,选择何种处理方式需要用户根据自己的部署环境做判断,最终使用一个较低成本的方式来解决问题,笔者仅提供一个比较完整的建议。
CDR计费中网络故障和会话时延的问题讨论
大部分情况下,我们的通话是在正常状态完成,整个CDR数据也非常正确。但是,在基于SIP网络环境中,特别是基于云平台或者全球部署的呼叫环境中,呼叫过程中出现网络故障是非常正常的事情。因为网络故障,整个Acc模块的记录就会出现问题,最终导致CDR和话单错误。如下示例中,如果运营商和用户代理服务器之间出现了网络故障,如何进行结算是一个比较头疼的问题。
在以上示例中,运营商已经对代理服务器发送了OK,但是代理服务器和运营商之间的网络出现了故障以后,我们需要特别