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