Mongodb特定場景性能數十倍提升優化實踐(記一次mongodb核心集羣雪崩故障)

1. 問題背景

      某核心JAVA長連接服務使用mongodb作爲主要存儲,客戶端數百臺機器連接同一mongodb集羣,短期內出現多次性能抖動問題,此外,還出現一次“雪崩”故障,同時流量瞬間跌零,無法自動恢復。本文分析這兩次故障的根本原因,包括客戶端配置使用不合理、mongodb內核鏈接認證不合理、代理配置不全等一系列問題,最終經過多方努力確定問題根源。

       該集羣有十來個業務接口訪問,每個接口部署在數十臺業務服務器上面,訪問該mongodb機器的客戶端總數超過數百臺,部分請求一次拉取數十行甚至百餘行數據。

       該集羣爲2機房同城多活集羣(選舉節不消耗太多資源,異地的第三機房來部署選舉節點),架構圖如下:

      從上圖可以看出,爲了實現多活,在每個機房都部署有對應代理,對應機房客戶端鏈接對應機房的mongos代理,每個機房多個代理。代理層部署IP:PORT地址列表(注意:不是真實IP地址)如下:

  1. A機房代理地址列表:1.1.1.1:111,2.2.2.2:1111,3.3.3.3:1111
  2. B機房代理地址列表:4.4.4.4:1111,4.4.4.4:2222

     A機房三個代理部署在三臺不同物理機,B機房2個代理部署在同一臺物理機。此外,A機房和B機房爲同城機房,跨機房訪問時延可以忽略。

     集羣存儲層和config server都採用同樣的架構:A機房(1主節點+1從節點) + B機房(2從節點)+C機房(1個選舉節點arbiter),即2(數據節點)+2(數據節點)+1(選舉節點)模式。

     該機房多活架構可以保證任一機房掛了,對另一機房的業務無影響,具體機房多活原理如下:

  1. 如果A機房掛掉,由於代理是無狀態節點,A機房掛掉不會影響B機房的代理。
  2. 如果A機房掛掉,同時主節點在A機房,這時候B機房的2個數據節點和C機房的選舉節點一共三個節點,可以保證新選舉需要大於一半以上節點這個條件,於是B機房的數據節點會在短時間內選舉出一個新的主節點,這樣整個存儲層訪問不受任何影響。

本文重點分析如下6個疑問點:

  1. 爲什麼突發流量業務會抖動?
  2. 爲什麼數據節點沒有任何慢日誌,但是代理負載缺100%?
  3. 爲何mongos代理引起數小時的“雪崩”,並且長時間不可恢復?
  4. 爲何一個機房代理抖動,對應機房業務切到另一個機房後,還是抖動?
  5. 爲何異常時候抓包分析,客戶端頻繁建鏈斷鏈,並且同一個鏈接建鏈到斷鏈間隔很短?
  6. 理論上代理就是七層轉發,消耗資源更少,相比mongod存儲應該更快,爲何mongod存儲節點無任何抖動,mongos代理缺有抖動?

2. 故障過程

2.1 業務偶爾流量高峯,業務抖動?

       該集羣一段時間內有多次短暫的抖動,當A機房客戶端抖動後,發現A機房對應代理負載很高,於是切換A機房訪問B機房代理,但是切換後B機房代理同樣抖動,也就是多活切換沒有作用,具體過程分析如下。

2.1.1 存儲節點慢日誌分析

       首先,分析該集羣所有mongod存儲節點系統CPU、MEM、IO、load等監控信息,發現一切正常,於是分析每個mongod節點慢日誌(由於該集羣對時延敏感,因此慢日誌調整爲30ms),分析結果如下:

       從上圖可以看出,存儲節點在業務抖動的時候沒有任何慢日誌,因此可以判斷存儲節點一切正常,業務抖動和mongod存儲節點無關。

