mongodb核心原始碼實現、效能調優、最佳運維實踐系列-網路傳輸層模組原始碼實現二

y123456yzzyz發表於2020-10-26

mongodb 網路傳輸層模組原始碼實現二

關於作者

  前滴滴出行技術專家,現任OPPO 文件資料庫 mongodb 負責人,負責 oppo 千萬級峰值 TPS/ 十萬億級資料量文件資料庫 mongodb 核心研發及運維工作,一直專注於分散式快取、高效能服務端、資料庫、中介軟體等相關研發。後續持續分享《 MongoDB 核心原始碼設計、效能優化、最佳運維實踐》, Github 賬號地址 : https://github.com/y123456yz

1. 說明

在之前的 <<Mongodb 網路傳輸處理原始碼實現及效能調優 - 體驗核心效能極致設計 >> 一文中分析瞭如何閱讀百萬級大工程原始碼、Asio 網路庫實現、 transport 傳輸層網路模組中執行緒模型實現,但是由於篇幅原因,傳輸層網路模組中的以下模組實現原理沒有分析,本文降將繼續分析遺留的以下子模組:

transport_layer 套接字處理及傳輸層管理子模組

session 會話子模組

Ticket 資料收發子模組

service_entry_point 服務入口點子模組

service_state_machine 狀態機子模組 ( 該《模組在網路傳輸層模組原始碼實現三》中分析 )

service_executor 執行緒模型子模組 ( 該《模組在網路傳輸層模組原始碼實現四》中分析 )

 

2.  transport_layer 套接字處理及傳輸層管理子模組

transport_layer 套接字處理及傳輸層管理子模組功能包括套接字相關初始化處理、結合 asio 庫實現非同步 accept 處理、不同執行緒模型管理及初始化等,該模組的原始碼實現主要由以下幾個檔案實現:

上圖是套接字處理及傳輸層管理子模組原始碼實現的相關檔案,其中mock test 檔案主要用於模擬測試等,所以真正核心的程式碼實現只有下表的幾個檔案,對應原始碼檔案功能說明如下表所示:

檔名

功能

transport_layer.h

transport_layer.cpp

該子模組基類,通過該類把本模組和 Ticket 資料分發模組銜接起來,具體類實現在 transport_layer_legacy.cpp transport_layer_asio.cpp

transport_layer_legacy.h

transport_layer_legacy.cpp

早期的傳輸模組實現方式,現在已淘汰,不在分析

transport_layer_asio.h

transport_layer_asio.cpp

傳輸模組 Asio 網路 IO 處理實現,同時銜接 ServiceEntryPoint 服務入口模組和 Ticket 資料分發模組

2.1 核心程式碼實現

該子模組核心程式碼主要由 TransportLayerManager 類和TransportLayerASIO 類相關介面實現。

2.1.1   TransportLayerManager 類核心程式碼實現

TransportLayerManager 類主要成員及介面如下:

1. //網路會話連結,訊息處理管理相關的類,在createWithConfig構造該類存入_tls  
2. class TransportLayerManager final : public TransportLayer {  
3.     //以下四個介面真正實現在TransportLayerASIO類中具體實現  
4.     Ticket sourceMessage(...) override;  
5.     Ticket sinkMessage(...) override;      
6.     Status wait(Ticket&& ticket) override;  
7.     void asyncWait(...) override;  
8.     //配置初始化實現  
9.     std::unique_ptr<TransportLayer> createWithConfig(...);  
10.   
11.     //createWithConfig中賦值,對應TransportLayerASIO,  
12.     //實際上容器中就一個成員,就是TransportLayerASIO  
13.     std::vector<std::unique_ptr<TransportLayer>> _tls;  
14. };

  TransportLayerManager 類包含一個 _tls 成員,該類最核心的 createWithConfig 介面程式碼實現如下:

1. //根據配置構造相應類資訊  _initAndListen中呼叫  
2. std::unique_ptr<TransportLayer> TransportLayerManager::createWithConfig(...) {  
3.     std::unique_ptr<TransportLayer> transportLayer;  
4.     //服務型別,也就是本例項是mongos還是mongod  
5.     //mongos對應ServiceEntryPointMongod,mongod對應ServiceEntryPointMongos  
6.     auto sep = ctx->getServiceEntryPoint();  
7.     //net.transportLayer配置模式,預設asio, legacy模式已淘汰  
8.     if (config->transportLayer == "asio") {  
9.          //同步方式還是非同步方式,預設synchronous  
10.         if (config->serviceExecutor == "adaptive") {  
11.             //動態執行緒池模型,也就是非同步模式  
12.             opts.transportMode = transport::Mode::kAsynchronous;  
13.         } else if (config->serviceExecutor == "synchronous") {  
14.             //一個連結一個執行緒模型,也就是同步模式  
15.             opts.transportMode = transport::Mode::kSynchronous;  
16.         }   
17.         //如果配置是asio,構造TransportLayerASIO類  
18.         auto transportLayerASIO = stdx::make_unique<transport::TransportLayerASIO>(opts, sep);  
19.         if (config->serviceExecutor == "adaptive") { //非同步方式  
20.              //構造動態執行緒模型對應的執行器ServiceExecutorAdaptive  
21.             ctx->setServiceExecutor(stdx::make_unique<ServiceExecutorAdaptive>(  
22.                 ctx, transportLayerASIO->getIOContext()));  
23.          } else if (config->serviceExecutor == "synchronous") { //同步方式  
24.             //構造一個連結一個執行緒模型對應的執行器ServiceExecutorSynchronous  
25.             ctx->setServiceExecutor(stdx::make_unique<ServiceExecutorSynchronous>(ctx));  
26.          }  
27.          //transportLayerASIO轉換為transportLayer類  
28.          transportLayer = std::move(transportLayerASIO);  
29.     }   
30.    //transportLayer轉存到對應retVector陣列中並返回  
31.     std::vector<std::unique_ptr<TransportLayer>> retVector;  
32.     retVector.emplace_back(std::move(transportLayer));  
33.     return stdx::make_unique<TransportLayerManager>(std::move(retVector));  
34. }

     createWithConfig 函式根據配置檔案來確定對應的TransportLayer ,如果 net.transportLayer 配置為 asio ,則選用TransportLayerASIO 類來進行底層的網路 IO 處理,如果配置為 legacy ,則選用TransportLayerLegacy legacy 模式當前已淘汰,本文只分析 asio 模式實現。

asio 模式包含兩種執行緒模型: adaptive ( 動態執行緒模型 ) synchronous ( 同步執行緒模型 ) adaptive 模式執行緒設計採用動態執行緒方式,執行緒數和 mongodb 壓力直接相關,如果 mongodb 壓力大,則執行緒數增加;如果 mongodb 壓力變小,則執行緒數自動減少。同步執行緒模式也就是一個連結一個執行緒模型,執行緒數的多少和連結數的多少成正比,連結數越多則執行緒數也越大。

