使用Sipp压测FreeSwitch实践

背景

笔者所在团队由于涉及自建呼叫中心业务,而针对客服系统这类存在大量的呼叫场景来说,呼叫服务的可靠性显得尤为重要。很显然对于一个基于Sip协议的呼叫业务来说,不可能雇佣几千人同时拨打电话,而传统压测工具(Grinder、Jmeter)等也无法满足Sip协议压测的需求,那么怎么办?FreeSwitch官方文档推荐一款简单易用的压测工具--Sipp,本文简单介绍如何应用Sipp工具对七鱼FreeSwitch服务进行压测的实践。

一、基本概念

  • Sip:会话发起协议(Session Initiation Protocol,缩写SIP),是用于VoIP最主要的信令协议之一。
  • Sipp:Sip的压力测试工具,可以简单模拟Sip流程中的各个场景,支持TCP和UDP传输,支持自定义编写脚本。
  • FreeSWITCH:一个开源的电话软交换平台,可用于创建音频、视频以及短消息类产品等应用。

关于Sip协议的基础本文不扩展讲了,类似于Http协议,主要是两个关键点:协议信令和状态码,如果需要从事相关测试,那么Sip协议是必须掌握的基础。

二、Sipp安装

这部分没有什么难度,按照Sipp的官网上的安装步骤安装即可。 Sipp官网

三、Sipp压测指标

平时我们在做WEB的压力测试时,会关注应用的TPS、支持的并发能力、RT、JVM等指标,当然还有一系列的硬件指标。而SIP服务器与我们平时的应用服务器会有一些差别,SIP服务器(FreeSWITCH是一种SIP服务器)会负责会话的建立及音视频数据的传输和编解码,我们知道一通电话呼叫所持续的时间,长则上百上千秒,短则只有一两秒,所以在SIP服务器的压力测试上,虽然依然存在TPS、并发能力等指标,但是与我们平时理解的概念会稍微有一些区别。

SIP压测的指标主要有两个:

  • CPS:Call Per Second,每秒能够处理的通话数,它代表的是每秒钟FreeSWITCH能够建立或者释放Channel的能力

  • Channel:FreeSWITCH本身是一个背靠背的用户代理,因此通过FreeSWITCH建立的一个呼叫,会存在多个数据链路,我们称之为Channel,比如一个单呼可能只有一个Channel,而一个双呼则有两个Channel,一个电话会议则可能有N个Channel

四、压测脚本编写

对于笔者的项目,主要涉及两种场景的压测,一是用户注册、二是用户外呼,我们分别来看一下测试脚本该如何实现。

1. UAC注册

一个完整的sip注册流程如下图所示:

编写sipp_uac_register.xml测试脚本如下:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">

<scenario name="registration">

<send retrans="500">
<![CDATA[
REGISTER sip:cc.qiyukf.com SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
Max-Forwards: 70
From: "sipp" <sip:[field0]@cc.qiyukf.com>;tag=[call_number]
To: "sipp" <sip:[field0]@cc.qiyukf.com>
Call-ID: reg///[call_id]
CSeq: 7 REGISTER
Contact: <sip:[field0]@[local_ip]:[local_port]>
Expires: 90
Content-Length: 0
User-Agent: SIPp
]]>
</send>

<recv response="100" optional="true">
</recv>

<recv response="401" auth="true" rtd="true">
</recv>

<send retrans="500">
<![CDATA[
REGISTER sip:cc.qiyukf.com SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
Max-Forwards: 70
From: "sipp" <sip:[field0]@cc.qiyukf.com>;tag=[call_number]
To: "sipp" <sip:[field0]@cc.qiyukf.com>
Call-ID: reg///[call_id]
CSeq: 8 REGISTER
Contact: <sip:[field0]@[local_ip]:[local_port]>
Expires: 90
Content-Length: 0
User-Agent: SIPp
[field1]
]]>
</send>

<recv response="100" optional="true">
</recv>

<recv response="200">
</recv>

<ResponseTimeRepartition value="10, 20"/>
<CallLengthRepartition value="10"/>
</scenario>

Sipp注册执行命令:

sipp 59.XXX.XXX.XXX:5160 -i 10.XXX.XXX.XXX -p 5050 -s 999999999 -r 1000 -inf register_data.csv -sf sipp_uac_register.xml -trace_screen
  • -s:指定外呼号码
  • -sf:指定注册脚本
  • -inf:指定注册用户信息
  • -i:Sipp所在主机地址
  • -p:Sipp端口号
  • -r:每秒发送多少Sip消息
  • -trace_screen:当程序结束时候打印统计信息并弹出屏幕

注册压测结果输出:


测试完成后Sipp会输出相应的呼叫次数,成功、失败次数及各种时长信息等,除此,我们还需要关注服务器CPU、内存、IO、网络指标,以及进入FreeSWITCH查看注册的正确率,以保证性能测试的可靠。

2. UAC外呼

一个完整的Sip外呼流程如下图所示:

编写sipp_uac_basic.xml测试脚本如下:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">

<scenario name="Basic Sipstone UAC">

