《一步一步開發呼叫中心系列》
編寫基於三匯板卡的呼叫中心之前,我們得先理解三匯提供的4種驅動程序事件處理模式:
1.輪詢模式:由應用程序不停地調用驅動程序提供的相關查詢函數,以獲取任務的進展情況。這種編程方式由於消耗計算機的資源較多,效率很低,只能適用於容量較小的應用系統,目前已經被逐漸淘汰。
2.事件等待:應用程序調用驅動程序提供的事件等待函數,當驅動程序沒有事件時,應用程序的調用者線程被阻塞;當驅動程序拋出某個事件時,應用程序的調用者線程被重新激活,恢復對事件的處理。
3.事件回調:由應用程序向驅動程序注冊一個回調函數,當驅動程序有事件發生時,由驅動程序調用回調函數,對事件進行處理。
4.Windwos消息模式(只適用於Windows操作系統,且輸出事件的數據結構只能是MESSAGE_INFO):驅動程序將事件發送到Windows的消息隊列中,通過Windows統一的消息隊列處理機制,來對事件進行處理。由於Windows消息模式可以攜帶的參數有限,因此實際使用這種編程模式的應用程序並不多。
這4中模式各有優缺點,每個模式的使用場景在官方文檔上面已經寫得比較清楚。下面我再基於自己的理解進行總結。
輪詢模式
輪詢模式就像我們的緩存自動更新模塊,使用一個死循環,不停地掃描數據狀態,如果數據發生了變更,再根據業務場景進行業務處理。
在三匯語音板卡中,輪詢模式也是一個道理:使用一個死循環,不停地掃描板卡中每個通道的狀態,當通道狀態發生變更之後,再根據上一次狀態與當前狀態判斷應該進入什麼樣的業務流程處理。
一般來說,在10個以下通道(包括坐席和外線)的場景下,這個模式還是可以使用的,但是相應的,還是效率會有明顯的降低。而且這樣做的話,基本上這個呼叫中心已經沒有了任何的可擴展性,因為一個公司如果需要添加更多的外線或者坐席,這種模式消耗的資源將完全拖累正常的呼叫系統使用。
通常,這種模式僅用於三匯內部對外提供的Demo中。實際生產過程中,幾乎沒人會用這種吃力不討好的事情。
事件等待模式
這種模式是官方推薦使用的模式,也是本系統目前采用的模式。
有點類似於C#多線程中的線程阻塞,一直到指定的委托或線程返回結果為止。
此模式消耗的系統資源較少,僅需要開啟一個後台線程,編寫一個死循環,在循環中阻塞等待直到三匯驅動返回事件為止,再根據事件參數進行業務處理。
據說這種模式,最多支持單卡500坐席的規模。
事件回調模式
顧名思義,與C#中的事件回調模式相差無幾。
啟用這種模式之後,系統不需要開啟後台死循環線程,僅需要提供一個內存中存在的函數指針,即可實現。當驅動程序有事件時,將直接把事件信息回調此函數指針,回調到我們編寫的應用程序中。
這種模式是三匯官方推薦的第二種方案,前期我也使用了一段時間,但是不知道由於什麼原因,始終會有事件回調不回來,故改為事件等待模式。
如果哪位園友對此模式研究甚深,請不吝指教。
Windows消息模式
此模式,不僅可以使用Windows消息隊列,還可以使用Windows窗口句柄作為消息處理載體。由於並沒有對此模式進行深入研究,此處不做細表。
無論如何,我們決定了選擇使用輪詢模式來開發我們的呼叫中心CTI系統。
事件等待模式,雖然作為官方推薦的模式,但是仍然有很多細節問題需要我們注意:
1.事件等待的線程,必須作為單獨的後台線程存在,避免線程阻塞導致整個CTI程序無響應。
2.接收到驅動程序事件返回之後,必須重新啟動一個新的後台線程進行業務處理,避免業務處理時間過長,丟失掉驅動程序後續的事件返回信息。
事件模式確認之後,我們需要知曉如何來等待驅動程序的事件返回,這就需要用到SsmWaitForEvent(或者SsmWaitForEventA函數,兩者無實際區別)
關於SsmWaitForEvent函數
獲取Synway驅動程序輸出的事件。異步函數,如果有事件則立即返回;如果沒有事件,調用者線程將被阻塞,直到有事件發生或者超時才返回。
關於此函數的使用方式,可參考下面的源碼:
private static void Polling() { // 開啟死循環,進行事件等待 while (true) { // 事件信息 MESSAGE_INFO Event = new MESSAGE_INFO(); // 等待並響應驅動程序拋出的事件 // 參數0xffff表示:如果沒有事件,函數一直掛起,直到有事件時才返回; if (ApiFunction.SsmWaitForEvent(0xffff, ref Event) == 0) { EventParameter p = new EventParameter() { Event = (EventCode)Event.wEventCode, Ch = Event.nReference, DwParam = (int)Event.dwParam }; // 新開啟線程進行事件處理 // 參數ProcessEvent為內部事件處理函數句柄 ThreadPool.QueueUserWorkItem(ProcessEvent, p); } } }
事件等待模式的確是效率最高的模式,然而,語音板卡的事件有很多(至少我是沒有數清到底有多少種事件)如果每一個事件都返回到我們的CTI中處理一次,肯定會給CTI程序帶來一定的壓力,所以,我們在啟動事件模式時,還應該對需要驅動程序返回的事件,進行一次篩選設置,告訴驅動程序,只返回我們關心的事件信息,無用的或者CTI不關心的事件,可以不用返回。
在三匯的驅動包中,有這麼一個函數:SsmSetEvent,這是一個神一般的函數,它既擔任了設置事件模式的責任,又擔任了過濾事件的責任,僅僅是因為參數不一樣從而導致作用不一樣。
函數原型:
int SsmSetEvent(WORD wEvent, int nReference, BOOL bEnable, PEVENT_SET_INFO pEventSet)
其中,wEvent參數含義如下:
0~0xfffe:事件編碼,取值范圍請參見第1章中“MESSAGE_INFO”部分內容。此時,SsmSetEvent用於通知驅動程序拋出或不拋出特定事件。
0xffff: 設置整個驅動程序的工作模式,即事件等待模式和事件回調模式,此時參數nReference必須為-1。
可參考如下源碼:
// 配置文件DefaultEventOutput項已經配置為0,不輸出任何事件 EVENT_SET_INFO set = new EVENT_SET_INFO(); set.dwWorkMode = (int)EventMode.EVENT_POLLING; // 事件列表 List<ushort> events = new List<ushort>(); // 設置事件模式 events.Add(0xffff); // E_CHG_RcvDTMF 收到一個Dtmf events.Add(0x000C); // E_CHG_ChState 通道狀態改變事件 events.Add(0x0018); // E_CHG_RingCount 模擬中繼線通道:鈴流信號檢測器中信號周期的計數器發生變化 events.Add(0x001E); // E_CHG_FlashCount 坐席通道或者錄音通道:在電話機上檢測到一次閃斷操作 events.Add(0x0023); // E_PROC_PlayEnd 放音任務結束 events.Add(0x000F); int r = 0; foreach (var item in events) { r = ApiFunction.SsmSetEvent(item, -1, 1, ref set); if (r != 0) { SendErrorMessage("事件初始化失敗。"); return; } }
好了,對於三匯語音板卡呼叫中心的事件模式就總結到這裡,下一篇我們將介紹如何設計事件響應核心CTI系統。
歡迎大家拍磚。
另外,本人QQ:416263499,歡迎交流。