Mongodb 核心實現中通過 opts.transportMode 來標記asio 的執行緒模型,這兩種模型對應標記如下:

執行緒模型

核心 transportMode 標記

說明

對應執行緒模型由那個類實現

adaptive

KAsynchronous( 非同步 )

adaptive 模型也可以稱為非同步模型

ServiceExecutorAdaptive

synchronous

KSynchronous( 同步 )

synchronous 模型也可以稱為同步模型

ServiceExecutorSynchronous

     說明:adaptive 執行緒模型被標記為 KAsynchronous synchronous 被標記為 KSynchronous 是有原因的, adaptive 動態執行緒模型網路 IO 處理藉助 epoll 非同步實現,而 synchronous 一個連結一個執行緒模型網路IO 處理是同步讀寫操作。 Mongodb 網路執行緒模型具體實現及各種優缺點可以參考: Mongodb 網路傳輸處理原始碼實現及效能調優 - 體驗核心效能極致設計

2.1.2 TransportLayerASIO 類核心程式碼實現

TransportLayerASIO 類核心成員及介面如下:

1. class TransportLayerASIO final : public TransportLayer {  
2.     //以下四個介面主要和套接字資料讀寫相關  
3.     Ticket sourceMessage(...);  
4.     Ticket sinkMessage(...);  
5.     Status wait(Ticket&& ticket);  
6.     void asyncWait(Ticket&& ticket, TicketCallback callback);  
7.     void end(const SessionHandle& session);  
8.     //新連結處理  
9.     void _acceptConnection(GenericAcceptor& acceptor);  
10.       
11.     //adaptive執行緒模型網路IO上下文處理  
12.     std::shared_ptr<asio::io_context> _workerIOContext;   
13.     //accept接收客戶端連結對應的IO上下文  
14.     std::unique_ptr<asio::io_context> _acceptorIOContext;    
15.     //bindIp配置中的ip地址列表,用於bind監聽,accept客戶端請求  
16.     std::vector<std::pair<SockAddr, GenericAcceptor>> _acceptors;  
17.     //listener執行緒負責接收客戶端新連結  
18.     stdx::thread _listenerThread;  
19.     //服務型別,也就是本例項是mongos還是mongod  
20.     //mongos對應ServiceEntryPointMongod,mongod對應ServiceEntryPointMongos  
21.     ServiceEntryPoint* const _sep = nullptr;  
22.     //當前執行狀態  
23.     AtomicWord<bool> _running{false};  
24.     //listener處理相關的配置資訊  
25.     Options _listenerOptions;  
26. }  
     從上面的類結構可以看出,該類主要通過listenerThread執行緒完成bind繫結及listen監聽操作,同時部分介面實現新連線上的資料讀寫。
套接字初始化程式碼實現如下:
1. Status TransportLayerASIO::setup() {  
2.     std::vector<std::string> listenAddrs;  
3.     //如果沒有配置bindIp,則預設監聽"127.0.0.1:27017"
4.     if (_listenerOptions.ipList.empty()) {  
5.         listenAddrs = {"127.0.0.1"};  
6.     } else {  
7.         //配置檔案中的bindIp:1.1.1.1,2.2.2.2,以逗號分隔符獲取ip列表存入ipList  
8.         boost::split(listenAddrs, _listenerOptions.ipList, boost::is_any_of(","), boost::token_compress_on);  
9.     }  
10.     //遍歷ip地址列表  
11.     for (auto& ip : listenAddrs) {  
12.         //根據IP和埠構造對應SockAddr結構  
13.         const auto addrs = SockAddr::createAll(  
14.             ip, _listenerOptions.port, _listenerOptions.enableIPv6 ? AF_UNSPEC : AF_INET);  
15.         ......  
16.         //根據addr構造endpoint  
17.         asio::generic::stream_protocol::endpoint endpoint(addr.raw(), addr.addressSize);  
18.         //_acceptorIOContext和_acceptors關聯  
19.         GenericAcceptor acceptor(*_acceptorIOContext);  
20.         //epoll註冊,也就是fd和epoll關聯  
21.         //basic_socket_acceptor::open  
22.         acceptor.open(endpoint.protocol());   
23.          //SO_REUSEADDR配置 basic_socket_acceptor::set_option  
24.         acceptor.set_option(GenericAcceptor::reuse_address(true));  
25.         //非阻塞設定 basic_socket_acceptor::non_blocking  
26.         acceptor.non_blocking(true, ec);    
27.         //bind繫結    
28.         acceptor.bind(endpoint, ec);   
29.         if (ec) {  
30.             return errorCodeToStatus(ec);  
31.         }  
32.     }  
33. }

    從上面的分析可以看出,程式碼實現首先解析出配置檔案中bindIP 中的 ip:port 列表,然後遍歷列表繫結所有服務端需要監聽的 ip:port ,每個 ip:port 對應一個 GenericAcceptor  ,所有 acceptor 和全域性accept IO 上下文 _acceptorIOContext 關聯,同時 bind() 繫結所有 ip:port

     Bind() 繫結所有配置檔案中的 Ip:port 後,然後通過 TransportLayerASIO::start() 完成後續處理,該介面程式碼實現如下:

1. //_initAndListen中呼叫執行   
2. Status TransportLayerASIO::start() { //listen執行緒處理  
3.     ......  
4.     //這裡專門起一個執行緒做listen相關的accept事件處理  
5.     _listenerThread = stdx::thread([this] {  
6.         //修改執行緒名  
7.         setThreadName("listener");   
8.         //該函式中迴圈處理accept事件  
9.         while (_running.load()) {  
10.             asio::io_context::work work(*_acceptorIOContext);   
11.             try {  
12.                 //accept事件排程處理  
13.                  _acceptorIOContext->run();    
14.             } catch (...) { //異常處理  
15.                 severe() << "Uncaught exception in the listener: " << exceptionToStatus();  
16.                 fassertFailed(40491);  
17.             }  
18.         }  
19.     });   
20.    遍歷_acceptors,進行listen監聽處理  
21.    for (auto& acceptor : _acceptors) {   
22.         acceptor.second.listen(serverGlobalParams.listenBacklog);  
23.         //非同步accept回撥註冊在該函式中  
24.         _acceptConnection(acceptor.second);       
25.     }  
26. }

