吴伟贤のBlog

Feed Rss

freeswitch-cookbook 第三章(三) Using a web server to handle XML CDRs

07.20.2013, freeswitch, by .

转自:http://www.8000hz.com/archives/41.html

Using a web server to handle XML CDRs

Freeswitch的mod_xml_cdr模块有个很好的功能,它可以把CDR使用HTTP POST到一个可以处理CDR或是把CDR插入到数据库的WEB服务器.
这个功能有几个优点:
1.现在的WEB服务器可以有很大的并发量
2.多个FreeSWITCH服务器可以都发送到一个WEB服务器
3.多个WEB服务器可以设置成允许备份和冗余.
本方法将会重点关注配置一个WEB SERVER用来处理带XML CDR的POST请求.
Getting ready
你需要一个你控制的可以使用的WEB SERVER.大多数Linux/Unix和windows都是可以安装Apache的.怎么配置一个WEB SERVER本书就不提了,此类内容的介绍在很多书中和互联网上都有.本方法将会假想已经有一个干净利索的Apache WEB服务器,但这些方法同样适用于其他的服务器,像Lighttpd and Nginx.本例子中我们假想apache和Freeswitch运行在同一台服务器上.
How to do it…
在你的服务器上启用mod_xml_cdr.(参数本章节中之前提到的 使用XML CDR方法).
下面, 请按照下列步骤操作:
1.打开conf/autoload_configs/xml_cdr.conf.xml然后定位到此行
<!– <param name=”url”  value=”http://localhost/cdr_curl/post.php”/> –>
2. 改变此行的内容 <param name=”url” value=”http://localhost/cgi-bin/cdr.pl”/>
3.保存退出
4.在您系统的cgi-bin目录下,创建一个名叫cdr.pl的文件(通常cgi-bin目录是/usr/lib/cgi-bin但在比较老的系统上可能不一样).
添加这些内容到文件中:

#!/usr/bin/perl
  use strict;
  use warnings;
  use CGI;
  $|++;
    my $q = CGI->new;
    my $raw_cdr = $q->param('cdr');
    open (FILEOUT,'>','/tmp/cdr.txt');
    print FILEOUT $raw_cdr;
  close(FILEOUT);
print $q->header();

5.保存文件退出
6.使用以下命令使文件可执行 chmod +x /usr/lib/cgi-bin/cdr.pl
7.进入fs_cli和按F6或执行reloadxml命令
8. 拨打一通测试电话,你应该可以看到/tmp/cdr.txt文件内容中含有XML CDR记录.
How it works…
这是一个简单的使用Perl写的CGI脚本.它的功能就是把mod_xml_cdr提交的POST数据的cdr参数值提取出来.一旦它有这个值(在变量$raw_cdr中)脚本就会把CDR信息输出到临时文件/tmp/cdr.txt中. 但这个例子在生产环境上就不是那以的有用了,它只是说明了得到POSTed的CDR数据并插入到系统中的最简单步骤.如果你更擅长其它的脚本语言,像PHP, Python,或Ruby,你可能很容易地使用这些语言来处理CDR.下面是一个使用PHP写的示例版本:

$raw_cdr = $_POST['cdr'];
  $writefile = fopen('/tmp/dump.txt',"w");
  fwrite($writefile, $raw_cdr);
fclose($writefile);

一旦你在程序中得到了数据,你处理它们的选项就多了.
There’s more…
使用CGI脚本处理XML CDR数据(Fast CGI或其它可以处理HTTP POST请求的有用的方法)的常用习惯就是处理数据然后插入到数据库中. 这面的部分就是描述怎么样把CDR插入到和前一个小方法Inserting CDRs into a backend database中使用的数据库和表中. 假设你一个”CDR”数据库,里面还有个”cdr”表,你可以使用下面的这个修正版本的cdr.pl脚本插入记录到数据库中. 你需要使用cspan工具去安装DBI和DBD模块去支持数据库.通用的是DBD::mysql和DBD::PgPP.示例是假设是DBD:PGPP,纯perl写的Postgres数据库驱动. The modifed cdr.pl is as follows: 修改版本的cdr.pl看起来像这样:

#!/usr/bin/perl

  use strict;
  use warnings;
  use CGI;
  use DBI;
  use Data::Dump qw(dump);
  $|++;
    my $q = CGI->new;
    my $raw_cdr = $q->param('cdr');
    my @all_fields = qw(caller_id_name caller_id_number 
    destination_number context start_stamp answer_stamp end_stamp 
    duration billsec hangup_cause uuid bleg_uuid \
    accountcode read_codec write_codec);
      my @fields;
      my @values;
      foreach my $field (@all_fields) {
        next unless $raw_cdr =~ m/$field>(.*?)</;
        push @fields, $field;
        push @values, "'" . urldecode($1) . "'";
      }
      my $cdr_line;
      my $query = sprintf(
      "INSERT INTO %s (%s) VALUES (%s);",
      'cdr', join(',', @fields), join(',', @values)
      );
    my $db = DBI->connect('DBI:PgPP:dbname=cdr;host=localhost', 
'postgres', 'postgres');
    $db->do($query);
  print $q->header();
  sub urldecode {
    my $url = shift;
    $url =~ s/%([a-fA-F0-9]{2,2})/chr(hex($1))/eg;
    return $url;
  }