2.1.2 Mongos代理分析

       存儲節點沒有任何問題,因此開始排查mongos代理節點。由於歷史原因,該集羣部署在其他平臺,該平臺對QPS、時延等監控不是很全,造成早期抖動的時候監控沒有及時發現。抖動後,遷移該平臺集羣到oppo自研的新管控平臺,新平臺有詳細的監控信息,遷移後QPS監控曲線如下:

       每個流量徒增時間點,對應業務監控都有一波超時或者抖動,如下:

       分析對應代理mongos日誌,發現如下現象:抖動時間點mongos.log日誌有大量的建鏈接和斷鏈接的過程,如下圖所示:

        從上圖可以看出,一秒鐘內有幾千個鏈接建立,同時有幾千個鏈接斷開,此外抓包發現很多鏈接短期內即斷開鏈接,現象如下(斷鏈時間-建鏈時間=51ms, 部分100多ms斷開):

       對應抓包如下:

         此外,該機器代理上客戶端鏈接低峯期都很高,甚至超過正常的QPS值,QPS大約7000-8000,但是conn鏈接缺高達13000,

mongostat獲取到監控信息如下:

2.1.3 代理機器負載分析

       每次突發流量的時候,代理負載很高,通過部署腳本定期採樣,抖動時間點對應監控圖如下圖所示:

        從上圖可以看出,每次流量高峯的時候CPU負載都非常的高,而且是sy%負載,us%負載很低,同時Load甚至高達好幾百,偶爾甚至過千。

2.1.4 抖動分析總結

        從上面的分析可以看出,某些時間點業務有突發流量引起系統負載很高。根因真的是因爲突發流量嗎?其實不然,請看後續分析,這其實是一個錯誤結論。沒過幾天,同一個集羣雪崩了。

       於是業務梳理突發流量對應接口,梳理出來後下掉了該接口,QPS監控曲線如下:

       爲了減少業務抖動,因此下掉了突發流量接口,此後幾個小時業務不再抖動。當下掉突發流量接口後,我們還做了如下幾件事情:

  1. 由於沒找到mongos負載100%真正原因,於是每個機房擴容mongs代理,保持每個機房4個代理,同時保證所有代理在不同服務器,通過分流來儘量減少代理負載。
  2. 通知A機房和B機房的業務配置上所有的8個代理,不再是每個機房只配置對應機房的代理(因爲第一次業務抖動後,我們分析mongodb的java sdk,確定sdk均衡策略會自動剔除請求時延高的代理,下次如果某個代理再出問題,也會被自動剔除)。
  3. 通知業務把所有客戶端超時時間提高到500ms。

 

       但是,心裏始終有很多疑惑和懸念,主要在以下幾個點:

  1. 存儲節點4個,代理節點5個,存儲節點無任何抖動 ,反而七層轉發的代理負載高?
  2. 爲何抓包發現很多新連接幾十ms或者一百多ms後就斷開連接了?頻繁建鏈斷鏈?
  3. 爲何代理QPS只有幾萬,這時代理CPU消耗就非常高,而且全是sy%系統負載? 以我多年中間件代理研發經驗,代理消耗的資源很少纔對,而且CPU只會消耗us%,而不是sy%消耗。

2.2 同一個業務幾天後雪崩了   

       好景不長,業務下掉突發流量的接口沒過幾天,更嚴重的故障出現了,機房B的業務流量在某一時刻直接跌0了,不是簡單的抖動問題,而是業務直接流量跌0,系統sy%負載100%,業務幾乎100%超時重連。

2.2.1 機器系統監控分析

       機器CPU和系統負載監控如下:

       從上圖可以看出,幾乎和前面的突發流量引起的系統負載過高現象一致,業務CPU sy%負載100%,load很高。登陸機器獲取top信息,現象和監控一致。

       同一時刻對應網絡監控如下:

       磁盤IO監控如下:

       從上面的系統監控分析可以看出,出問題的時間段,系統CPU sy%、load負載都很高,網絡讀寫流量幾乎跌0,磁盤IO一切正常,可以看出整個過程幾乎和之前突發流量引起的抖動問題完全一致。

2.2.2 業務如何恢復

        第一次突發流量引起的抖動問題後,我們擴容所有的代理到8個,同時通知業務把所有業務接口配置上所有代理。由於業務接口衆多,最終B機房的業務沒有配置全部代理,只配置了原先的兩個處於同一臺物理機的代理(4.4.4.4:1111,4.4.4.4:2222),最終觸發mongodb的一個性能瓶頸(詳見後面分析),引起了整個mongodb集羣雪崩

       最終,業務通過重啓服務,同時把B機房的8個代理同時配置上,問題得以解決。