從上面的 TransportLayerASIO::start() 介面可以看出,mongodb 特地建立了一個 listener 執行緒用於客戶端accept 事件處理,然後藉助 ASIO 網路庫的 _acceptorIOContext->run() 介面來排程,當有新連結到來的時候,就會執行相應的accept 回撥處理, accept 回撥註冊到 io_context 的流程由 acceptConnection () 完成,該介面核心原始碼實現如下:

1. //accept新連線到來的回撥註冊 
2. void TransportLayerASIO::_acceptConnection(GenericAcceptor& acceptor) {  
3.       //新連結到來時候的回撥函式,服務端接收到新連線都會執行該回撥
4.     //注意這裡面是遞迴執行,保證所有accept事件都會一次處理完畢
5.     auto acceptCb = [this, &acceptor](const std::error_code& ec, GenericSocket peerSocket) mutable {  
6.         if (!_running.load())  
7.             return;  
8.   
9.         ......  
10.         //每個新的連結都會new一個新的ASIOSession  
11.         std::shared_ptr<ASIOSession> session(new ASIOSession(this, std::move(peerSocket)));  
12.         //新的連結處理ServiceEntryPointImpl::startSession,  
13.         //和ServiceEntryPointImpl服務入口點模組關聯起來  
14.         _sep->startSession(std::move(session));  
15.         //遞迴,直到處理完所有的網路accept事件  
16.         _acceptConnection(acceptor);   
17.     };  
18.     //accept新連線到來後服務端的回撥處理在這裡註冊  
19.     acceptor.async_accept(*_workerIOContext, std::move(acceptCb));  
}

TransportLayerASIO::_acceptConnection 的新連線處理過程藉助ASIO 庫實現,通過 acceptor.async_accept 實現所有監聽的 acceptor 回撥非同步註冊。

當服務端接收到客戶端新連線事件通知後,會觸發執行 acceptCb () 回撥,該回撥中底層 ASIO 庫通過 epoll_wait 獲取到所有的 accept 事件,每獲取到一個 accept 事件就代表一個新的客戶端連結,然後呼叫 ServiceEntryPointImpl::startSession () 介面處理這個新的連結事件,整個過程遞迴執行,保證一次可以處理所有的客戶端 accept 請求資訊。

每個連結都會構造一個唯一的session 資訊,該 session 就代表一個唯一的新連線,連結和 session 一一對應。此外,最終會呼叫 ServiceEntryPointImpl::startSession () 進行真正的 accept() 處理,從而獲取到一個新的連結。

注意 TransportLayerASIO::_acceptConnection () 中實現了 TransportLayerASIO 類和 ServiceEntryPointImpl 類的關聯,這兩個類在該介面實現了關聯。

此外,從前面的 TransportLayerASIO 類結構中可以看出,該類還包含如下四個介面:sourceMessage(...) sinkMessage(...) wait(Ticket&& ticket) asyncWait(Ticket&& ticket, TicketCallback callback) ,這四個介面入參都和 Ticket 資料分發子模組相關聯,具體核心程式碼實現如下:

1. //根據asioSession, expiration, message三個資訊構造資料接收類ASIOSourceTicket  
2. Ticket TransportLayerASIO::sourceMessage(...) {  
3.     ......  
4.     auto asioSession = checked_pointer_cast<ASIOSession>(session);  
5.     //根據asioSession, expiration, message三個資訊構造ASIOSourceTicket  
6.     auto ticket = stdx::make_unique<ASIOSourceTicket>(asioSession, expiration, message);  
7.     return {this, std::move(ticket)};  
8. }  
9.   
10. //根據asioSession, expiration, message三個資訊構造資料傳送類ASIOSinkTicket  
11. Ticket TransportLayerASIO::sinkMessage(...) {  
12.     auto asioSession = checked_pointer_cast<ASIOSession>(session);  
13.     auto ticket = stdx::make_unique<ASIOSinkTicket>(asioSession, expiration, message);  
14.     return {this, std::move(ticket)};  
15. }  
16.   
17. //同步接收或者傳送,最終呼叫ASIOSourceTicket::fill 或者 ASIOSinkTicket::fill  
18. Status TransportLayerASIO::wait(Ticket&& ticket) {  
19.     //獲取對應Ticket,接收對應ASIOSourceTicket,傳送對應ASIOSinkTicket  
20.     auto ownedASIOTicket = getOwnedTicketImpl(std::move(ticket));  
21.     auto asioTicket = checked_cast<ASIOTicket*>(ownedASIOTicket.get());  
22.     ......  
23.     //呼叫對應fill介面 同步接收ASIOSourceTicket::fill 或者 同步傳送ASIOSinkTicket::fill  
24.     asioTicket->fill(true, [&waitStatus](Status result) { waitStatus = result; });  
25.     return waitStatus;  
26. }  
27. //非同步接收或者傳送,最終呼叫ASIOSourceTicket::fill 或者 ASIOSinkTicket::fill  
28. void TransportLayerASIO::asyncWait(Ticket&& ticket, TicketCallback callback) {  
29.     //獲取對應資料收發的Ticket,接收對應ASIOSourceTicket,傳送對應ASIOSinkTicket  
30.     auto ownedASIOTicket = std::shared_ptr<TicketImpl>(getOwnedTicketImpl(std::move(ticket)));  
31.     auto asioTicket = checked_cast<ASIOTicket*>(ownedASIOTicket.get());  
32.   
33.    //呼叫對應ASIOTicket::fill  
34.     asioTicket->fill(  
35.         false,   [ callback = std::move(callback),  
36.         ownedASIOTicket = std::move(ownedASIOTicket) ](Status status) { callback(status); });  
37. }

  上面四個介面中的前兩個介面主要通過 Session, expiration, message 這三個引數來獲取對應的 Ticket  資訊,實際上mongodb 核心實現中把接收資料的 Ticket 和傳送資料的 Ticket 分別用不同的繼承類ASIOSourceTicket ASIOSinkTicket 來區分,三個引數的作用如下表所示:

引數名

作用

Session

代表一個連結,一個 session 和一個連結意義對應

expiration

資料收發超時相關設定

message

資料內容

資料收發包括同步收發和非同步收發,同步收發通過 TransportLayerASIO::wait () 實現,非同步收發通過 TransportLayerASIO::asyncWait () 實現。

 

注意: 以上四個介面把 TransportLayerASIO 類和 Ticket  資料收發類的關聯。    

2.2 總結
      transport_layer 套接字處理及傳輸層管理子模組主要由 transport_layer_manager transport_layer_asio 兩個核心類組成,這兩個類的核心介面功能總結如下表所示:

類命

介面名

功能說明

 

 

transport_layer_manager

TransportLayerManager::createWithConfig()

根據配置檔案選擇不同的 TransportLayer serviceExecutor

TransportLayerManager::setup()

已廢棄

TransportLayerManager::start()

