眾所周知,F(xiàn)reeSWITCH中可以使用嵌入式的腳本語(yǔ)言javascript、lua等來控制呼叫流程。而更復(fù)雜一點(diǎn)操作可能就需要使用Event Socket了。其實(shí)不然,嵌入式的腳本也可以一直運(yùn)行,并可以監(jiān)聽所有的Event,就像使用Event Socket起一個(gè)單獨(dú)的Daemon一樣。
這里我們以lua為例來講一下都有哪些限制以及如何解決。
首先,在控制臺(tái)或fs_cli中執(zhí)行一個(gè)Lua腳本有兩種方式,lua和luarun。二者的不同就是lua是在當(dāng)前線程中運(yùn)行的,所以,它會(huì)阻塞;而luarun會(huì)spawn一個(gè)新的線程,不會(huì)阻塞當(dāng)前的線程執(zhí)行。
另外,你也可以寫到lua.conf配置文件中,這樣它就能隨FreeSWITCH一起啟動(dòng)。
<param name="startup-script" value="gateway_report.lua"/>
腳本后面可以加參數(shù),如 luarun test.lua arg1 arg2,在腳本中,就可以通過argv[1], argv[2]來獲得參數(shù)的值。而argv[0]是腳本的名字。
如果要讓腳本一直運(yùn)行,腳本中必須有一個(gè)無限循環(huán)。你可以這樣做:
while true do
-- Sleep for 500 milliseconds
freeswitch.msleep(500);
freeswitch.consoleLog("info", "blah...");
end
但這樣的腳本是無法終止的,由于FreeSWITCH使用swig支持這些嵌入式語(yǔ)言,而有些語(yǔ)言沒有退出機(jī)制,所以,所有語(yǔ)言的退出機(jī)制都沒有在FreeSWITCH中實(shí)現(xiàn),即使unload相關(guān)的語(yǔ)言模塊也不行,也是因?yàn)槿绱,為了避免產(chǎn)生問題,所有語(yǔ)言模塊也都不能unload。
另外,使用freeswitch.msleep() 也不安全,Wiki上說: Do not use this on a session-based script or bad things will happen。
既然是長(zhǎng)期運(yùn)行的腳本,那,為什么為停止呢?是的,大部分時(shí)間你不需要,但,如果你想修改腳本,總不會(huì)每次都重啟FreeSWITCH吧?尤其是在調(diào)試的時(shí)候。
那,還有別的辦法嗎?
我們可以使用事件機(jī)制構(gòu)造另一個(gè)循環(huán):
con = freeswitch.EventConsumer("all");
for e in (function() return con:pop(1) end) do
freeswitch.consoleLog("info", "event\n" .. e:serialize("xml"));
end
上面的代碼中,con被初始化成一個(gè)事件消費(fèi)者。它會(huì)一直阻塞并等待FreeSWITCH發(fā)出一個(gè)事件,并打印該事件的XML表示。當(dāng)然,事件總會(huì)有的。如每個(gè)電話初始化、掛機(jī)等都會(huì)有相應(yīng)的事件。除此之外,F(xiàn)reeSWITCH內(nèi)部也會(huì)毎20秒發(fā)出一個(gè)heartbeat事件,這樣你就可以定時(shí)執(zhí)行一些任務(wù)。
當(dāng)然如果使用 con:pop(0)也可以變成無阻塞的,但你必須在循環(huán)內(nèi)部執(zhí)行一些sleep()以防止腳本占用太多的資源。
通過這種方法,你應(yīng)該就能想到辦法讓腳本退出了。那就是,另外執(zhí)行一個(gè)腳本觸發(fā)一個(gè)custom的事件,當(dāng)該腳本監(jiān)測(cè)到特定的custom事件后退出。當(dāng)然你也可以不退出,比方說,打印一些信息以用于調(diào)試。
我寫了一個(gè)gateway_report.lua腳本。就用了這種技術(shù)。思路是:監(jiān)聽所有事件。如果收到hangup,則判斷是通過哪個(gè)gateway出去的,并計(jì)算一些統(tǒng)計(jì)信息。如果需要保存這些信息,可以有以下幾種方式:
- fire_event,即觸發(fā)另一個(gè)事件,這樣,如果有其它程序監(jiān)聽,就可以收到這個(gè)事件,從而可以進(jìn)行處理,如存入數(shù)據(jù)庫(kù)等。
- http_post,發(fā)一個(gè)HTTP post請(qǐng)求到一個(gè)HTTP server,HTTP server接收到請(qǐng)求后進(jìn)行下一步處理。其中,http_post是無阻塞的,以提高效率,即只發(fā)請(qǐng)求,而不等待處理結(jié)果。
- db,可以通過luasql直接寫到數(shù)據(jù)庫(kù),未完全實(shí)現(xiàn)
- 當(dāng)然你也可以直接通過io.open寫到一個(gè)本地文件,未實(shí)現(xiàn)...
由于這種腳本會(huì)在FreeSWITCH內(nèi)部執(zhí)行,需要消耗FreeSWITCH的資源,因此,在大話務(wù)量(確切來說是“大事件量”)的情況下還是應(yīng)該用Event Socket。