2.2.3 mongos代理實例監控分析

       分析該時間段代理日誌,可以看出和2.1同樣得現象,大量的新鍵連接,同時新連接在幾十ms、一百多ms後又關閉連接。整個現象和之前分析一致,這裏不在統計分析對應日誌。

       此外,分析當時的代理QPS監控,正常query讀請求的QPS訪問曲線如下,故障時間段QPS幾乎跌零雪崩了:

       從上面的統計可以看出,當該代理節點的流量故障時間點有一波尖刺,同時該時間點的command統計瞬間飆漲到22000(實際可能更高,因爲我們監控採樣週期30s,這裏只是平均值),也就是瞬間有2.2萬個連接瞬間進來了。Command統計實際上是db.ismaster()統計,客戶端connect服務端成功後的第一個報文就是ismaster報文,服務端執行db.ismaster()後應答客戶端,客戶端收到後開始正式的sasl認證流程。

       正常客戶端訪問流程如下:

  1. 客戶端發起與mongos的鏈接
  2. Mongos服務端accept接收鏈接後,鏈接建立成功
  3. 客戶端發送db.isMaster()命令給服務端
  4. 服務端應答isMaster給客戶端
  5. 客戶端發起與mongos代理的sasl認證(多次和mongos交互)
  6. 客戶端發起正常的find()流程

      客戶端SDK鏈接建立成功後發送db.isMaster()給服務端的目的是爲了負載均衡策略和判斷節點是什麼類型,保證客戶端快速感知到訪問時延高的代理,從而快速剔除往返時延高的節點,同時確定訪問的節點類型。

       此外,通過提前部署的腳本,該腳本在系統負載高的時候自動抓包,從抓包分析結果如下圖所示:

      上圖時序分析如下:

  1.  11:21:59.506174鏈接建立成功
  2.  11:21:59.506254 客戶端發送db.IsMaster()到服務端
  3.  11:21:59.656479 客戶端發送FIN斷鏈請求
  4.  11:21:59.674717 服務端發送db.IsMaster()應答給客戶端
  5.  11:21:59.675480 客戶端直接RST

       第3和第1個報文之間相差大約150ms,最後和業務確定該客戶端IP對應的超時時間配置,確定就是150ms。此外,其他抓包中有類似40ms、100ms等超時配置,通過對應客戶端和業務確認,確定對應客戶端業務接口超時時間配置的就是40ms、100ms等。因此,結合抓包和客戶端配置,可以確定當代理超過指定超時時間還沒有給客戶端db.isMaster()返回值,則客戶端立馬超時,超時後立馬發起重連請求。

總結:通過抓包和mongos日誌分析,可以確定鏈接建立後快速斷開的原因是:客戶端訪問代理的第一個請求db.isMaster()超時了,因此引起客戶端重連。重連後又開始獲取db.isMaster()請求,由於負載CPU 100%, 很高,每次重連後的請求都會超時。其中配置超時時間爲500ms的客戶端,由於db.isMaster()不會超時,因此後續會走sasl認證流程。

       因此可以看出,系統負載高和反覆的建鏈斷鏈有關,某一時刻客戶端大量建立鏈接(2.2W)引起負載高,又因爲客戶端超時時間配置不一,超時時間配置得比較大得客戶端最終會進入sasl流程,從內核態獲取隨機數,引起sy%負載高,sy%負載高又引起客戶端超時,這樣整個訪問過程就成爲一個“死循環”,最終引起mongos代理雪崩。

2.3 線下模擬故障

       到這裏,我們已經大概確定了問題原因,但是爲什麼故障突發時間點那一瞬間2萬個請求就會引起sy%負載100%呢,理論上一秒鐘幾萬個鏈接不會引起如此嚴重的問題,畢竟我們機器有40個CPU。因此,分析反覆建鏈斷鏈爲何引起系統sy%負載100%就成爲了本故障的關鍵點。

2.3.1 模擬故障過程

       模擬頻繁建鏈斷鏈故障步驟如下:

  1. 修改mongos內核代碼,所有請求全部延時600ms
  2. 同一臺機器起兩個同樣的mongos,通過端口區分
  3. 客戶端啓用6000個併發鏈接,超時時間500ms

       通過上面的操作,可以保證所有請求超時,超時後客戶端又會立馬開始重新建鏈,再次建鏈後訪問mongodb還會超時,這樣就模擬了反覆建鏈斷鏈的過程。此外,爲了保證和雪崩故障環境一致,把2個mongos代理部署在同一臺物理機。