已廢棄

 

 

 

 

 

 

 

TransportLayerASIO

TransportLayerASIO::Options::Options()

Net 相關配置

TransportLayerASIO::TransportLayerASIO()

初始化構造

TransportLayerASIO::sourceMessage()

獲取資料接收的 Ticket

TransportLayerASIO::sinkMessage()

獲取資料傳送的 Ticket

TransportLayerASIO::wait()

同步傳送接收或者傳送資料

TransportLayerASIO::asyncWait()

非同步方式傳送接收或者傳送資料

TransportLayerASIO::end()

關閉連結

TransportLayerASIO::setup()

建立套接字並 bind 繫結

TransportLayerASIO::start()

建立 listener 執行緒做監聽操作,同時註冊 accept 回撥

TransportLayerASIO::shutdown()

shutdown 處理及資源回收

TransportLayerASIO::_acceptConnection()

Accept 回撥註冊

T ransport_layer_manager 中初始化TransportLayer serviceExecutor net.TransportLayer 配置可以為 legacy asio ,其中 legacy 已經淘汰,當前核心只支援 asio 模式。 asio 配置對應的 TransportLayer TransportLayerASIO 實現,對應的 serviceExecutor 執行緒模型可以是 adaptive 動態執行緒模型,也可以是 synchronous 同步執行緒模型。

套接字建立、bind() 繫結、 listen() 監聽、 accept 事件註冊等都由本類實現,同時資料分發 Ticket 模組也與本模組關聯,一起配合完成整個後續 Ticket 模組模組的同步及非同步資料讀寫流程。此外,本模組還通過 ServiceEntryPoint 服務入口子模組聯動,保證了套接字初始化、accept 事件註冊完成後,服務入口子模組能有序的進行新連線接收處理。

 

接下來繼續分析本模組相關聯的 ServiceEntryPoint 服務入口子模組和 Ticket 資料分發子模組實現。

3.  service_entry_point 服務入口點子模組

service_entry_point 服務入口點子模組主要負責如下功能:新連線處理、 Session 會話管理、接收到一個完整報文後的回撥處理 ( 含報文解析、認證、引擎層處理等 )

該模組的原始碼實現主要包含以下幾個檔案:

  service_entry_point 開頭的程式碼檔案都和本模組相關,其中 service_entry_point_utils* 負責工作執行緒建立, service_entry_point_impl* 完成新連結回撥處理及 sesseion 會話管理。

3.1 核心原始碼實現

     服務入口子模組相關程式碼實現比較簡潔,主要由ServiceEntryPointImpl 類和 service_entry_point_utils 中的執行緒建立函式組成。

3.1.1 ServiceEntryPointImpl 類核心程式碼實現

ServiceEntryPointImpl 類主要成員和介面如下:

1. class ServiceEntryPointImpl : public ServiceEntryPoint {  
2.     MONGO_DISALLOW_COPYING(ServiceEntryPointImpl);  
3. public:  
4.     //建構函式  
5.     explicit ServiceEntryPointImpl(ServiceContext* svcCtx);     
6.     //以下三個介面進行session會話處理控制  
7.     void startSession(transport::SessionHandle session) final;  
8.     void endAllSessions(transport::Session::TagMask tags) final;  
9.     bool shutdown(Milliseconds timeout) final;  
10.     //session會話統計  
11.     Stats sessionStats() const final;  
12.     ......  
13. private:  
14.     //該list結構管理所有的ServiceStateMachine資訊  
15.     using SSMList = stdx::list<std::shared_ptr<ServiceStateMachine>>;  
16.     //SSMList對應的迭代器  
17.     using SSMListIterator = SSMList::iterator;  
18.     //賦值ServiceEntryPointImpl::ServiceEntryPointImpl  
19.     //對應ServiceContextMongoD(mongod)或者ServiceContextNoop(mongos)類  
20.     ServiceContext* const _svcCtx;   
21.     //該成員變數在程式碼中沒有使用  
22.     AtomicWord<std::size_t> _nWorkers;  
23.     //鎖  
24.     mutable stdx::mutex _sessionsMutex;  
25.     //一個新連結對應一個ssm儲存到ServiceEntryPointImpl._sessions中  
26.     SSMList _sessions;  
27.     //最大連結數控制  
28.     size_t _maxNumConnections{DEFAULT_MAX_CONN};  
29.     //當前的總連結數,不包括關閉的連結  
30.     AtomicWord<size_t> _currentConnections{0};  
31.     //所有的連結,包括已經關閉的連結  
32.     AtomicWord<size_t> _createdConnections{0};  
};

該類的幾個介面主要是session 相關控制處理,該類中的變數成員說明如下:

成員名

功能說明

_svcCtx

服務上下文, mongod 例項對應 ServiceContextMongoD 類, mongos 代理例項對應 ServiceContextNoop

_sessionsMutex

_sessions 鎖保護

_sessions

一個新連結對應一個 ssm 儲存到 ServiceEntryPointImpl._sessions

_maxNumConnections

最大連結數,預設 1000000 ,可以通過 maxConns 配置

_currentConnections

當前的線上連結數,不包括以前關閉的連結

_createdConnections

所有的連結,包括已經關閉的連結

ServiceEntryPointImpl 類最核心的 startSession () 介面負責每個新連線到來後的內部回撥處理,具體實現如下:

1. //新連結到來後的回撥處理  
2. void ServiceEntryPointImpl::startSession(transport::SessionHandle session) {   
3.     //獲取該新連線對應的服務端和客戶端地址資訊  
4.     const auto& remoteAddr = session->remote().sockAddr();  
5.     const auto& localAddr = session->local().sockAddr();  
6.     //服務端和客戶端地址記錄到session中  
7.     auto restrictionEnvironment =  stdx::make_unique<RestrictionEnvironment>(*remoteAddr, *localAddr);  
8.     RestrictionEnvironment::set(session, std::move(restrictionEnvironment));  
9.     ......  
10.   
11.     //獲取transportMode,kAsynchronous或者kSynchronous  
12.     auto transportMode = _svcCtx->getServiceExecutor()->transportMode();  
13.     //構造ssm  
14.     auto ssm = ServiceStateMachine::create(_svcCtx, session, transportMode);  
15.     {//該{}體內實現連結計數,同時把ssm統一新增到_sessions列表管理  
16.         stdx::lock_guard<decltype(_sessionsMutex)> lk(_sessionsMutex);  
17.         connectionCount = _sessions.size() + 1; //連線數自增  
18.         if (connectionCount <= _maxNumConnections) {  
19.             //新來的連結對應的session儲存到_sessions連結串列    
20.             //一個新連結對應一個ssm儲存到ServiceEntryPointImpl._sessions中  
21.             ssmIt = _sessions.emplace(_sessions.begin(), ssm);  
22.             _currentConnections.store(connectionCount);  
23.             _createdConnections.addAndFetch(1);  
24.         }  
25.     }  
26.     //連結超限,直接退出  
27.     if (connectionCount > _maxNumConnections) {   
28.         ......  
29.         return;  
30.     }  
31.     //連結關閉的回收處理  
32.     ssm->setCleanupHook([ this, ssmIt, session = std::move(session) ] {  
33.          ......  
34.     });  
35.     //獲取transport模式為同步模式還是非同步模式,也就是adaptive執行緒模式還是synchronous執行緒模式  
36.     auto ownership = ServiceStateMachine::Ownership::kOwned;  
37.     if (transportMode == transport::Mode::kSynchronous) {  
38.         ownership = ServiceStateMachine::Ownership::kStatic;  
39.     }  
40.     //ServiceStateMachine::start,這裡和服務狀態機模組銜接起來  
41.     ssm->start(ownership);  
42. }

  該介面拿到該連結對應的服務端和客戶端地址後,記錄到該連結對應session 中,然後根據該 session transportMode _svcCtx 構建一個服務狀態機 ssm(ServiceStateMachine) 。一個新連結對應一個唯一 session ,一個 session 對應一個唯一的服務狀態機 ssm ,這三者保持唯一的一對一關係。

      最終, startSession () 讓服務入口子模組、 session 會話子模組、 ssm 狀態機子模組關聯起來。   

3.1.2  service_entry_point_utils 核心程式碼實現

service_entry_point_utils 原始碼檔案只有 launchServiceWorkerThread 一個介面,該介面主要負責工作執行緒建立,並設定每個工作執行緒的執行緒棧大小,如果系統預設棧大於 1M ,則每個工作執行緒的執行緒棧大小設定為 1M ,如果系統棧大小小於 1M ,則以系統堆疊大小為準,同時 warning 列印提示。該函式實現如下:   

1. Status launchServiceWorkerThread(stdx::function<void()> task) {  
2.         static const size_t kStackSize = 1024 * 1024;  //1M  
3.         struct rlimit limits;  
4.         //或者系統堆疊大小  
5.         invariant(getrlimit(RLIMIT_STACK, &limits) == 0);  
6.         //如果系統堆疊大小大於1M,則預設設定執行緒棧大小為1M  
7.         if (limits.rlim_cur > kStackSize) {  
8.             size_t stackSizeToSet = kStackSize;  
9.             int failed = pthread_attr_setstacksize(&attrs, stackSizeToSet);  
10.             if (failed) {  
11.                 const auto ewd = errnoWithDescription(failed);  
12.                 warning() << "pthread_attr_setstacksize failed: " << ewd;  
13.             }  
14.         } else if (limits.rlim_cur < 1024 * 1024) {  
15.             //如果系統棧大小小於1M,則已係統堆疊為準,同時給出告警  
16.             warning() << "Stack size set to " << (limits.rlim_cur / 1024) << "KB. We suggest 1MB";  
17.         }}  
18.         ......  
19.         //task引數傳遞給新建執行緒  
20.         auto ctx = stdx::make_unique<stdx::function<void()>>(std::move(task));  
21.         int failed = pthread_create(&thread, &attrs, runFunc, ctx.get());   
22.         ......  
23. }

3.2 總結

service_entry_point 服務入口點子模組主要負責新連線後的回撥處理及工作執行緒建立,該模組和後續的 session 會話模組、 SSM 服務狀態機模組銜接配合,完成資料收發的正常邏輯轉換處理。上面的分析只列出了服務入口點子模組的核心介面實現,下表總結該模組所有的介面功能:

介面

功能說明

 

 

 

 

ServiceEntryPointImpl

ServiceEntryPointImpl::ServiceEntryPointImpl()

構造初始化,最大連結數限制

ServiceEntryPointImpl::startSession()

新連結的回撥處理

ServiceEntryPointImpl::endAllSessions()

Ssm 服務狀態機及 session 會話回收處理

ServiceEntryPointImpl::shutdown()

例項下線處理

ServiceEntryPointImpl::sessionStats()

獲取連結統計資訊

service_entry_point_utils

launchServiceWorkerThread

建立工作執行緒,同時限制每個執行緒對應執行緒棧大小

 

3.  Ticket 資料收發子模組

    Ticket 資料收發子模組主要功能如下:呼叫 session 子模組進行底層 asio 庫處理、拆分資料接收和資料傳送到兩個類、完整 mongodb 報文讀取 、接收或者傳送 mongodb 報文後的回撥處理。

3.1 ASIOTicket 類核心程式碼實現

Ticket 資料收發模組相關實現主要由 ASIOTicket 類完成,該類結構如下:

1. //下面的ASIOSinkTicket和ASIOSourceTicket繼承該類,用於控制資料的傳送和接收  
2. class TransportLayerASIO::ASIOTicket : public TicketImpl {  
3. public:  
4.     //初始化構造  
5.     explicit ASIOTicket(const ASIOSessionHandle& session, Date_t expiration);  
6.     //獲取sessionId  
7.     SessionId sessionId() const final {  
8.         return _sessionId;  
9.     }  
10.     //asio模式沒用,針對legacy模型  
11.     Date_t expiration() const final {  
12.         return _expiration;  
13.     }  
14. 
15.     //以下四個介面用於資料收發相關處理  
16.     void fill(bool sync, TicketCallback&& cb);  
17. protected:  
18.     void finishFill(Status status);  
19.     bool isSync() const;  
20.     virtual void fillImpl() = 0;  
21. private:  
22.     //會話資訊,一個連結一個session  
23.     std::weak_ptr<ASIOSession> _session;  
24.     //每個session有一個唯一id  
25.     const SessionId _sessionId;  
26.     //asio模型沒用,針對legacy生效  
27.     const Date_t _expiration;  
28.     //資料傳送或者接收成功後的回撥處理  
29.     TicketCallback _fillCallback;  
30.     //同步方式還是非同步方式進行資料處理,預設非同步  
31.     bool _fillSync;  
32. };

該類保護多個成員變數,這些成員變數功能說明如下:

成員名

作用

_session

Session 會話資訊,一個連結對應一個 session

_sessionId

每個 session 都有一個對應的唯一 ID

_expiration

Legacy 模式使用,當前都是用 asio ,該成員已淘汰

_fillCallback

傳送或者接收一個完整 mongodb 報文後的回撥處理

_fillSync

同步還是非同步方式收發資料。 adaptive 執行緒模型為非同步, synchronous 執行緒模型為同步讀寫方式