<send retrans="500">
        <![CDATA[
        INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
        Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
        From: sipp <sip:[field0]@cc.qiyukf.com>;tag=[pid]SIPpTag00[call_number]
        To: sut <sip:[service]@cc.qiyukf.com>
        Call-ID: [call_id]
        CSeq: 1 INVITE
        Contact: sip:[field0]@[local_ip]:[local_port];transport=udp
        Max-Forwards: 70
        Subject: Performance Test
        Content-Type: application/sdp
        Content-Length: [len]

        v=0
        o=[field0] 53655765 2353687637 IN IP4 [local_ip]
        s=Talk
        c=IN IP4 [local_ip]
        t=0 0
        m=audio [auto_media_port] rtp/avp 0 8 9 101
        a=rtpmap:9 G722/8000
        a=rtpmap:0 pcmu/8000
        a=rtpmap:8 pcma/8000
        a=rtpmap:101 telephone-event/8000
        a=fmtp:101 0-11,16
        a=sendrecv
        a=ptime:20
        ]]>
    </send>

    <recv response="100">
    </recv>

    <recv response="407" option="true" auth="true">
    </recv>

    <send>
        <![CDATA[
        ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0
        Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch-3]
        From: sipp <sip:[field0]@cc.qiyukf.com>;tag=[pid]SIPpTag00[call_number]
        To: sut <sip:[service]@cc.qiyukf.com>[peer_tag_param]
        Call-ID: [call_id]
        CSeq: 1 ACK
        Contact: sip:[field0]@[local_ip]:[local_port];transport=udp
        Max-Forwards: 70
        Subject: Performance Test
        Content-Length: 0
        ]]>
    </send>

    <send retrans="500">
        <![CDATA[
        INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
        Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
        From: sipp <sip:[field0]@cc.qiyukf.com>;tag=[pid]SIPpTag01[call_number]
        To: sut <sip:[service]@cc.qiyukf.com>
        Call-ID: [call_id]
        CSeq: 2 INVITE
        Contact: sip:[field0]@[local_ip]:[local_port];transport=udp
        [field1]
        Max-Forwards: 70
        Subject: Performance Test
        Content-Type: application/sdp
        Content-Length:[len]

        v=0
        o=[field0] 53655765 2353687637 IN IP4 [local_ip]
        s=Talk
        c=IN IP4 [local_ip]
        t=0 0
        m=audio [auto_media_port] rtp/avp 0 8 9 101
        a=rtpmap:9 G722/8000
        a=rtpmap:0 pcmu/8000
        a=rtpmap:8 pcma/8000
        a=rtpmap:101 telephone-event/8000
        a=fmtp:101 0-11,16
        a=sendrecv
        a=ptime:20
        ]]>
    </send>

    <recv response="100">
    </recv>

    <recv response="180" optional="true">
    </recv>

    <recv response="183" optional="true">
    </recv>

    <recv response="200" rtd="true">
    </recv>

    <send>
        <![CDATA[
        ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0
        Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch-5]
        From: sipp <sip:[field0]@cc.qiyukf.com>;tag=[pid]SIPpTag01[call_number]
        To: sut <sip:[service]@cc.qiyukf.com>[peer_tag_param]
        Call-ID: [call_id]
        CSeq: 2 ACK
        Contact: sip:[field0]@[local_ip]:[local_port];transport=udp
        [field1]
        Max-Forwards: 70
        Subject: Performance Test
        Content-Length: 0
        ]]>
    </send>

    <nop>
      <action>
      <exec play_pcap_audio="g722.pcap"/>
      </action>
    </nop>

    <pause milliseconds = "30000"/>

    <send retrans="500">
        <![CDATA[
        BYE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
        Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch-7]
        From: sipp <sip:[field0]@[local_ip]:[local_port]>;tag=[pid]SIPpTag01[call_number]
        To: sut <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
        Call-ID: [call_id]
        CSeq: 2 BYE
        Contact: sip:[field0]@[local_ip]:[local_port]
        Max-Forwards: 70
        Subject: Performance Test
        Content-Length: 0
        ]]>
    </send>

    <recv response="200" crlf="true">
    </recv>

    <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
    <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
</scenario>

UAC外呼主要模拟一个用户呼叫某个电话,电话接通后持续说话30s,之后挂断的场景,也是大部分客服经常遇到的真实场景。

Sipp外呼执行命令:

sipp 59.xxx.xxx.xxx:5160 -i 10.xxx.xxx.xxx -p 5050 -s 999999999 -m 5000 -r 50 -t tn  -max_socket 5000  -sf sipp_uac_basic.xml -inf qiyutest.csv -rtp_echo -trace_screen
  • -s:指定外呼号码
  • -sf:指定注册脚本
  • -inf:指定注册用户信息
  • -i:Sipp所在主机地址
  • -p:Sipp端口号
  • -r:每秒发送多少Sip消息
  • -m:共发送多少Sip消息
  • -t:使用tcp的传输模式,公司内网udp有限制,tn代表每个呼叫占用一个socket
  • -rtp:启用 rtp 回送功能
  • -trace_screen:当程序结束时候打印统计信息并弹出屏幕

UAC外呼压测结果输出:


在Sipp施压的情况下,结合服务器的硬件性能数据,可以不断的调整CPS和持续时间,来最终获取在特定硬件配置的情况下,服务器稳定运行时FreeSWITCH能够获得的最大CPS和Channel并发值。

总结

毕竟Sip的测试对于传统Web测试来说确实属于小众,网上可借鉴资料也相对较少,笔者也是第一次接触,并且在测试过程中也是踩坑无数,特意在ks中搜索了一把,本文应该算是ks中第一篇关于Sip压测的文章,实属荣幸,同时欢迎从事相关测试的同学相互交流。

本文来自网易实践者社区,经作者李赫授权发布。