2.3.2 故障模擬測試結果

       爲了保證和故障的mongos代理硬件環境一致,因此選擇故障同樣類型的服務器,並且操作系統版本一樣(2.6.32-642.el6.x86_64),程序都跑起來後,問題立馬浮現:

        由於出故障的服務器操作系統版本linux-2.6過低,因此懷疑可能和操作系統版本有問題,因此升級同一類型的一臺物理機到linux-3.10版本,測試結果如下:

       從上圖可以看出,客戶端6000併發反覆重連,服務端壓力正常,所有CPU消耗在us%,sy%消耗很低。用戶態CPU消耗3個CPU,內核態CPU消耗幾乎爲0,這是我們期待的正常結果,因此覺得該問題可能和操作系統版本有問題。

      爲了驗證更高並反覆建鏈斷鏈在Linux-3.10內核版本是否有2.6版本同樣的sy%內核態CPU消耗高的問題,因此把併發從6000提升到30000,驗證結果如下:

測試結果:通過修改mongodb內核版本故意讓客戶端超時反覆建鏈斷鏈,在linux-2.6版本中,1500以上的併發反覆建鏈斷鏈系統CPU sy% 100%的問題即可浮現。但是,在Linux-3.10版本中,併發到10000後,sy%負載逐步增加,併發越高sy%負載越高。

總結:linux-2.6系統中,mongodb只要每秒有幾千的反覆建鏈斷鏈,系統sy%負載就會接近100%。Linux-3.10,併發20000反覆建鏈斷鏈的時候,sy%負載可以達到30%,隨着客戶端併發增加,sy%負載也相應的增加。Linux-3.10版本相比2.6版本針對反覆建鏈斷鏈的場景有很大的性能改善,但是不能解決根本問題。

2.4 客戶端反覆建鏈斷鏈引起sy% 100%根因

       爲了分析%sy系統負載高的原因,安裝perf獲取系統top信息,發現所有CPU消耗在如下接口:

       從perf分析可以看出,cpu 消耗在_spin_lock_irqsave函數,繼續分析內核態調用棧,得到如下堆棧信息:

    - 89.81% 89.81% [kernel] [k] _spin_lock_irqsave ▒
    - _spin_lock_irqsave ▒
    - mix_pool_bytes_extract ▒
    - extract_buf ▒
    extract_entropy_user ▒
    urandom_read ▒
    vfs_read ▒
    sys_read ▒
    system_call_fastpath ▒
    0xe82d

      上面的堆棧信息說明,mongodb在讀取 /dev/urandom ,並且由於多個線程同時讀取該文件,導致消耗在一把spinlock上。

      到這裏問題進一步明朗了,故障root case 不是每秒幾萬的連接數導致sys 過高引起。根本原因是每個mongo客戶端的新鏈接會導致mongodb後端新建一個線程,該線程在某種情況下會調用urandom_read 去讀取隨機數/dev/urandom ,並且由於多個線程同時讀取,導致內核態消耗在一把spinlock鎖上,出現cpu 高的現象。

2.5 mongodb內核隨機數優化

2.5.1 mongodb內核源碼定位分析

      上面的分析已經確定,問題根源是mongodb內核多個線程讀取/dev/urandom隨機數引起,走讀mongodb內核代碼,發現讀取該文件的地方如下:

      上面是生成隨機數的核心代碼,每次獲取隨機數都會讀取/dev/urandom系統文件,所以只要找到使用該接口的地方即可即可分析出問題。

繼續走讀代碼,發現主要在如下地方:

//服務端收到客戶端sasl認證的第一個報文後的處理,這裏會生成隨機數

//如果是mongos,這裏就是接收客戶端sasl認證的第一個報文的處理流程

Sasl_scramsha1_server_conversation::_firstStep(...) {

    ... ...

    unique_ptr<SecureRandom> sr(SecureRandom::create());

    binaryNonce[0] = sr->nextInt64();

    binaryNonce[1] = sr->nextInt64();

    binaryNonce[2] = sr->nextInt64();

    ... ...

}

//mongos相比mongod存儲節點就是客戶端,mongos作爲客戶端也需要生成隨機數