mongodb 在具體實現上,資料接收和資料傳送分開實現,分別是資料接收類 ASIOSourceTicket 和資料傳送類 ASIOSinkTicket ,這兩個類都繼承自 ASIOTicket 類,這兩個類的主要結構如下:

1. //資料接收的ticket  
2. class TransportLayerASIO::ASIOSourceTicket : public TransportLayerASIO::ASIOTicket {  
3. public:  
4.     //初始化構造  
5.     ASIOSourceTicket(const ASIOSessionHandle& session, Date_t expiration, Message* msg);  
6. protected:  
7.     //資料接收Impl  
8.     void fillImpl() final;  
9. private:  
10.     //接收到mongodb頭部資料後的回撥處理  
11.     void _headerCallback(const std::error_code& ec, size_t size);  
12.     //接收到mongodb包體資料後的回撥處理    
13.     void _bodyCallback(const std::error_code& ec, size_t size);  
14.   
15.     //儲存資料的buffer,網路IO讀取到的原始資料內容  
16.     SharedBuffer _buffer;  
17.     //資料Message管理,資料來源為_buffer  
18.     Message* _target;  
19. };  
1. 
2.   
20. //資料傳送的ticket  
21. class TransportLayerASIO::ASIOSinkTicket : public TransportLayerASIO::ASIOTicket {  
22.  public:  
23.     //初始化構造  
24.     ASIOSinkTicket(const ASIOSessionHandle& session, Date_t expiration, const Message& msg);  
25. protected:  
26.     //資料傳送Impl  
27.     void fillImpl() final;  
28. private:  
29.     //傳送資料完成的回撥處理  
30.     void _sinkCallback(const std::error_code& ec, size_t size);  
31.     //需要傳送的資料message資訊  
32.     Message _msgToSend;  
33. };

   從上面的程式碼實現可以看出, ASIOSinkTicket  ASIOSourceTicket  類介面及成員實現幾乎意義,只是具體的實現方法不同,下面對ASIOSourceTicket ASIOSinkTicket  相關核心程式碼實現進行分析。

3.1.2 ASIOSourceTicket  資料接收核心程式碼實現

資料接收過程核心程式碼如下:

1. //資料接收的fillImpl介面實現  
2. void TransportLayerASIO::ASIOSourceTicket::fillImpl() {    
3.     //獲取對應session資訊  
4.     auto session = getSession();  
5.     if (!session)  
6.         return;  
7.     //收到讀取mongodb頭部資料,頭部資料長度是固定的kHeaderSize位元組  
8.     const auto initBufSize = kHeaderSize;  
9.     _buffer = SharedBuffer::allocate(initBufSize);  
10.   
11.     //呼叫TransportLayerASIO::ASIOSession::read讀取底層資料存入_buffer  
12.     //讀完頭部資料後執行對應的_headerCallback回撥函式  
13.     session->read(isSync(),  
14.                   asio::buffer(_buffer.get(), initBufSize), //先讀取頭部欄位出來  
15.                   [this](const std::error_code& ec, size_t size) { _headerCallback(ec, size); });  
16. }  
17.   
18. //讀取到mongodb header頭部資訊後的回撥處理  
19. void TransportLayerASIO::ASIOSourceTicket::_headerCallback(const std::error_code& ec, size_t size) {  
20.     ......  
21.     //獲取session資訊  
22.     auto session = getSession();  
23.     if (!session)  
24.         return;  
25.     //從_buffer中獲取頭部資訊  
26.     MSGHEADER::View headerView(_buffer.get());  
27.     //獲取message長度  
28.     auto msgLen = static_cast<size_t>(headerView.getMessageLength());  
29.     //長度太小或者太大,直接報錯  
30.     if (msgLen < kHeaderSize || msgLen > MaxMessageSizeBytes) {  
31.         .......  
32.         return;  
33.     }  
34.     ....  
35.    //內容還不夠一個mongo協議報文,繼續讀取body長度位元組的資料,讀取完畢後開始body處理  
36.    //注意這裡是realloc,保證頭部和body在同一個buffer中  
37.     _buffer.realloc(msgLen);   
38.     MsgData::View msgView(_buffer.get());  
39.   
40.     //呼叫底層TransportLayerASIO::ASIOSession::read讀取資料body   
41.     session->read(isSync(),  
42.       //資料讀取到該buffer                  
43.       asio::buffer(msgView.data(), msgView.dataLen()),  
44.       //讀取成功後的回撥處理  
45.       [this](const std::error_code& ec, size_t size) { _bodyCallback(ec, size); });  
46. }  
47.   
48. //_headerCallback對header讀取後解析header頭部獲取到對應的msg長度,然後開始body部分處理  
49. void TransportLayerASIO::ASIOSourceTicket::_bodyCallback(const std::error_code& ec, size_t size) {  
50.     ......  
51.     //buffer轉存到_target中  
52.     _target->setData(std::move(_buffer));  
53.     //流量統計  
54.     networkCounter.hitPhysicalIn(_target->size());  
55.     //TransportLayerASIO::ASIOTicket::finishFill    
56.     finishFill(Status::OK()); //包體內容讀完後,開始下一階段的處理    
57.     //報文讀取完後的下一階段就是報文內容處理,開始走ServiceStateMachine::_processMessage  
58. }

Mongodb 協議由 msg header + msg body 組成,一個完整的 mongodb 報文內容格式如下:

上圖所示各個欄位及body 內容部分功能說明如下表:

Header or body

欄位名

功能說明

 

msg header

messageLength

整個 message 長度,包括 header 長度和 body 長度

requestID

該請求 id 資訊

responseTo

應答 id

opCode

操作型別: OP_UPDATE OP_INSERT OP_QUERY OP_DELETE

msg body

Body

不同 opCode 對應的包體內容

     ASIOSourceTicket 類的幾個核心介面都是圍繞這一原則展開,整個mongodb 資料接收流程如下:

1.  讀取mongodb 頭部 header 資料,解析出 header 中的 messageLength 欄位。

2.  檢查messageLength 欄位是否在指定的合理範圍,該欄位不能小於 Header 整個頭部大小,也不能超過 MaxMessageSizeBytes 最大長度。

3.  Header len 檢查通過,說明讀取 header 資料完成,於是執行 _headerCallback 回撥。

4.  realloc 更多的空間來儲存 body 內容。

5.  繼續讀取body len 長度資料,讀取 body 完成後,執行 _bodyCallback 回撥處理。

3.1.3 ASIOSinkTicket 資料傳送類核心程式碼實現

    ASIOSinkTicket 傳送類相比接收類,沒有資料解析相關的流程,因此實現過程會更加簡單,具體原始碼實現如下:

