優秀的輕量級網路開發架構spserver源碼分析(二)

來源:互聯網
上載者:User

1。分析socket讀寫流程

void SP_EventCallback :: onRead( int fd, short events, void * arg )
{
    SP_Session * session = (SP_Session*)arg;

    session->setReading( 0 );

    SP_Sid_t sid = session->getSid();

    if( EV_READ & events ) {
        int len = session->getIOChannel()->receive( session );

        if( len > 0 ) {
            session->addRead( len );
            if( 0 == session->getRunning() ) {
                SP_MsgDecoder * decoder = session->getRequest()->getMsgDecoder();
                if( SP_MsgDecoder::eOK == decoder->decode( session->getInBuffer() ) ) {
                    SP_EventHelper::doWork( session );
                }
            }
            addEvent( session, EV_READ, -1 );
        } else {
            int saved = errno;

            if( 0 != errno ) {
                sp_syslog( LOG_WARNING, "session(%d.%d) read error, errno %d, status %d",
                        sid.mKey, sid.mSeq, errno, session->getStatus() );
            }

            if( EAGAIN != saved ) {
                if( 0 == session->getRunning() ) {
                    SP_EventHelper::doError( session );
                } else {
                    sp_syslog( LOG_NOTICE, "session(%d.%d) busy, process session error later",
                            sid.mKey, sid.mSeq );
                    // If this session is running, then onResponse will add write event for this session.
                    // It will be processed as write fail at the last. So no need to re-add event here.
                }
            } else {
                addEvent( session, EV_READ, -1 );
            }
        }
    } else {
        if( 0 == session->getRunning() ) {
            SP_EventHelper::doTimeout( session );
        } else {
            sp_syslog( LOG_NOTICE, "session(%d.%d) busy, process session timeout later",
                    sid.mKey, sid.mSeq );
            // If this session is running, then onResponse will add write event for this session.
            // It will be processed as write fail at the last. So no need to re-add event here.
        }
    }
}

拿到讀事件之後。int len = session->getIOChannel()->receive( session ); 通過IOChannel到底層 socket中讀取資料,此時的IOChannel實際上轉入SP_DefaultIOChannel的receive方法,該方法執行libevent中的evbuffer_read方法讀資料。

注意此時讀資料是由onRead觸發的,所以不會阻塞,讀完資料後,執行SP_MsgDecoder::eOK == decoder->decode( session->getInBuffer(),此時會轉入到協議處理流程,由協議解碼器去解碼緩衝區,得到相關協議。我們來看一個典型的解碼器寫法

int SP_LineMsgDecoder :: decode( SP_Buffer * inBuffer )
{
    if( NULL != mLine ) free( mLine );
    mLine = inBuffer->getLine();

    return NULL == mLine ? eMoreData : eOK;
}

 

底層緩衝區是inBuffer,從底層緩衝讀一個完整的行,讀到之後就將ret=eOK.

SP_EventHelper::doWork( session ); 回去找協議處理器去執行。我們看一個典型的代碼。

class SP_EchoHandler : public SP_Handler {
public:
    SP_EchoHandler(){}
    virtual ~SP_EchoHandler(){}

    // return -1 : terminate session, 0 : continue
    virtual int start( SP_Request * request, SP_Response * response ) {
        request->setMsgDecoder( new SP_MultiLineMsgDecoder() );
        response->getReply()->getMsg()->append(
            "Welcome to line echo server, enter 'quit' to quit./r/n" );

        return 0;
    }

    // return -1 : terminate session, 0 : continue
    virtual int handle( SP_Request * request, SP_Response * response ) {
        SP_MultiLineMsgDecoder * decoder = (SP_MultiLineMsgDecoder*)request->getMsgDecoder();
        SP_CircleQueue * queue = decoder->getQueue();

        int ret = 0;
        for( ; NULL != queue->top(); ) {
            char * line = (char*)queue->pop();

            if( 0 != strcasecmp( line, "quit" ) ) {
                response->getReply()->getMsg()->append( line );
                response->getReply()->getMsg()->append( "/r/n" );
            } else {
                response->getReply()->getMsg()->append( "Byebye/r/n" );
                ret = -1;
            }

            free( line );
        }

        return ret;
    }

    virtual void error( SP_Response * response ) {}

    virtual void timeout( SP_Response * response ) {}

    virtual void close() {}
};

這是 testecho伺服器的代碼,SP_EventHelper::doWork( session ); 最終會定位到virtual int handle( SP_Request * request, SP_Response * response )來執行

具體執行過程如下

void SP_EventHelper :: doWork( SP_Session * session )
{
    if( SP_Session::eNormal == session->getStatus() ) {
        session->setRunning( 1 );
        SP_EventArg * eventArg = (SP_EventArg*)session->getArg();
        eventArg->getInputResultQueue()->push( new SP_SimpleTask( worker, session, 1 ) );
    } else {
        SP_Sid_t sid = session->getSid();

        char buffer[ 16 ] = { 0 };
        session->getInBuffer()->take( buffer, sizeof( buffer ) );
        sp_syslog( LOG_WARNING, "session(%d.%d) status is %d, ignore [%s...] (%dB)",
            sid.mKey, sid.mSeq, session->getStatus(), buffer, session->getInBuffer()->getSize() );
        session->getInBuffer()->reset();
    }
}

SP_EventHelper::doWork( session )  轉到 eventArg->getInputResultQueue()->push( new SP_SimpleTask( worker, session, 1 ) ); 執行

eventArg內部包含InputResultQueue隊列,我們回到int SP_Server :: start()

int SP_Server :: start()
{
#ifdef SIGPIPE
    /* Don't die with SIGPIPE on remote read shutdown. That's dumb. */
    signal( SIGPIPE, SIG_IGN );
#endif

    int ret = 0;
    int listenFD = -1;

    ret = SP_IOUtils::tcpListen( mBindIP, mPort, &listenFD, 0 );

    if( 0 == ret ) {

        SP_EventArg eventArg( mTimeout );

        // Clean close on SIGINT or SIGTERM.
        struct event evSigInt, evSigTerm;
        signal_set( &evSigInt, SIGINT,  sigHandler, this );
        event_base_set( eventArg.getEventBase(), &evSigInt );
        signal_add( &evSigInt, NULL);
        signal_set( &evSigTerm, SIGTERM, sigHandler, this );
        event_base_set( eventArg.getEventBase(), &evSigTerm );
        signal_add( &evSigTerm, NULL);

        SP_AcceptArg_t acceptArg;
        memset( &acceptArg, 0, sizeof( SP_AcceptArg_t ) );

        if( NULL == mIOChannelFactory ) {
            mIOChannelFactory = new SP_DefaultIOChannelFactory();
        }
        acceptArg.mEventArg = &eventArg;
        acceptArg.mHandlerFactory = mHandlerFactory;
        acceptArg.mIOChannelFactory = mIOChannelFactory;
        acceptArg.mReqQueueSize = mReqQueueSize;
        acceptArg.mMaxConnections = mMaxConnections;
        acceptArg.mRefusedMsg = mRefusedMsg;

        struct event evAccept;
        event_set( &evAccept, listenFD, EV_READ|EV_PERSIST,
                SP_EventCallback::onAccept, &acceptArg );
        event_base_set( eventArg.getEventBase(), &evAccept );
        event_add( &evAccept, NULL );

        SP_Executor workerExecutor( mMaxThreads, "work" );
        SP_Executor actExecutor( 1, "act" );
        SP_CompletionHandler * completionHandler = mHandlerFactory->createCompletionHandler();

        /* Start the event loop. */
        while( 0 == mIsShutdown ) {
            event_base_loop( eventArg.getEventBase(), EVLOOP_ONCE );

            for( ; NULL != eventArg.getInputResultQueue()->top(); ) {
                SP_Task * task = (SP_Task*)eventArg.getInputResultQueue()->pop();
                workerExecutor.execute( task );
            }

           for( ; NULL != eventArg.getOutputResultQueue()->top(); ) {
                SP_Message * msg = (SP_Message*)eventArg.getOutputResultQueue()->pop();

                void ** arg = ( void** )malloc( sizeof( void * ) * 2 );
                arg[ 0 ] = (void*)completionHandler;
                arg[ 1 ] = (void*)msg;

                actExecutor.execute( outputCompleted, arg );
            }
        }

        delete completionHandler;

……   

}

SP_Executor workerExecutor( mMaxThreads, "work" );  SP_Executor actExecutor( 1, "act" );是兩個重要的執行線程池。

在                 for( ; NULL != eventArg.getInputResultQueue()->top(); ) {
                SP_Task * task = (SP_Task*)eventArg.getInputResultQueue()->pop();
                workerExecutor.execute( task );
            }

迴圈中, eventArg.getInputResultQueue將任務出隊,然後使用workerExecutor.execute( task );執行任務,而task封裝了virtual int handle( SP_Request * request, SP_Response * response )

總結:到此為止,我們分析清楚,用戶端資料到達socket,觸發onRead事件,調用decoder判斷是否協議解碼成功(eOK),解碼成功則將handel方法封裝到Task,送入InputResultQueue隊列,由workerExecutor線程池出隊並執行。
 

資料寫出與這個過程類似。

(未完待續……)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.