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语句结束进程。

 

 

 

 

 

 

 

 

 

 

 

 

 

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