1. //傳送資料成功後的回撥處理  
2. void TransportLayerASIO::ASIOSinkTicket::_sinkCallback(const std::error_code& ec, size_t size) {  
3.     //傳送的網路位元組數統計  
4.     networkCounter.hitPhysicalOut(_msgToSend.size());   
5.     //執行SSM中對應的狀態流程  
6.     finishFill(ec ? errorCodeToStatus(ec) : Status::OK());  
7. }  
8.   
9. //傳送資料的fillImpl  
10. void TransportLayerASIO::ASIOSinkTicket::fillImpl() {  
11.     //獲取對應session  
12.     auto session = getSession();  
13.     if (!session)  
14.         return;  
15.   
16.     //呼叫底層TransportLayerASIO::ASIOSession::write傳送資料,傳送成功後執行_sinkCallback回撥   
17.     session->write(isSync(),  
18.        asio::buffer(_msgToSend.buf(), _msgToSend.size()),  
19.        //傳送資料成功後的callback回撥  
20.        [this](const std::error_code& ec, size_t size) { _sinkCallback(ec, size); });  
21. }

3.2 總結

從上面的分析可以看出,Ticket 資料收發模組主要呼叫 session 會話模組來進行底層資料的讀寫、解析,當讀取或者傳送一個完整的 mongodb 報文後最終交由 SSM 服務狀態機模組排程處理。

ticket 模組主要介面功能總結如下表所示:

類命

介面名

功能說明

 

 

ASIOTicket

ASIOTicket::getSession()

獲取 session 資訊

ASIOTicket::isSync()

判斷是同步收發還是非同步收發

ASIOTicket::finishFill()

執行 _fillCallback 回撥

ASIOTicket::fill()

_fillCallback 賦值

ASIOTicket::ASIOTicket()

ASIOTicket 構造初始化

 

ASIOSourceTicket

ASIOSourceTicket::ASIOSourceTicket()

ASIOSourceTicket 初始化

ASIOSourceTicket::_bodyCallback()

接收到 mesg header+body 後的回撥處理

ASIOSourceTicket::_headerCallback

接收到 msg header 後的回撥處理

 

ASIOSinkTicket

ASIOSinkTicket::ASIOSinkTicket()

ASIOSinkTicket 初始化構造

ASIOSourceTicket::fillImpl()

傳送指定 msg 資料,傳送完成後執行回撥

ASIOSinkTicket::_sinkCallback

傳送 msg 成功後的回撥處理

前面的分析也可以看出,Ticket 資料收發模組會呼叫 session 處理模組來進行真正的資料讀寫,同時接收或者傳送完一個完整 mongodb 報文後的後續回撥處理講交由 SSM 服務狀態機模組處理。

4.  Session 會話子模組

Session 會話模組功能主要如下:負責記錄 HostAndPort 、和底層 asio 庫直接互動,實現資料的同步或者非同步收發。一個新連線 fd 對應一個唯一的 session ,對 fd 的操作直接對映為 session 操作。 Session 會話子模組主要程式碼實現相關檔案如下:

4.1 session 會話子模組核心程式碼實現

1. class TransportLayerASIO::ASIOSession : public Session {  
2.     //初始化構造  
3.     ASIOSession(TransportLayerASIO* tl, GenericSocket socket);  
4.     //獲取本session使用的tl  
5.     TransportLayer* getTransportLayer();  
6.     //以下四個介面套接字相關,本端/對端地址獲取,獲取fd,關閉fd等  
7.     const HostAndPort& remote();  
8.     const HostAndPort& local();  
9.     GenericSocket& getSocket();  
10.     void shutdown();  
11.   
12.     //以下四個介面呼叫asio網路庫實現資料的同步收發和非同步收發  
13.     void read(...)  
14.     void write(...)  
15.     void opportunisticRead(...)  
16.     void opportunisticWrite(...)  
17.   
18.     //遠端地址資訊  
19.     HostAndPort _remote;  
20.     //本段地址資訊  
21.     HostAndPort _local;  
22.     //賦值見TransportLayerASIO::_acceptConnection  
23.     //也就是fd,一個新連線對應一個_socket  
24.     GenericSocket _socket;  
25.     //SSL相關不做分析,  
26. #ifdef MONGO_CONFIG_SSL  
27.     boost::optional<asio::ssl::stream<decltype(_socket)>> _sslSocket;  
28.     bool _ranHandshake = false;  
29. #endif  
30.   
31.     //本套接字對應的tl,賦值建TransportLayerASIO::_acceptConnection(...)  
32.     TransportLayerASIO* const _tl;  
33. }

該類最核心的三個介面ASIOSession(...) opportunisticRead(...) opportunisticWrite(..) 分別完成套接字處理、呼叫 asio 庫介面實現底層資料讀和底層資料寫。這三個核心介面原始碼實現如下:

1. //初始化構造 TransportLayerASIO::_acceptConnection呼叫  
2. ASIOSession(TransportLayerASIO* tl, GenericSocket socket)  
3.     //fd描述符及TL初始化賦值  
4.     : _socket(std::move(socket)), _tl(tl) {  
5.     std::error_code ec;  
6.   
7.     //非同步方式設定為非阻塞讀  
8.     _socket.non_blocking(_tl->_listenerOptions.transportMode == Mode::kAsynchronous, ec);  
9.     fassert(40490, ec.value() == 0);  
10.   
11.     //獲取套接字的family  
12.     auto family = endpointToSockAddr(_socket.local_endpoint()).getType();  
13.     //滿足AF_INET
14.     if (family == AF_INET || family == AF_INET6) {  
15.         //no_delay keep_alive套接字系統引數設定  
16.         _socket.set_option(asio::ip::tcp::no_delay(true));  
17.         _socket.set_option(asio::socket_base::keep_alive(true));  
18.         //KeepAlive系統引數設定  
19.         setSocketKeepAliveParams(_socket.native_handle());  
20.     }  
21.   
22.     //獲取本端和對端地址  
23.     _local = endpointToHostAndPort(_socket.local_endpoint());  
24.     _remote = endpointToHostAndPort(_socket.remote_endpoint(ec));  
25.     if (ec) {  
26.         LOG(3) << "Unable to get remote endpoint address: " << ec.message();  
27.     }  
28. }

該類初始化的時候完成新連線_socket 相關的初始化設定,包括阻塞讀寫還是非阻塞讀寫。如果是同步執行緒模型 ( 一個連結一個執行緒 ) ,則讀寫方式位阻塞讀寫;如果是非同步執行緒模型 (adaptive 動態執行緒模型 ) ,則呼叫 asio 網路庫介面實現非同步讀寫。

此外,該連結_socket 對應的客戶端 ip:port 和服務端 ip:port 也在該初始化類中獲取,最終儲存到本 session _remote _local 成員中。

資料讀取核心程式碼實現如下:

1. //讀取指定長度資料,然後執行handler回撥  
2. void opportunisticRead(...) {  
3.     std::error_code ec;  
4.     //如果是非同步執行緒模型,在ASIOSession構造初始化的時候會設定non_blocking非阻塞模式  
5.     //非同步執行緒模型這裡實際上是非阻塞讀取,如果是同步執行緒模型,則沒有non_blocking設定,也就是阻塞讀取  
6.     auto size = asio::read(stream, buffers, ec);    
7.   
8.     //如果是非同步讀,並且read返回would_block或者try_again說明指定長度的資料還沒有讀取完畢  
9.     if ((ec == asio::error::would_block || ec == asio::error::try_again) && !sync) {  
10.         //buffers有大小size,實際讀最多讀size位元組  
11.         MutableBufferSequence asyncBuffers(buffers);  
12.         if (size > 0) {  
13.             asyncBuffers += size; //buffer offset向後移動  
14.         }  
15.   
16.         //繼續非同步方式讀取資料,讀取到指定長度資料後執行handler回撥處理  
17.         asio::async_read(stream, asyncBuffers, std::forward<CompleteHandler>(handler));  
18.     } else {   
19.         //阻塞方式讀取read返回後可以保證讀取到了size位元組長度的資料  
20.         //直接read獲取到size位元組資料,則直接執行handler   
21.         handler(ec, size);  
22.     }  
23. }

opportunisticRead 首先呼叫asio::read(stream, buffers, ec) 讀取 buffers 對應 size 長度的資料, buffers 空間大小就是需要讀取的資料 size 大小。如果是同步執行緒模型,則這裡為阻塞式讀取,直到讀到 size 位元組才會返回;如果是非同步執行緒模型,這這裡是非阻塞讀取,非阻塞讀取當核心網路協議棧資料讀取完畢後,如果還沒有讀到 size 位元組,則繼續進行 async_read 非同步讀取。

buffers 指定的 size 位元組全部讀取完整後,不管是同步模式還是非同步模式,最終都會執行 handler 回撥,開始後續的資料解析及處理流程。

傳送資料核心程式碼實現如下:

1. //傳送資料  
2. void opportunisticWrite(...) {  
3.     std::error_code ec;  
4.     //如果是同步模式,則阻塞寫,直到全部寫成功。非同步模式則非阻塞寫  
5.     auto size = asio::write(stream, buffers, ec);   
6.   
7.     //非同步寫如果返回try_again說明資料還沒有傳送完,則繼續非同步寫傳送  
8.     if ((ec == asio::error::would_block || ec == asio::error::try_again) && !sync) {  
9.         ConstBufferSequence asyncBuffers(buffers);  
10.         if (size > 0) {  //buffer中資料指標偏移計數
11.             asyncBuffers += size;  
12.         }  
13.         //非同步寫傳送完成,執行handler回撥  
14.         asio::async_write(stream, asyncBuffers, std::forward<CompleteHandler>(handler));  
15.     } else {  
16.         //同步寫成功,則直接執行handler回撥處理  
17.         handler(ec, size);  
18.     }  
19. }

資料傳送流程和資料接收流程類似,也分位同步模式傳送和非同步模式傳送,同步模式傳送為阻塞式寫,只有當所有資料通過 asio::write () 傳送成功後才返回;非同步模式傳送為非阻塞寫, asio::write () 不一定全部傳送出去,因此需要再次呼叫 asio 庫的 asio::async_write( ) 進行非同步傳送。

不管是同步模式還是非同步模式傳送資料,最終資料傳送成功後,都會呼叫 handler () 回撥來執行後續的流程。

4.2 總結

從上面的程式碼分析可以看出,session 會話模組最終直接和 asio 網路庫互動實現資料的讀寫操作。該模組核心介面功能總結如下表:

類名

介面名

功能說明

 

 

 

 

ASIOSession

ASIOSession(...)

新連線 fd 相關處理,如是否非阻塞、 delay 設定、 keep_alive 設定、兩端地址獲取等

getTransportLayer()

獲取對應 TL

remote()

獲取該連結對應的對端 Ip:port 資訊

local()

獲取該連結對應的對端 Ip:port 資訊

getSocket()

獲取該連結的 fd

shutdown()

fd 回收處理

opportunisticRead()

同步或者非同步資料讀操作

opportunisticWrite()

同步或者非同步資料寫操作

5.  總結

     Mongodb 網路傳輸處理原始碼實現及效能調優 - 體驗核心效能極致設計》 一文對 mongodb 網路傳輸模組中的 ASIO 網路庫實現、 service_executor 服務執行子模組 ( 即執行緒模型子模組 ) 進行了詳細的分析,加上本文的 transport_layer 套接字處理及傳輸層管理子模組、 session 會話子模組、 Ticket 資料收發子模組、 service_entry_point 服務入口點子模組。

    transport_layer 套接字處理及傳輸層管理子模組主要由 transport_layer_manager transport_layer_asio 兩個核心類組成。分別完成 net 相關的配置檔案初始化操作,套接字初始化及處理,最終 transport_layer_asio 的相應介面實現了和 ticket 資料分發子模組、服務入口點子模組的關聯。

     服務入口子模組主要由ServiceEntryPointImpl 類和 service_entry_point_utils 中的執行緒建立函式組成,實現新連線 accept 處理及控制。該模組通過 startSession() 讓服務入口子模組、 session 會話子模組、 ssm 狀態機子模組關聯起來。

    ticket 資料收發子模組主要功能如下:呼叫 session 子模組進行底層 asio 庫處理、拆分資料接收和資料傳送到兩個類、完整 mongodb 報文讀取 、接收或者傳送 mongodb 報文後的回撥處理 , 回撥處理由 SSM 服務狀態機模組管理,當讀取或者傳送一個完整的 mongodb 報文後最終交由 SSM 服務狀態機模組排程處理。。

    Session 會話模組功能主要如下:負責記錄 HostAndPort 、和底層 asio 庫直接互動,實現資料的同步或者非同步收發。一個新連線 fd 對應一個唯一的 session ,對 fd 的操作直接對映為 session 操作。

到這裡,整個mongodb 網路傳輸層模組分析只差 service_state_machine 狀態機排程子模組,狀態機排程子模組相比本文分析的幾個子模組更加複雜,因此將在下期《 mongodb 網路傳輸層模組原始碼分析三》中單獨分析。

本文所有原始碼註釋分析詳見如下連結: mongodb 網路傳輸模組詳細原始碼分析


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69984922/viewspace-2729717/,如需轉載,請註明出處,否則將追究法律責任。

相關文章