天天看點

MongoDB使用教程系列文章--Driver原理(初始化)Driver初始化

driver是mongodb非常重要的組成部分,通過不同的配置實作secondary通路;讀寫分離,動态感覺叢集容災切換等功能。mongodb目前已經覆寫了大部分的開發語言,常見的java到go,可以參考官方連接配接mongodb drivers。這篇文章我們以java版本為例介紹mongodb的drivers實作邏輯和協定,選擇java的原因為語言閱讀門檻低,配合ide,進行debug追蹤也非常容易,其次java版本實作的功能非常完善。主要講解的是replicaset模式,因為shard模式有mongos作為代理,沒有充分展現driver的路由邏輯。

本篇講解了以下三個部分:

driver初始化

driver請求路由

driver容災處理

首先來看下java初始話配置demo:

還有另外一種通過uri初始化的方法:

兩種方式的實作原理麼有本質差別,driver會将uri拆解為第一種的參數設定方式。

uri方式的有點是在複雜的系統中更通用,不同程式設計語言系統可以共享一套初始化配置;如果有多套異構的系統通路mongodb,建議采用uri的方式。

builder方式更靈活,對參數的設定非常細,并且代碼清晰易讀。

仔細的讀者會注意到,address位址變量叫做seed,而不是mongodb叢集上的primary,secondary,或者其他名稱。seed是mongodb通路位址的一種抽象概念,可以是mongodb叢集中的任意一個節點,如果是shardcluster模式,則應該是mongos位址。這樣的好處是使用者不需要關系叢集的狀态改變,更不用随着叢集狀态改變而修改配置。比如,增加了一個新的節點,再或者發生了primary與secondary之間的角色切換,但seed卻保持不變,隻要是叢集中的任意位址即可。然而driver在處理請求時是需要明确primary,secondary等角色的位址關系。

所謂的初始化,就是對叢集狀态的第一次擷取,并且建構driver内部使用的servermonitor線程,叢集中的每個節點,都對應有一個servermonitor。初始狀态下會以seed為起點開始,servermonitor主要有幾下個動作:

首先檢車連接配接狀态,如果是不可用的,需要重建立立連結,并執行action a動作:

<code>ismaster</code> 這裡的作用相當于ping一下,在這個action中沒有特别的作用

<code>buildinfo</code> 擷取server的版本資訊,目的是做版本之間的相容性

<code>auth</code> 如果配置有鑒權屬性,會執行此步驟,目前預設的是sasl,可以參考rfc,scram方式一般需要三次的rpc互動。

<code>getlasterror</code> 确定上面的請求沒有出錯,整個action結束

連接配接ready後,開始心跳,并且間隔性的檢查,預設每10秒鐘,可配置,但最小不能低于500毫秒

<code>ismaster</code> 與action a的指令一樣,但這裡擷取到的資訊非常重要

檢查上一步剛剛獲得的server description與之前的是否一緻,如果沒有發生任何改變,狀态穩定,則該輪心跳檢查結束。不一緻,則産生changeeven事件,調用acton c。

如果有必要,則繼續執行onchanged listener:

更新server hosts清單,如果清單中之間不存在該位址,則新建立連接配接,以新位址構造servermonitor線程

如果目前節點是primary,還要更新electionid等字段

在action c動作結束後,每個server都産生了一個server montior線程,并且driver也同步到了叢集狀态,primary位址,electionid等資訊。一旦叢集資訊發生改變,心跳線程也會随時發現并做出相應的修改。

MongoDB使用教程系列文章--Driver原理(初始化)Driver初始化

servermonitor是獨立的線程,隻保證心跳檢查,不處理真正的使用者請求。使用者的請求是通過其他的連接配接完成,那對連接配接的管理是有個套connectionpool機制,每個連接配接有一套獨立的connectionpool。初始化後connectionpool是空的,等請求需要時會在建立連接配接。

請求線程從pool中拿到connection後,會在請求線程裡處理請求,結束後再放回給connectionpool。不同的實作語言,這裡處理的邏輯并不一樣,不屬于mongodb約定的協定範圍内。

可以參考官方文檔,ismaster,在mongoshell中執行會得到以下結果,非常重要的是ismaster和hosts字段,這兩個字段完整的描述了叢集的狀态資訊。action b.2的判斷,依賴ismaster的是否是true.

從整個流程上可以看到,ismaster指令存在備援,一個連接配接的初始化邏輯,至少需要發送2次,并且對同一個server,不同的連接配接還是要發送,很多情況不是必要的,可以簡化,提高連接配接建立的速度。

通過對mongodb driver for java的分析,我們已經很清楚了初始化的行為動作,包括平時運作時的大概情況,其他語言的實作也大同小異,跑不出這個流程。希望對讀者日後處理用戶端初始化問題時有所幫助。

MongoDB使用教程系列文章--Driver原理(初始化)Driver初始化

了解原理後我們回頭來思考下,怎麼使用才是最佳實踐,主要幾點:

從整個流程上看,mongodb driver初始化的過程很‘重’,要互動很多次,尤其是使用php的同學注意了,不建議采用短連接配接的方式,請将mongodb的連接配接持久化下來。

每個driver都至少會有一個monitor連接配接,而且是不會回收的。是以,規劃連接配接數時,需要關注到這點。

提高請求線程并發量的同時,嘗試同步提高連接配接池上限,對性能會有一定幫助。但請注意,不是越多越好,過多的線程會導緻線程排程消耗過多的資源。而且請配置連接配接的idle time,讓其自動回收長期不用的連接配接,避免連接配接洩漏。

最重要的一點,學會用<code>ismaster</code>指令排查問題,driver請求無法通路時,嘗試在mongo shell中執行<code>ismaster</code>,確定傳回的叢集資訊都是正确的。并且ismaster是不需要鑒權的,是以,保護好你的mongodb執行個體,不要出現的公網上。

配置多個seed位址是,避免其中一個不可用。

......

 待續,driver請求路由