mongodb C driver的異步查詢

  最近在選用NOSQL數據庫的時候最終選擇了mongodb, 感覺其各方面都很優秀, 於是爲服務器增加了一組mongodb的接口, 以方便LUA邏輯層使用.

 

  驅動方面選用了官方指定的C DRIVER, 大家有興趣的可以直接去mongodb的官網上查找, 不過查看了其mongo_find接口發現爲同步調用, 這在服務器併發應用方面會受到限制, 通常服務器爲了提高併發處理效率會使用異步接口. 開始的時候比較偷懶, 在其官網上留言想讓其driver開發人員幫忙添加異步調用接口, 這事情也就先放下了... 2天過後, 等我想要接着製作的時候, 再次查看了官網動態... 發現死氣沉沉的毫無反應... 我個人還算是比較勤快, 好在driver代碼實在好讀, 這點讓我很佩服, 仔細的分析了下原尾, 決定自己爲其添加一組異步調用接口 :)

 

  ok, let's do it :) 

 

  由於鄙人資歷經驗尚淺... 如有不妥之處, 歡迎各位及時指正.

 

 思路:

  我們知道LUA中提供coroutine, 這極大的方便了我們開發異步調用, 首先, 我們需要明確異步查詢流程: LUA腳本層發起查詢請求, 調用引擎層接口發送查詢請求, 返回LUA腳本層並掛起當前coroutine, --- (引擎處理其他事件) ---, 引擎收到mongodb返回查詢結果, 組織封裝結果並通知LUA腳本層喚醒查詢請求coroutine, coroutine繼續執行得到返回結果並處理.

 

  由於有coroutine的幫助, 使得我們在LUA層面上的調用和正常的順序編程沒有區別, 而異步的回調處理過程完全被屏蔽在邏輯層之下, 大大的簡化了我們的邏輯層編程複雜度, 而且提高了引擎併發處理能力.

 

 實現:

  好了, 接下來我們來看看如何實現這樣的功能. 根據上面的思路, 我們需要首先解決一個問題, 如何正確的恢復一個被掛起的coroutine, 這裏如果我們使用多個線程等待同一條連接上的請求響應時, 如果在未加鎖保護的情況下會出現這樣一種情況: 比如2個線程通過同一條連接請求A, B發送至數據庫, 但接受情況爲B, A. 因爲加鎖實屬下策, 所以, 我們應當讓數據庫幫助我們記錄一些信息以便在返回的結果中我們可以正確的喚醒相應的coroutine. 

  

  mongodb的消息頭中包含了這樣的字段, 我們可以暫且爲其命名爲query_id, 當我們在發送時填充該字段後, 在接受到的返回消息頭中會原封不動的得到該query_id, 這樣我們就可以簡單的使用自增id作爲query_id進行填充即可.

  

  下面我們要用將原driver中的find接口進行拆分, 原有接口的邏輯爲: 創建消息頭, 填充query, 發送請求, 等待接受結果, 接收到結果並返回. 我們將其拆分爲3個接口:

  1.創建併發送請求

  2.接受返回結果消息頭

  3.接受返回結果數據並做相應的回調處理.

 

  這樣, 我們可以非常方便的將其放入如libevent的事件驅動內進行處理.

  因爲第二個和第三個接口都在接收返回結果時調用, 此過程爲連續過程, 所以不用擔心這裏面會有亂序的情況.

 

  下面放上代碼:(此代碼爲在原作者接口基礎上進行拆分和稍作修改)

   

 

  這裏面需要注意的地方就是在處理回調的過程中需要妥善保存好發起請求時傳入的參數"ns", 此爲database.collection, 簡單的做法就是使用發送請求時生成的query_id作爲key, 將ns copy出來存入hash結構中, 在響應回調時取出並正確的處理結果 :)

 總結:

  經過簡單測試, 還是比較滿意這次的改動, 不過細節方面還有待改善, 不過這些代碼僅作爲初步使用, 昨天下午的時候我也將這些改動的patch發給了原driver製作組, 他們的反饋意見是會仔細考慮這個意見, 不過他們還要處理優先級更高的任務 :D , 所以, 如果想使用最終官方的接口, 還是要等driver製作組的更新了. 本人的改動僅作爲參考和測試.

 

  大家有什麼問題相互探討可以隨時留言或email給我([email protected])

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章