上面的脚本是一个将通话记录插入到数据库中的一个简单的示例. @all_fields数组是一个包含了CDR表中每一个字段的列表.我们遍历这个列表寻找匹配的值.如果找到一个,我们使用urldecode和添加字段名到@fields及字段的值存储到@values.从这里,我们使用@fields和@values阵列,并创建 一个查询字符串,然后将其插入到数据库中。
See also
参考本章前些提到的秘决Using XML CDRs and Inserting records into a backend database

Using the event socket to handle CDRs

使用event socket处理CDR
有些时候你需要立刻获取到CDR的信息. FreeSWITCH通过非常强大的event socket来适应这类需求.本小节的秘方将会简短的描述如何在event socket上来接收CDR信息.本章节中你将会发现event socket接口上很多非常有用的信息.
Getting ready
本方法主要是依靠FreeSWITCH的event socket接口.不过,连接到event socket有很多种方式.正因为如此,我们要使用一个引用了event socket library库的简单Perl脚本来演示复杂的理论.任何支持ESL的语言都可以使用这里所演示的方法. 按照第4章 中的小窍门Setting up the event socket library设置event socket. 特别要说,为了执行示例的脚本一定要编译Perl模块.
How to do it…
Enter this script (or download it from the Packt website athttp://www.packtpub.com): 加入下面的脚本(或从Packt网站下载http://www.packtpub.com)

#!/usr/bin/perl

# handle_cdr.pl
# Connect to event socket, listen for CHANNEL_HANGUP_COMPLETE events
# Uses event data to create custom CDRs
  use strict;
  use warnings;
  use lib '/usr/src/freeswitch.git/libs/esl/perl';
  use ESL;

    my $host = "localhost"; my $port = "8021";
    my $pass = "ClueCon";
    my $con  = new ESL::ESLconnection($host, $port, $pass);
      if ( ! $con ) {
        die "Unable to establish connection to FreeSWITCH.\n";
      }

  ## Listen for events, filter in only CHANNEL_HANGUP_COMPLETE
    $con->events('plain','all');
    $con->filter('Event-Name','CHANNEL_HANGUP_COMPLETE');
      print "Connected to FreeSWITCH $host:$port and waiting for events...\n\n";
      while (1) {
        my @raw_data = split "\n",$e->serialize();
        my %cdr;
        foreach my $item ( @raw_data ) {
          #print "$item\n";
          my ($header, $value) = split ': ', $item;
          $header =~ s/^variable_//;
          $cdr{$header} = $value;
        }

        # %cdr contains a complete list of channel variables
        print "New CDR: ";
        print $cdr{uuid} . ', ' . $cdr{direction} . ', ';
        print $cdr{answer_epoch} . ', ' . $cdr{end_epoch} . ', ';
        print $cdr{hangup_cause} . "\n";
      }

运行本脚本和打一个测试电话. 一个简短的CDR将会被打印到屏幕上.
按Ctrl+C去退出脚本.

How it works…

这其中包含的基础原理如下:
1.建立一个ESL连接到Freeswitch
2.通过使用filter订阅事件CHANNEL_HANGUP_COMPLETE
3.把每个事件当成一个独立的CDR来处理 如果你更熟悉PHP, Python或Ruby你应该可以从我们的示例脚本转换这些概念到PHP,Python和Ruby. .
There’s more…

这里是一个小的提示, 帮助你了解使用event socket获取CDR最常用的用法.

ESL considerations

记住脚本需要找到ESL库. 注意此行: use lib ‘/usr/src/freeswitch.git/libs/esl/perl’; 这就是告诉Perl当使用附加的模块时在这个特定的目录下寻找.如果不添加这行, ESL指令会失败(或者你可以把必须的文件装到你的Perl的系统目录).
另一个重点是这种方法一通通话将会收到两个事件.A leg和B leg 都会生成一个 CHANNEL_HANGUP_COMPLETE 事件. A Leg的$cdr{direction}是”inbound”以及B Leg 的这个值是”outboud”.
最后, 记住这一行是阻塞的:
my $e = $con->recvEvent();
它会阻塞整个脚本直到有一个新的事件到达. 请参看第四章的过滤事件小技巧去了解一个recvEventTimed()方法的示例, 它是不阻塞的.
Receiving XML CDRs
通过event socket接收XML格式的CDR也是可以的. 这是是建立在每个呼叫的基础上, 使用hangup_complete_with_xml这个通道变量. 在你的diaplan中像下面这样设置为true:
<action application=”set” data=” hangup_complete_with_xml=true”/>
See also
Refer to the Using XML CDRs recipe in this chapter for more information on XML-based CDRs
Refer to the Setting up the event socket library and Filtering events recipes in Chapter 4

评论已关闭。