文/顧鵬程
OCI是Oracle公司提供的Oracle數據庫的C接口。一些監控系統,如軌道交通監控系統、電力調度系統等,需要在Linux平臺對OCI進行封裝。這類系統實時性要求較高,而OCI接口在Linux平臺存在斷網阻塞問題,這大大影響了系統實時性。本文提出一種對已封裝Linux平臺OCI接口的改進方法,可在接口斷網阻塞時快速切換至備網。
長期不間斷運行的系統不可避免會出現數據庫服務器網線插拔或松動等問題,如果故障未及時恢復,OCI函數將長時間阻塞,例如在Ubuntu系統可能長達二十多分鐘,這在大多數情況下是不允許的。監控系統一般配備主備網絡,因此開發者往往希望當發生阻塞時,數據庫接口能夠自動切換網絡。
在Linux系統中對于阻塞的解決通常使用sigalarm信號,指定信號觸發時間,在可能的阻塞模塊前調用alarm函數。經過實驗,該方法只是對現有進程的打斷,無法對接口函數給出錯誤返回值,且需退出進程,無法保證事務連貫。
還有采用線程和條件變量相結合的處理方法,將接口函數置于線程中執行,用條件變量計時等待接口函數返回。該方法不必退出程序,但未考慮主備網絡切換,且條件變量方法存在弊端,即當信號先于等待發出時,信號將不再起作用,導致等待無法返回。

圖1:任務流程
本文以上方法進行總結,提出一種結合泛型、線程、信號量方法的阻塞式任務線程方法,避免了上述方法的弊端,能夠在接口阻塞時自動切換網絡。
首先在創建數據庫連接時,創建一個任務線程,負責執行接口函數。接口函數通過泛型進入線程。當線程被獲得任務時,觸發“任務”信號以執行任務。當任務結束時,觸發“返回”信號,主線程返回結果。調用接口函數的模塊在獲得“返回”信號前將一直阻塞并計時,當任務超時,采取tnsping的方式對網絡狀況進行判斷,若網絡未斷將繼續等待;若網絡斷開,將創建備用線程和備用網絡數據庫連接,而后執行阻塞任務,并退出原線程。具體流程如圖1所示。
本文采用VC10實現。泛型部分參考了任務隊列的方法,增加了支持不同返回類型的修改。首先定義模版類class Base,結構體struct task_unit由class Base指針對象構造,表示任意函數任務。任務的產生由template
阻塞任務隊列部分采用ACE庫實現,也可以選擇Linux的C++標準庫實現。線程類繼承ACE_Task_Base,信號量采用ACE_Semaphore,“任務”信號初始化為 t_sem(0),“返回”信號初始化為r_sem(0)。線程通過調用t_sem.acquire()等待任務注入。主線程將任務注入任務線程后,調用t_sem.release()觸發任務執行,調用r_sem.acquire(&timeout)等待任務完成,其中timeout為超時設置。任務結束時,線程調用ret_sem.release(),使主線程獲得返回值。
本文方法對Linux平臺OCI接口的網絡阻塞問題進行處理,針對已封裝的接口函數,只需利用泛型、多線程、信號量,即可構造一個通用的阻塞模式任務隊列,將接口函數置于阻塞任務隊列下,在網絡阻塞時,通關新的線程調用備用網絡,解決了原本OCI接口函數長時間阻塞的問題。經過實驗,該方法對舊有接口的改造工作量小,并且能實現應用的數據庫連接在網絡發生通斷問題時進行自動切換。