kamailio软交换CDR 呼叫记录维护管理讨论
在前面的讨论例中,我们介绍了关于计费的几个机制和存储方式。针对计费功能来说,呼叫记录是非常重要的核算对账依据。无论是 IPPBX 或者呼叫中心,CDR 是呼叫计费结算以及报告的核心数据。今天,我们讨论一下 Kamailio 中的 CDR 以及维护管理。
CDRs 可以通过调用kamailio_cdrs()
存储过程来创建。该过程是由安装向导执行时添加的,用户也可以通过cron.d
任务或通过类似应用外部程序执行。用户也可以在配置文件中通过加载rtimer
和sqlops
模块,并且设置周期路由块执行生成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
记录。如果找到匹配结果,则计算BYE
和INVITE
记录之间的时间差作为呼叫持续时间,并将匹配结果存储在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秒的呼叫时长)。cost
和rated
表的列由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-Id
、From
和To
标签,在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 中看到这些数据。
参考资料: