我们在做Asterisk功能开发的时候,除了直接用C写模块之前,更常用的方法是用自己熟悉的语言来开发自己的业务逻辑,这个时候就需要用到AGI的功能。一般比较多的选择是:PHP,Perl,JAVA 来实现。JAVA因为是FASTAGI的方式,故效果会比PHP,Perl好点,因为PHP,Perl是脚本语音,需要进行编译操作,故这个CPU消耗还是比较大的。
    另外一种方式是asterisk 的dialplan 是可以直接支持lua 脚本来编写,这个就很方便来用lua 来做扩展。经过实测使用Lua方式来做嵌入式来扩展业务,这个性能确实有质的飞跃。
    故本次将介绍使用Lua来写dialPlan。

    官方文档指引,请参考:
    https://wiki.asterisk.org/wiki/display/AST/Lua+Dialplan+Configuration

    通过官方文档的介绍,其实extensions.lua 其实就是一个lua 脚本,直接在上面写代码即可。

    关于数据存储的选择技巧:
    如果要快速响应呼叫请求,可以利用redis 来做缓存,使用redis 的列表功能进行消息推送。把话单和通话状态通过外部的监控服务来推送到MySQL。

    经过上面的改造后,Asterisk的性能得到了很大的提升,下面是具体的测试效果:

    CPU型号:
            Intel(R) Core(TM) i3-4020Y CPU @ 1.50GHz 
    CPU个数:4

    内存:4G

    Asterisk版本:Asterisk 13.21.0

    上面安装了2套Asterisk,一套做接入转码,一套做模拟正常的30%的接通率。

    稳定并发:150线并发

    一台微型小机器,即可满足大部分公司的业务需求。

    下面附上lua dialplan 的使用例子:
function hangup()
    app.Hangup()
end

function verbose(msg)
    app.Verbose('"' .. msg .. '"')
end

function noop(msg)
    app.Noop('"' .. msg .. '"')
end

function dial(dialstr)
    app.Dial(dialstr)
end

function getChannelItem(item)
    return channel.CHANNEL('"' .. item .. '"'):get()
end

function getCallerIdItem(item)
    return channel.CALLERID('"' .. item .. '"'):get()
end

function getVar(key)
    -- key : CALLERID(num) ,EXTEN , CHANNEL
    return channel[key]:get();
end

function setVar(key,val)
    channel[key] = val;
end

e = {};

e.default = {};
e.default["_X."] = function(context,exten)
    app.Noop("incoming call to default")
    app.Hangup()
end;

e.TO_EXT_UserName = {};
e.TO_EXT_UserName["_X."] = function(context,exten)
    local peerIp = channel.CHANNEL('peerip'):get()
    local userAgent = channel.CHANNEL('useragent'):get()
    local uriHeader = channel.CHANNEL('ruri'):get()
    local from = channel.CHANNEL('from'):get()
    local concat = getChannelItem('uri')

    local caller = getCallerIdItem('num');
    local callerName = getCallerIdItem('name')

    local uniqueid = getVar('UNIQUEID');
    local channelName = getVar('CHANNEL');
    setVar('CALLERID(num)','+8615875329063')
    local msg = 'incoming call: ' .. peerIp .. ',useragent: '.. userAgent .. ',request uri: ' .. uriHeader ..',from header: ' .. from .. ',concat header: ' .. concat ;
    verbose(msg);
    msg = "caller: " .. caller ..",callerName: " .. callerName ..',uniqueid: ' .. uniqueid .. ',channel name: ' .. channelName;
    noop(msg);
    caller = getVar('CALLERID(num)');
    noop("Your new callerid is: " .. caller)
    local res = dial("SIP/IMS_PROXY/" .. exten .. ',45,geU(sub_record_ims^s)')

    hangup()
end;

e.TO_EXT_UserName["h"] = function(context,exten)
    local msg = '';
    local caller = getCallerIdItem('num');
    local callerName = getCallerIdItem('name')

    local uniqueid = getVar('UNIQUEID');

    local duration = getVar('DIALEDTIME');
    if duration == nil or duration == '' then
        duration = 0;
    end

    local billsec = getVar('ANSWEREDTIME');
    if billsec == nil or billsec == '' then
        billsec = 0;
    end

    local dialresult = getVar('DIALSTATUS');
    if dialresult == nil then
        dialresult = ''
    end
    -- 获取挂断通道名称
    local hangupCauseString = getVar('HANGUPCAUSE_KEYS()');

    msg = "hangup==> caller: " .. caller .. ',is hangup,uniqueid='.. uniqueid .. ',duration='.. duration .. ',billsec='.. billsec .. ',result: ' .. dialresult;
    noop(msg)
    -- 获取挂断原因SIP代码
    local hangupCause = getVar('HANGUPCAUSE("' .. hangupCauseString ..'",tech)');   

    msg = "hangup by: " .. hangupCauseString .. ',hangupCause: ' .. hangupCause;
    noop(msg)

end;

e.sub_record_ims = {};
e.sub_record_ims['s'] = function(context,exten)
    -- 当被叫接通主叫前执行
    local msg = "";
    local caller = getCallerIdItem('num');
    local callerName = getCallerIdItem('name')

    local uniqueId = getVar('UNIQUEID');
    msg = "caller: " .. caller .. ',callerName: ' .. callerName .. ',uniqueId: ' .. uniqueId .. ' is answered ...'; 
    noop(msg);
    app['Return']();
end

extensions = e;