SaslSCRAMSHA1ClientConversation::_firstStep(...) {

    ... ...

    unique_ptr<SecureRandom> sr(SecureRandom::create());

    binaryNonce[0] = sr->nextInt64();

    binaryNonce[1] = sr->nextInt64();

    binaryNonce[2] = sr->nextInt64();

    ... ...

}

2.5.2 mongodb內核源碼隨機數優化

        從2.5.1分析可以看出,mongos處理客戶端新連接sasl認證過程都會通過"/dev/urandom"生成隨機數,從而引起系統sy% CPU過高,我們如何優化隨機數算法就是解決本問題的關鍵。

       繼續分析mongodb內核源碼,發現使用隨機數的地方很多,其中有部分隨機數通過用戶態算法生成,因此我們可以採用同樣方法,在用戶態生成隨機數,用戶態隨機數生成核心算法如下:

    class PseudoRandom {

    ... ...

    uint32_t _x;

    uint32_t _y;

    uint32_t _z;

    uint32_t _w;

}

       該算法可以保證產生的數據隨機分佈,該算法原理詳見:

     http://en.wikipedia.org/wiki/Xorshift

     也可以查看如下git地址獲取算法實現:

     mongodb隨機數生成算法註釋

     總結:通過優化sasl認證的隨機數生成算法爲用戶態算法後,CPU sy% 100%的問題得以解決,同時代理性能在短鏈接場景下有了數倍/數十倍的性能提升

  1. 問題總結及疑問解答

         從上面的分析可以看出,該故障由多種因素連環觸發引起,包括客戶端配置使用不當、mongodb服務端內核極端情況異常缺陷、監控不全等。總結如下:

  1. 客戶端配置不統一,同一個集羣多個業務接口配置千奇百怪,超時配置、鏈接配置各不相同,增加了抓包排查故障的難度,超時時間設置太小容易引起反覆重連。
  2. 客戶端需要配全所有mongos代理,這樣當一個代理故障的時候,客戶端SDK默認會剔除該故障代理節點,從而可以保證業務影響最小,就不會存在單點問題。
  3. 同一集羣多個業務接口應該使用同一配置中心統一配置,避免配置不統一。
  4. Mongodb內核的新連接隨機算法存在嚴重缺陷,在極端情況下引起嚴重性能抖動,甚至業務“雪崩”。

       分析到這裏,我們可以回答第1章節的6個疑問點了,如下:

爲什麼突發流量業務會抖動?

       答:由於業務是java業務,採用鏈接池方式鏈接mongos代理,當有突發流量的時候,鏈接池會增加鏈接數來提升訪問mongodb的性能,這時候客戶端就會新增鏈接,由於客戶端衆多,造成可能瞬間會有大量新連接和mongos建鏈。鏈接建立成功後開始做sasl認證,由於認證的第一步需要生成隨機數,就需要訪問操作系統"/dev/urandom"文件。又因爲mongos代理模型是默認一個鏈接一個線程,所以會造成瞬間多個線程訪問該文件,進而引起內核態sy%負載過高。

爲何mongos代理引起“雪崩”,流量爲何跌零不可用?

       答:原因客戶端某一時刻可能因爲流量突然有增加,鏈接池中鏈接數不夠用,於是增加和mongos代理的鏈接,由於是老集羣,代理還是默認的一個鏈接一個線程模型,這樣瞬間就會有大量鏈接,每個鏈接建立成功後,就開始sasl認證,認證的第一步服務端需要產生隨機數,mongos服務端通過讀取"/dev/urandom"獲取隨機數,由於多個線程同時讀取該文件觸發內核態spinlock鎖CPU sy% 100%問題。由於sy%系統負載過高,由於客戶端超時時間設置過小,進一步引起客戶端訪問超時,超時後重連,重連後又進入sasl認證,又加劇了讀取"/dev/urandom"文件,如此反覆循環持續。

此外,第一次業務抖動後,服務端擴容了8個mongos代理,但是客戶端沒有修改,造成B機房業務配置的2個代理在同一臺服務器,無法利用mongo java sdk的自動剔除負載高節點這一策略,所以最終造成雪崩

