kamailio软交换CDR 呼叫记录维护管理讨论

在前面的讨论例中,我们介绍了关于计费的几个机制和存储方式。针对计费功能来说,呼叫记录是非常重要的核算对账依据。无论是 IPPBX 或者呼叫中心,CDR 是呼叫计费结算以及报告的核心数据。今天,我们讨论一下 Kamailio 中的 CDR 以及维护管理。

CDRs 可以通过调用kamailio_cdrs()存储过程来创建。该过程是由安装向导执行时添加的,用户也可以通过cron.d任务或通过类似应用外部程序执行。用户也可以在配置文件中通过加载rtimersqlops模块,并且设置周期路由块执行生成CDR。用户可以将下面的代码片段添加到kamailio.cfg中:

#!define WITH_MYSQL #!define WITH_ACCDB
...
# -- Siremis CDRs --------------
loadmodule "rtimer.so" loadmodule "sqlops.so"
...
modparam("rtimer", "timer", "name=cdr;interval=300;mode=1;") modparam("rtimer", "exec", "timer=cdr;route=CDRS")
modparam("sqlops", "sqlcon", "cb=>mysql://kamailio:kamailiorw@localhost/kamailio")
...
# route block for period execution of stored procedures for: # - CDRs generation
# - CDRs rating route[CDRS] {
sql_query("cb","call kamailio_cdrs()","rb");
sql_query("cb","call kamailio_rating('default')","rb");
}

路由块 CDRS 每 5 分钟(300 秒)执行一次,如果用户想要更短或更长的间隔,请更改 rtimer 模块计时器参数的 interval 属性值。

用户要注意,不要保留太长时间的计费记录,特别是如果用户有很多通话记录,因为表格会变得很大,MySQL 执行性能也会变慢。用户也可以从相同的路由[CDRS]中删除旧记录,例如删除 180 天以前的记录。

route[CDRS] {
sql_query(“cb”,
"DELETE FROM acc WHERE time < DATE_SUB(NOW(), INTERVAL 180 DAY)”);
...
}

存储过程kamailio_cdrs()选择所有新的INVITE(选择那些cdr_id列没有值的)然后搜索相应的BYE记录。如果找到匹配结果,则计算BYEINVITE记录之间的时间差作为呼叫持续时间,并将匹配结果存储在cdrs表中。cdrs表的结构如下:

Name Type Description
cdr_id bigint database unique id (written back to cdr_id in acc table)
csrc_username string the source user /caller (username from From URI)
csrc_domain string the source domain (domain from From URI)
dst_username string the destination user/callee (username from R-URI)
dst_domain string the destination domain (domain from R-URI)
dst_ousername string the original destination user/callee (username from To URI)
call_start_time datetime the SIP request type (aka SIP method)
duration int the duration of call in seconds
sip_from_tag string the value for tag parameter in From header
sip_to_tag string the value for tag parameter in To header
sip_call_id string the value for Call-Id header
cost int the cost of the call
rated int the flag to mark the CDR rated
Name Type Description
created datetime time when the accounting event is happening
src_ip string source IP - the address from where the request was sent

我们根据以上描述很容易看出哪些属性是从acc表中复制的,其中一些值是通过kamailio_cdrs()kamailio_rating(...)存储过程计算得出的结果。

call_start_time是从INVITE记录的时间获取的,呼叫时长是BYE的时间减去INVITE的时间,然后加1,因为第一秒和最后一秒都很关键(或者换句话说,每个开始呼叫的秒数都被计算在内,如果通话在同一秒内被马上接听和挂机的化,则会导致一个1秒的呼叫时长)。costrated表的列由kamailio_rating()存储过程充入。kamailio_cdrs()的SQL代码非常简短,熟悉SQL的用户都可以轻松调整处理流程来更好地满足自己的需求:

...
CREATE PROCEDURE `kamailio_cdrs`() 
BEGIN 
 DECLARE done INT DEFAULT 0; 
 DECLARE bye_record INT DEFAULT 0; 
 DECLARE v_src_user,v_src_domain,v_dst_user,v_dst_domain,v_dst_ouser,v_callid, 
 v_from_tag,v_to_tag,v_src_ip VARCHAR(64); 
 DECLARE v_inv_time, v_bye_time DATETIME; 
 DECLARE inv_cursor CURSOR FOR SELECT src_user, src_domain, dst_user, 
 dst_domain, dst_ouser, time, callid,from_tag, to_tag, src_ip 
 FROM acc 
 where method='INVITE' and cdr_id='0'; 
 DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1; 
 OPEN inv_cursor; 
 REPEAT 
 FETCH inv_cursor INTO v_src_user, v_src_domain, v_dst_user, v_dst_domain, 
 v_dst_ouser, v_inv_time, v_callid, v_from_tag, v_to_tag, v_src_ip; 
 IF NOT done THEN 
 SET bye_record = 0; 
 SELECT 1, time INTO bye_record, v_bye_time FROM acc WHERE 
 method='BYE' AND callid=v_callid AND ((from_tag=v_from_tag 
 AND to_tag=v_to_tag) 
 OR (from_tag=v_to_tag AND to_tag=v_from_tag)) 
 ORDER BY time ASC LIMIT 1; 
 IF bye_record = 1 THEN 
 INSERT INTO cdrs (src_username,src_domain,dst_username, 
 dst_domain,dst_ousername,call_start_time,duration,sip_call_id, 
 sip_from_tag,sip_to_tag,src_ip,created) VALUES (v_src_user, 
 v_src_domain,v_dst_user,v_dst_domain,v_dst_ouser,v_inv_time,
 UNIX_TIMESTAMP(v_bye_time)-UNIX_TIMESTAMP(v_inv_time), 
 v_callid,v_from_tag,v_to_tag,v_src_ip,NOW()); // 插入cdr
 UPDATE acc SET cdr_id=last_insert_id() WHERE callid=v_callid 
 AND from_tag=v_from_tag AND to_tag=v_to_tag; 
 END IF; 
 SET done = 0; 
 END IF; 
 UNTIL done END REPEAT; 
END

读者可以看到,在cdrs表中插入新记录后,通过匹配Call-IdFromTo标签,在acc表中相应的INVITE记录将会通过cdr_id的值更新。在下一次执行kamailio_cdrs()时,将不再选择INVITE记录,因为它的过滤包括条件包括了cdr_id=0

如果用户使用的是Siremis界面的话,用户可以在SIP Admin Menu => Accounting Services => CDR List列表中查看生成的CDR, 默认视图仅显示几个属性,要查看每个记录的所有属性,请单击Id列。

用户要注意,随着 SIP 通话的不断增加,acc记录将出现在数据库 acc 表中,而CDR记录将定期出现在数据库 cdrs 表中,这取决于 rtimer 模块计时器参数的值,因此,用户可能需要等待一段时间才能在 Siremis 中看到这些数据。

参考资料: