tars服務端(一):server的啓動流程

前言

tars服務端在框架層幫我們封裝了rpc的實現細節,讓開發人員只需專注於業務層的接口實現。這一篇文章主要分兩個部分來初步瞭解tars服務端:

  1. 先從頂層瞭解服務端框架層提供了什麼,不糾結代碼細節
  2. 再結合代碼具體的瞭解一個tars server的啓動流程

一.框架封裝的功能

  • 線程模型創建:主線程,網絡線程,業務線程
  • 網絡io模型創建
  • 業務模型創建:Adapter,Servant的管理
  • 通信器創建
  • 其他的:異常信息,統計信息上報,ssl,協程,日誌,鏈路跟蹤,消息染色等

這一節先重點了解一下線程模型,servant和adapter的細節。

 

1.線程模型

  1. 1個主線程:框架的初始化,業務層初始化,servant和adapter的管理,epoll模型的創建,業務線程創建,網絡線程創建。
  2. N個網絡線程:調度epoll處理網絡事件,socket的accept,read,write,請求包push到接收隊列讓業務線程處理。
  3. M個業務線程:解析請求包,分發到業務層進行處理。
  4. 其他的線程,例如通信器的線程,不在這個線程模型的範疇內。

  • 藍色部分,主線程啓動了業務線程。業務線程的數量可以針對不同的servant設置不同的線程數。之後業務線程在一個循環邏輯中,每次從隊列中取出請求進行處理。
  • 黃色部分,網絡線程啓動。框架限制了網絡線程的數量最大爲15,這個和鏈接管理的uid有關,uid的組成有4位是給網絡線程索引的,所以最大的線程索引是15。網絡線程同樣把收到的請求push到隊列中,供業務線程處理。
  • 紅色部分,當進程被設置爲terminate的時候,主線程結束,同時網絡線程和業務線程也把terminate設置爲true,線程結束。
  • 紫色的接收隊列,網絡線程收到請求後push到隊列中,業務線程通過waitForRevcQueue取出請求進行處理。接收隊列只有1個,屬於adapter的。
  • 灰色的N個發送隊列,每個網絡線程都有1個發送隊列,當業務線程向客戶端回包的時候,會push到發送隊列中,網絡線程把發送包取出來發送到對應的客戶端。
  • 另外還有一點需要注意的,不管是網絡線程還是業務線程,都是基於一個循環去處理事件和消息,所以一般不能出現耗時比較久的操作(例如長時間的阻塞)。特別是網絡線程,如果在處理一個fd的事件時,如果長時間阻塞,那麼在阻塞期間這個網絡線程的其他fd就都處理不了了;而業務線程,雖然會同時有多個業務線程去處理一個消息隊列,但是如果出現了比較多的阻塞操作,也會影響到整個消息隊列的消費速度(tars提供了協程解決這個問題)。

 

2.Servant和Adapter

       一開始接觸tars會比較容易混淆Servant和Adapter。官網的文檔告訴我們,tars節點的名字由三級組成:App.Server.Servant,在web上部署的時候也是一個Servant對應一個ip:port;但實際上Servant並沒有和ip:port直接綁定,而是由Adapter來管理ip:port,Servant再和Adapter進行一一映射。從tars Server的配置文件就可以看出來,ip和port都是在Adapter的配置中的,同時每個Adapter還對應着一個servant的配置項:

<TestApp.HelloServer.HelloObjAdapter>                                                                                    
        allow                                                                                                                  
        endpoint=tcp -h 172.17.136.115 -p 23800 -t 60000                                                                       
        handlegroup=TestApp.HelloServer.HelloObjAdapter                                                                        
        maxconns=100000                                                                                                        
        protocol=tars                                                                                                          
        queuecap=50000                                                                                                         
        queuetimeout=20000                                                                                                     
        servant=TestApp.HelloServer.HelloObj                                                                                   
        threads=5                                                                                                             
</TestApp.HelloServer.HelloObjAdapter> 

        Servant和Adapter如何映射起來呢?每個Servant和Adapter都有自己的名字,在框架進行初始化時,會調用setAdapterServant()把Servant和Adapter的名字映射起來:

  • Adapter和Servant的映射關係保存在全局單列類ServantHelperManager中
  • 調用setAdapterServant把兩者的名字存在map中
  • 用Adapter的名字調用getAdapterServant可以獲取到對應的Servant名字
  • 用Servant的名字調用getServantAdapter可以獲取到對應的Adapter名字         

每個Adapter管理一個端口,同時網絡線程和業務線程都是直接與Adapter進行交互,可以說Adapter是端口在框架網絡層的封裝;而Servant是該端口提供的業務邏輯的集合:

 

  • 這裏只畫了用tars協議的servant,如果是非tars協議,那麼在servant中的表現就是重載了父類的doRequest()接口
  • 每個adapter有屬於自己的接收隊列,在網絡線程收到請求後,找到對應的Adapter,把包push到Adapter的接收隊列中,然後業務線程再把包從adapter的接收隊列中取出來分發到servant中進行處理:

 

3.框架的變與不變

以官網的HelloServer爲例,實現自己的業務邏輯只需要修改下面這四個文件就可以了:

  • HelloImp.cpp
  • HelloImp.h
  • HelloServer.cpp
  • HelloServer.h

   只需兩步:

addServant實際上是向框架中註冊了HelloImp的生成方式,而並不是創建了HelloImp的實例。HelloImp這個Servant的實例創建其實是在後面的業務線程中做的:

  • addServant把HelloImp這個Servant的生成方法保存在全局單例類ServantHelperManager中
  • 框架的業務線程在真正需要用到HelloImp的時候會自動調用ServantHelperManager的create方法創建實例

框架藉助ServantHelperManager這個全局單列類實現了servant的可插拔註冊,又在運行時動態創建servant實例,通過servant管理所有可變的業務邏輯。總結起來,框架爲我們封裝了線程模型,epoll模型,網絡通信細節和協議解析等不變的部分;而變化的部分通過繼承把兩個類暴露給業務層:

  1. 業務邏輯是變化的。一個server可以同時監聽多個端口,框架把每個端口提供的服務抽象成servant,業務層通過繼承servant,可以實現自己的業務邏輯,然後把實現好的servant註冊進框架中。
  2. 業務層和框架層的交互,例如業務層需要把servant註冊進框架,把協議註冊進框架,註冊命令到框架,獲取框架的默認通信器,拉配置文件等,這些操作都是通過Application暴露給業務層的。每個業務server繼承Application,還可以重載Application的一些接口,實現業務的自定義。

  HelloImp和HelloServer這兩個類分別繼承於Servant和Application,是變化的部分,它們與框架的交互如下:

  • 藍色箭頭方向,HelloServer通過幾個接口調用了框架提供的功能:servant的註冊,配置文件拉取,協議註冊等;
  • 黃色箭頭方向,框架在收到客戶端的請求後,找到該端口對應的servant,如果是tars協議,則調用servant的onDispatch接口把請求分發到HelloImp對應的rpc接口;如果不是tars接口,則調用servant的doRequest接口,而HelloImp會重載這個接口去處理請求。

二.主線程邏輯

主線程對框架進行初始化,創建epoll,啓動網絡線程,handle(業務)線程等,跟着主線程的邏輯走一遍,可以熟悉框架的功能。流程圖如下:

  • 紅色部分,是主流程,其實就是上面說的主線程幹了啥:初始化,adapter創建,epoller創建,線程創建等
  • 與主流程對應的還有很多細節放在子流程中,當然中間還是省略了很多細節,下面會結合代碼進行詳細講解
  • 黃色部分,是epoller模型的創建,分佈在兩個階段中。先是在adapter創建的時候,就已經把對應的ip和端口進行socket的創建,綁定,和監聽;然後在epoller創建之後,把監聽的fd加到epoller中。
  • 灰色部分,是adapter和servant的創建綁定過程。先是在創建adapter的時候,就執行adapter和servant的名字映射setAdapterServant();接着在業務初始化的時候addServant;最後,在業務線程啓動後,生成每個業務線程自己的servant實例。
  • 藍色部分,是三種線程最後的狀態,分別等待在接收隊列,epoll,或者條件變量上,直到terminate。(實際上,handle線程是等在handleGroup的monitor上,這裏爲了簡化,才畫成把handle線程等在接收隊列上)

下面結合代碼講一下整個邏輯。直接從main函數看起:

int                                                                                                                            
main(int argc, char* argv[])                                                                                                   
{                                                                                                                              
    try                                                                                                                        
    {                                                                                                                          
        g_app.main(argc, argv);                                                                                                
        g_app.waitForShutdown();                                                                                               
    }                                                                                                                          
    catch (std::exception& e)                                                                                                  
    {                                                                                                                          
        cerr << "std::exception:" << e.what() << std::endl;                                                                    
    }                                                                                                                          
    catch (...)                                                                                                                
    {                                                                                                                          
        cerr << "unknown exception." << std::endl;                                                                             
    }                                                                                                                          
    return -1;                                                                                                                 
}                           

主要邏輯分佈在application.main()和application.watiForShutdown()。

 

application.main():

1.框架初始化

    執行一些初始化工作:

    (1)配置文件解析_conf

    (2)通信器創建_communicator

    (3)_epollServer創建,網絡線程數設置,網絡線程內存池配置等

        同時在_epollServer的構造函數中創建了網絡線程對象(注意:這裏網絡線程並未啓動,只是創建了線程對象)。

for (size_t i = 0; i < _netThreadNum; ++i)                                                                                                                                                 
    {                                                                                                                                                                                          
        TC_EpollServer::NetThread* netThreads = new TC_EpollServer::NetThread(this);                                                                                                           
        _netThreads.push_back(netThreads);                                                                                                                                                     
    }         

    (4)在_communicator中創建配置中心,日誌中心,notify,node等框架節點的本地代理,用於之後與框架其他節點的通信。

//初始化到LogServer代理                                                                                                                                                   
bool bLogStatReport = (_conf.get("/tars/application/server<logstatreport>", "0") == "1") ? true : false;                   
TarsTimeLogger::getInstance()->setLogInfo(_communicator, ServerConfig::Log, ServerConfig::Application, ServerConfig::ServerName, ServerConfig::LogPath, setDivision(), bLogStatReport);                                                                   
                                                                                                                               
//初始化到配置中心代理                                                                                                     
TarsRemoteConfig::getInstance()->setConfigInfo(_communicator, ServerConfig::Config, ServerConfig::Application,ServerConfig::ServerName,ServerConfig::BasePath,setDivision());                                                                           
                                                                                                                               
                        
//初始化到信息中心代理                                                                                                     
TarsRemoteNotify::getInstance()->setNotifyInfo(_communicator, ServerConfig::Notify, ServerConfig::Application, ServerConfig::ServerName, setDivision());                                                                                                  
                                                                                                                               
//初始化到Node的代理                                                                                                       
TarsNodeFHelper::getInstance()->setNodeInfo(_communicator, ServerConfig::Node, ServerConfig::Application, ServerConfig::ServerName);                                                                                                                      

2.adapter創建

    (1)這裏創建的adapter有兩種:

         a.框架用於接收web命令的adapter(AdminAdapter),框架裏叫管理對象。(綁定了配置裏面的Local地址:127.0.0.1)

            該adapter對應的servant已經由框架實現好,在AdminServant類中,提供了notify和shutdown兩個rpc接口            

ServantHelperManager::getInstance()->addServant<AdminServant>("AdminObj");

ServantHelperManager::getInstance()->setAdapterServant("AdminAdapter", "AdminObj");

TC_EpollServer::BindAdapterPtr lsPtr = new TC_EpollServer::BindAdapter(_epollServer.get());

lsPtr->setName("AdminAdapter");

lsPtr->setEndpoint(ServerConfig::Local);

lsPtr->setMaxConns(TC_EpollServer::BindAdapter::DEFAULT_MAX_CONN);

lsPtr->setQueueCapacity(TC_EpollServer::BindAdapter::DEFAULT_QUEUE_CAP);

lsPtr->setQueueTimeout(TC_EpollServer::BindAdapter::DEFAULT_QUEUE_TIMEOUT);

lsPtr->setProtocolName("tars");

lsPtr->setProtocol(AppProtocol::parse);                                                                                
                                                                                                                               
lsPtr->setHandleGroupName("AdminAdapter");                                                                             
                                                                                                                               
lsPtr->setHandleNum(1);                                                                                                
                                                                                                                               
lsPtr->setHandle<ServantHandle>();                                                                                     
                                                                                                                               
epollServer->bind(lsPtr);  

         b.我們部署的業務adapter(TestApp.HelloServer.HelloObjAdapter)。          

if (_conf.getDomainVector("/tars/application/server", adapterName))                                                        
{                                                                                                                          
    for (size_t i = 0; i < adapterName.size(); i++)                                                                        
    {                                                                                                                      
        string servant = _conf.get("/tars/application/server/" + adapterName[i] + "<servant>");                            
                                                                                                                               
        checkServantNameValid(servant, sPrefix);                                                                           
                                                                                                                               
        ServantHelperManager::getInstance()->setAdapterServant(adapterName[i], servant);                                   
                                                                                                                               
        TC_EpollServer::BindAdapterPtr bindAdapter = new TC_EpollServer::BindAdapter(_epollServer.get());                  
                                                                                                                               
                                                                                                                               
        string sLastPath = "/tars/application/server/" + adapterName[i];                                                   
                                                                                                                               
        bindAdapter->setName(adapterName[i]);                                                                              
                                                                                                                               
        bindAdapter->setEndpoint(_conf[sLastPath + "<endpoint>"]);  

        省略部分代碼
        ..........
     }
}

    (2)在創建adapter的時候,會把對應的servant名字和adapter名字進行映射,之後就可以根據名字找到關聯的servant或adapter:

void ServantHelperManager::setAdapterServant(const string &sAdapter, const string &sServant)                                   
{                                                                                                                              
    _adapter_servant[sAdapter] = sServant;                                                                                     
                                                                                                                               
    _servant_adapter[sServant] = sAdapter;                                                                                     
}            

          之後會設置每個adapter的handle線程組名和線程數,AdminAdapter已經由框架寫死用1個線程;而我們自己的業務adapter可              以自己配置成合適的線程數。

     (3)在創建完adapter後,用adapter的ip和端口進行socket創建,bind,listen。並把該adapter對象存放在網絡線程對象的_listeners

          成員中。(所有的adapter都是在第一個網絡網絡線程中進行bind,listen的)

int  TC_EpollServer::bind(TC_EpollServer::BindAdapterPtr &lsPtr)                                                               
{                                                                                                                              
    int iRet = 0;                                                                                                              
                                                                                                                               
    for(size_t i = 0; i < _netThreads.size(); ++i)                                                                             
    {                                                                                                                          
        if(i == 0)                                                                                                             
        {                                                                                                                      
            iRet = _netThreads[i]->bind(lsPtr);                                                                                
        }                                                                                                                      
        else                                                                                                                   
        {                                                                                                                      
            //當網絡線程中listeners沒有監聽socket時,list使用adapter中設置的最大連接數作爲初始化                               
            _netThreads[i]->setListSize(lsPtr->getMaxConns());                                                                 
        }                                                                                                                      
    }                                                                                                                          
                                                                                                                               
    return iRet;                                                                                                               
}       

int  TC_EpollServer::NetThread::bind(BindAdapterPtr &lsPtr)                                                                                                                                    
{                                                                                                                                                                                              
    for (const auto& kv : _listeners)                                                                                                                                                          
    {                                                                                                                                                                                          
        if(kv.second->getName() == lsPtr->getName())                                                                                                                                           
        {                                                                                                                                                                                      
            throw TC_Exception("bind name '" + lsPtr->getName() + "' conflicts.");                                                                                                             
        }                                                                                                                                                                                      
    }                                                                                                                                                                                          
                                                                                                                                                                                               
    const TC_Endpoint &ep = lsPtr->getEndpoint();                                                                                                                                              
                                                                                                                                                                                               
    TC_Socket& s = lsPtr->getSocket();                                                                                                                                                         
                                                                                                                                                                                               
    bind(ep, s);                                                                                                                                                                               
                                                                                                                                                                                               
    _listeners[s.getfd()] = lsPtr;                                                                                                                                                             
                                                                                                                                                                                               
    return s.getfd();                                                                                                                                                                          
}

void TC_EpollServer::NetThread::bind(const TC_Endpoint &ep, TC_Socket &s)
{
    int type = ep.isUnixLocal() ? AF_LOCAL : ep.isIPv6() ? AF_INET6 : AF_INET;

    if(ep.isTcp())
    {
        s.createSocket(SOCK_STREAM, type);
    }
    else
    {
        s.createSocket(SOCK_DGRAM, type);
    }

    if(ep.isUnixLocal())
    {
        s.bind(ep.getHost().c_str());
    }
    else
    {
        s.bind(ep.getHost(), ep.getPort());
    }

    if(ep.isTcp() && !ep.isUnixLocal())
    {
        s.listen(1024);
        s.setKeepAlive();
        s.setTcpNoDelay();
        //不要設置close wait否則http服務回包主動關閉連接會有問題
        s.setNoCloseWait();
    }

    s.setblock(false);
}
             

3.業務初始化

   (1)調用initialize(),讓業務層進行初始化,

   (2)業務層在initialize()裏一般需要調用addServant,將自己實現的servant註冊進框架裏。這裏其實只是在ServantHelperManager

            裏面存儲了該servant的生成方法,在後面需要該servant對象時可以動態創建。

template<typename T>                                                                                                       
void addServant(const string &id,bool check = false)
{                                                                                                                          
    if(check && _servant_adapter.end() == _servant_adapter.find(id))
    {
       cerr<<"[TARS]ServantHelperManager::addServant "<< id <<" not find adapter. 
       (maybe not conf in the web)"<<endl;
       throw runtime_error("[TARS]ServantHelperManager::addServant " + id + " not 
       find adapter.(maybe not conf in the web)");                                                                                                                            
    } 
                                                                                                                     
    _servant_creator[id] = new ServantCreation<T>();                                                                       
}                 

模板類SrevantCreation很簡單:

class ServantHelperCreation : public TC_HandleBase                                                                             
{                                                                                                                              
public:                                                                                                                        
    virtual ServantPtr create(const string &s) = 0;                                                                            
};                                                                                                                             
typedef TC_AutoPtr<ServantHelperCreation> ServantHelperCreationPtr;                                                            
                                                                                                                               
//////////////////////////////////////////////////////////////////////////////                                                 
/**                                                                                                                            
 * Servant                                                                                                                     
 */                                                                                                                            
template<class T>                                                                                                              
struct ServantCreation : public ServantHelperCreation                                                                          
{                                                                                                                              
    ServantPtr create(const string &s) { T *p = new T; p->setName(s); return p; }                                              
};                              

4.handle線程啓動

   (1)創建每個adapter的handle線程組

   (2)啓動所有的handle線程。

 (3)在啓動handle線程後,每個handle線程會根據該handle所處理的adapter名字獲取servant的生成器,創建對應的servant對象來處理

        網絡請求消息(所以,每一個handle線程都會有自己的一個獨立的servant對象)。

void ServantHandle::initialize()
{
    map<string, TC_EpollServer::BindAdapterPtr>::iterator adpit;

    map<string, TC_EpollServer::BindAdapterPtr>& adapters = _handleGroup->adapters;

    for (adpit = adapters.begin(); adpit != adapters.end(); ++adpit)
    {                                                                                                                          
        ServantPtr servant = ServantHelperManager::getInstance()->create(adpit->first);
                                                                                                                               
        if (servant)                                                                                                           
        {                                                                                                                      
            _servants[servant->getName()] = servant;
        }                                                                                                                      
        else                                                                                                                   
        {                                                                                                                      
            TLOGERROR("[TARS]ServantHandle initialize createServant ret null, for adapter `" + adpit->first + "`" << endl);
        }
    }

    省略部分代碼
    .........
}

        注意:如果 之前業務層沒有add相應的Servant,則在這一步會報錯誤:找不到對應servant。

    (4)接下來,進入handle線程的主邏輯:

       業務線程等在它所屬的handleGroup的monitor上:

            TC_ThreadLock::Lock lock(_handleGroup->monitor);                                                                                                                                   
                                                                                                                                                                                               
            if (allAdapterIsEmpty() && allFilterIsEmpty())                                                                                                                                     
            {                                                                                                                                                                                  
                _handleGroup->monitor.timedWait(_iWaitTime);                                                                                                                                   
            }              

      直到被喚醒,搶奪Adapter接收隊列上的請求包:        

while (adapter->waitForRecvQueue(recv, 0))                                                                                                                                     
{
      ............      
}

       注意到waitForRecvQueue的timeout是0,所以這裏實際上是非阻塞的。

       取出請求包後進行解析,如果用的是tars協議,則調用servant的對應的rpc接口進行處理;如果用的是非tars協議,則最終調用servant的doRequest()到達業務層。

void ServantHandle::handle(const TC_EpollServer::tagRecvData &stRecvData)                                                      
{                                                                                                                              
    TarsCurrentPtr current = createCurrent(stRecvData);                                                                        
                                                                                                                               
    if (!current) return;                                                                                                      
                                                                                                                               
    if (current->getBindAdapter()->isTarsProtocol())                                                                           
    {                                                                                                                          
        handleTarsProtocol(current);                                                                                           
    }                                                                                                                          
    else                                                                                                                       
    {                                                                                                                          
        handleNoTarsProtocol(current);                                                                                         
    }                                                                                                                          
}     

       

     (5)如果檢查到terminated,則handle線程結束

 

 5.epoll創建

   (1)每個網絡線程對象分別創建epoll

void TC_EpollServer::createEpoll()                                                                                             
{                                                                                                                              
    for(size_t i = 0; i < _netThreads.size(); ++i)                                                                             
    {                                                                                                                          
        _netThreads[i]->createEpoll(i+1);                                                                                      
    }                                                                                                                          
    //必須先等所有網絡線程調用createEpoll(),初始化list後,才能調用initUdp()                                                   
    for(size_t i = 0; i < _netThreads.size(); ++i)                                                                             
    {                                                                                                                          
        _netThreads[i]->initUdp();                                                                                             
    }                                                                                                                          
}                             

   (2)將adapter的socket fd加到第一個網絡線程對象的epoll中。另外每個網絡線程還有兩個socket fd:_shutdown,_notify。_shutdown是進程結束時,用來喚醒網絡線程的epoll.wait()用的;_notify是handle線程往發送隊列push消息後,通知網絡線程有發送消息用的。

還有一點,tars對epoll_event.data的用法,用低32位存儲要監聽的fd,高32位存儲監聽類型:

 enum                                                                                                                   
 {                                                                                                                      
     ET_LISTEN = 1,     //socket accept                                                                                                     
     ET_CLOSE  = 2,     //暫時沒用                                                                                                
     ET_NOTIFY = 3,     //handle線程通知網絡線程                                                                                                
     ET_NET    = 0,     //socket read,write                                                                                                        
 };             
void TC_EpollServer::NetThread::createEpoll(uint32_t iIndex)
{
    if(!_createEpoll)
    {
        _createEpoll = true;

        // 創建本網絡線程的內存池
        assert (!_bufferPool);
        _bufferPool = new TC_BufferPool(_poolMinBlockSize, _poolMaxBlockSize);
        _bufferPool->SetMaxBytes(_poolMaxBytes);

        //創建epoll
        _epoller.create(10240);

        _epoller.add(_shutdown.getfd(), H64(ET_CLOSE), EPOLLIN);
        _epoller.add(_notify.getfd(), H64(ET_NOTIFY), EPOLLIN);

        size_t maxAllConn   = 0;

        //監聽socket
        for (const auto& kv : _listeners)
        {                                                                                                                      
            if(kv.second->getEndpoint().isTcp())
            {                                                                                                                  
                //獲取最大連接數                                                                                               
                maxAllConn += kv.second->getMaxConns();
                                                                                                                               
                _epoller.add(kv.first, H64(ET_LISTEN) | kv.first, EPOLLIN);
            }                                                                                                                  
            else
            {                                                                                                                  
                maxAllConn++;                                                                                                  
                _hasUdp = true;
            }
        }
        
        省略部分代碼
         .........
    }
}

 

到這裏,application.main()結束,進入:

application.waitForShutdown()

 6.網絡線程啓動

    (1)網絡線程等待在epoll上,開始處理網絡請求:         

void TC_EpollServer::NetThread::run()
{
    //循環監聽網路連接請求
    while(!_bTerminate)
    {
        _list.checkTimeout(TNOW);

        int iEvNum = _epoller.wait(2000);

        for(int i = 0; i < iEvNum; ++i)
        {
            try
            {
                const epoll_event &ev = _epoller.get(i);

                uint32_t h = ev.data.u64 >> 32;

                switch(h)
                {                                                                                                              
                case ET_LISTEN:                                                                                                
                    {                                                                                                          
                        //監聽端口有請求                                                                                       
                        auto it = _listeners.find(ev.data.u32);
                        if( it != _listeners.end())                                                                            
                        {                                                                                                      
                            if(ev.events & EPOLLIN)                                                                            
                            {                                                                                                  
                                bool ret;
                                do                                                                                             
                                {                                                                                              
                                    ret = accept(ev.data.u32, it->second->_ep.isIPv6() ? AF_INET6 : AF_INET);
                                }while(ret);
                            }
                        }
                    }            
                   break;
                case ET_CLOSE:
                    //關閉請求
                    break;
                case ET_NOTIFY:
                    //發送通知
                    processPipe();
                    break;
                case ET_NET:
                    //網絡請求
                    processNet(ev);
                    break;
                default:
                    assert(true);
                }
            }
            catch(exception &ex)
            {
                error("run exception:" + string(ex.what()));
            }
        }
    }
}

      (2)如果檢查到terminated,則網絡線程結束。

 

 7.主線程等待在_epollServer的條件變量上

     當服務被設置爲terminate(例如ctrl+c或者web上關掉該服務),則主線程準備結束,同時通知其他線程服務狀態已

     經爲terminate,可以結束了。之後主線程調用destroyApp(),讓業務層在進程結束前做清理工作。

 

到這裏,waitForShutdown()結束,返回:

main()

   8.waitForShutdown()返回後,直接調用return語句結束進程。

 

 

 

 

 

 

 

 

 

 

 

 

 

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