爲什麼數據節點沒有任何慢日誌,但是代理負載卻CPU sy% 100%?

       答:由於客戶端java程序直接訪問的是mongos代理,所以大量鏈接只發生在客戶端和mongos之間,同時由於客戶端超時時間設置太短(有接口設置位幾十ms,有的接口設置位一百多ms,有的接口設置位500ms),就造成在流量峯值的時候引起連鎖反應(突發流量系統負載高引起客戶端快速超時,超時後快速重連,進一步引起超時,無限死循環)。Mongos和mongod之間也是鏈接池模型,但是mongos作爲客戶端訪問mongod存儲節點的超時很長,默認都是秒級別,所以不會引起反覆超時建鏈斷鏈。

爲何A機房代理抖動的時候,A機房業務切到B機房後,還是抖動?

       答:當A機房業務抖動,業務切換到B機房的時候,客戶端需要重新和服務端建立鏈接認證,又會觸發大量反覆建鏈斷鏈和讀取隨機數"/dev/urandom"的流程,所以最終造成機房多活失敗。

爲何異常時候抓包分析,客戶端頻繁建鏈斷鏈,並且同一個鏈接建鏈到斷鏈間隔很短?

       答:頻繁建鏈斷鏈的根本原因是系統sy%負載高,客戶端極短時間內建立鏈接後又端口的原因是客戶端配置超時時間太短。

理論上代理就是七層轉發,消耗資源更少,相比mongod存儲應該更快,爲何mongod存儲節點無任何抖動,mongos代理卻有嚴重抖動?

       答:由於採用分片架構,所有mongod存儲節點前面都有一層mongos代理,mongos代理作爲mongod存儲節點的客戶端,超時時間默認秒級,不會出現超時現象,也就不會出現頻繁的建鏈斷鏈過程。

如果mongodb集羣採用普通複製集模式,客戶端頻繁建鏈斷鏈是否可能引起mongod存儲節點同樣的雪崩

        答:會。如果客戶端過多,操作系統內核版本過低,同時超時時間配置過段,直接訪問複製集的mongod存儲節點,由於客戶端和存儲節點的認證過程和與mongos代理的認證過程一樣,所以還是會觸發引起頻繁讀取"/dev/urandom"文件,引起CPU sy%負載過高,極端情況下引起雪崩。

  1. 雪崩解決辦法

       從上面的一系列分析,問題在於客戶端配置不合理,加上mongodb內核認證過程讀取隨機數在極端情況下存在缺陷,最終造成雪崩。如果沒有mongodb內核研發能力,可以通過規範化客戶端配置來避免該問題。當然,如果客戶端配置規範化,同時mongodb內核層面解決極端情況下的隨機數讀取問題,這樣問題可以得到徹底解決。

4.1 JAVA SDK客戶端配置規範化

       在業務接口很多,客戶端機器很多的業務場景,客戶端配置一定要做到如下幾點:

  1. 超時時間設置爲秒級,避免超時時間設置過端引起反覆的建鏈斷鏈。
  2. 客戶端需要配置所有mongos代理地址,不能配置單點,否則流量到一個mongos很容易引起瞬間流量峯值的建鏈認證。
  3. 增加mongos代理數量,這樣可以分流,保證同一時刻每個代理的新鍵鏈接儘可能的少,客戶端在多代理配置時,默認是均衡流量分發的,如果某個代理負載高,客戶端會自動剔除。

        如果沒有mongodb內核源碼研發能力,可以參考該客戶端配置方法,同時淘汰linux-2.6版本內核,採用linux-3.10或者更高版本內核,基本上可以規避踩同樣類型的坑。

4.2 mongodb內核源碼優化(擯棄內核態獲取隨機數,選擇用戶態隨機數算法)

     詳見2.5.2 章節。

 

4.3 PHP短鏈接業務,如何規避踩坑

       由於PHP業務屬於短鏈接業務,如果流量很高,不可避免的要頻繁建鏈斷鏈,也就會走sasl認證流程,最終多線程頻繁讀取"/dev/urandom"文件,很容易引起前面的問題。這種情況,可以採用4.1 java客戶端類似的規範,同時不要使用低版本的Linux內核,採用3.x以上內核版本,就可以規避該問題的存在。

5. Mongodb內核源碼設計與實現分析

       本文相關的Mongodb線程模型及隨機數算法實現相關源碼分析如下:

mongodb動態線程模型源碼設計與實現分析

mongodb一個鏈接一個線程模型源碼設計與實現分析

mongodb內核態及用戶態隨機數算法實現分析

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