文/顧鵬程
OCI是Oracle公司提供的Oracle數(shù)據(jù)庫(kù)的C接口。一些監(jiān)控系統(tǒng),如軌道交通監(jiān)控系統(tǒng)、電力調(diào)度系統(tǒng)等,需要在Linux平臺(tái)對(duì)OCI進(jìn)行封裝。這類系統(tǒng)實(shí)時(shí)性要求較高,而OCI接口在Linux平臺(tái)存在斷網(wǎng)阻塞問(wèn)題,這大大影響了系統(tǒng)實(shí)時(shí)性。本文提出一種對(duì)已封裝Linux平臺(tái)OCI接口的改進(jìn)方法,可在接口斷網(wǎng)阻塞時(shí)快速切換至備網(wǎng)。
長(zhǎng)期不間斷運(yùn)行的系統(tǒng)不可避免會(huì)出現(xiàn)數(shù)據(jù)庫(kù)服務(wù)器網(wǎng)線插拔或松動(dòng)等問(wèn)題,如果故障未及時(shí)恢復(fù),OCI函數(shù)將長(zhǎng)時(shí)間阻塞,例如在Ubuntu系統(tǒng)可能長(zhǎng)達(dá)二十多分鐘,這在大多數(shù)情況下是不允許的。監(jiān)控系統(tǒng)一般配備主備網(wǎng)絡(luò),因此開(kāi)發(fā)者往往希望當(dāng)發(fā)生阻塞時(shí),數(shù)據(jù)庫(kù)接口能夠自動(dòng)切換網(wǎng)絡(luò)。
在Linux系統(tǒng)中對(duì)于阻塞的解決通常使用sigalarm信號(hào),指定信號(hào)觸發(fā)時(shí)間,在可能的阻塞模塊前調(diào)用alarm函數(shù)。經(jīng)過(guò)實(shí)驗(yàn),該方法只是對(duì)現(xiàn)有進(jìn)程的打斷,無(wú)法對(duì)接口函數(shù)給出錯(cuò)誤返回值,且需退出進(jìn)程,無(wú)法保證事務(wù)連貫。
還有采用線程和條件變量相結(jié)合的處理方法,將接口函數(shù)置于線程中執(zhí)行,用條件變量計(jì)時(shí)等待接口函數(shù)返回。該方法不必退出程序,但未考慮主備網(wǎng)絡(luò)切換,且條件變量方法存在弊端,即當(dāng)信號(hào)先于等待發(fā)出時(shí),信號(hào)將不再起作用,導(dǎo)致等待無(wú)法返回。
圖1:任務(wù)流程
本文以上方法進(jìn)行總結(jié),提出一種結(jié)合泛型、線程、信號(hào)量方法的阻塞式任務(wù)線程方法,避免了上述方法的弊端,能夠在接口阻塞時(shí)自動(dòng)切換網(wǎng)絡(luò)。
首先在創(chuàng)建數(shù)據(jù)庫(kù)連接時(shí),創(chuàng)建一個(gè)任務(wù)線程,負(fù)責(zé)執(zhí)行接口函數(shù)。接口函數(shù)通過(guò)泛型進(jìn)入線程。當(dāng)線程被獲得任務(wù)時(shí),觸發(fā)“任務(wù)”信號(hào)以執(zhí)行任務(wù)。當(dāng)任務(wù)結(jié)束時(shí),觸發(fā)“返回”信號(hào),主線程返回結(jié)果。調(diào)用接口函數(shù)的模塊在獲得“返回”信號(hào)前將一直阻塞并計(jì)時(shí),當(dāng)任務(wù)超時(shí),采取tnsping的方式對(duì)網(wǎng)絡(luò)狀況進(jìn)行判斷,若網(wǎng)絡(luò)未斷將繼續(xù)等待;若網(wǎng)絡(luò)斷開(kāi),將創(chuàng)建備用線程和備用網(wǎng)絡(luò)數(shù)據(jù)庫(kù)連接,而后執(zhí)行阻塞任務(wù),并退出原線程。具體流程如圖1所示。
本文采用VC10實(shí)現(xiàn)。泛型部分參考了任務(wù)隊(duì)列的方法,增加了支持不同返回類型的修改。首先定義模版類class Base,結(jié)構(gòu)體struct task_unit由class Base指針對(duì)象構(gòu)造,表示任意函數(shù)任務(wù)。任務(wù)的產(chǎn)生由template
阻塞任務(wù)隊(duì)列部分采用ACE庫(kù)實(shí)現(xiàn),也可以選擇Linux的C++標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)。線程類繼承ACE_Task_Base,信號(hào)量采用ACE_Semaphore,“任務(wù)”信號(hào)初始化為 t_sem(0),“返回”信號(hào)初始化為r_sem(0)。線程通過(guò)調(diào)用t_sem.acquire()等待任務(wù)注入。主線程將任務(wù)注入任務(wù)線程后,調(diào)用t_sem.release()觸發(fā)任務(wù)執(zhí)行,調(diào)用r_sem.acquire(&timeout)等待任務(wù)完成,其中timeout為超時(shí)設(shè)置。任務(wù)結(jié)束時(shí),線程調(diào)用ret_sem.release(),使主線程獲得返回值。
本文方法對(duì)Linux平臺(tái)OCI接口的網(wǎng)絡(luò)阻塞問(wèn)題進(jìn)行處理,針對(duì)已封裝的接口函數(shù),只需利用泛型、多線程、信號(hào)量,即可構(gòu)造一個(gè)通用的阻塞模式任務(wù)隊(duì)列,將接口函數(shù)置于阻塞任務(wù)隊(duì)列下,在網(wǎng)絡(luò)阻塞時(shí),通關(guān)新的線程調(diào)用備用網(wǎng)絡(luò),解決了原本OCI接口函數(shù)長(zhǎng)時(shí)間阻塞的問(wèn)題。經(jīng)過(guò)實(shí)驗(yàn),該方法對(duì)舊有接口的改造工作量小,并且能實(shí)現(xiàn)應(yīng)用的數(shù)據(jù)庫(kù)連接在網(wǎng)絡(luò)發(fā)生通斷問(wèn)題時(shí)進(jìn)行自動(dòng)切換。