服务器开发之 IO 处理

来源:互联网 发布:淘宝上的日系店铺 编辑:程序博客网 时间:2024/05/28 20:19
  服务器的主要事务是处理各种逻辑,为上千上万的用户提供特定的服务。所以,对它的要有能够应对高并发的特点,同时还要有高的响应处理能力,稳定性更是不用多说,这是第一位的,没有玩家希望自己购买的道具丢失。
              下面花一个简单的服务器进程的模型图,说明服务器进程的基本事务结构,其实就是和一些他要处理的 IO 的关系。



              上面的图中,服务器进程中一共接入了三个IO处理模块,其中两个是网络IO,一个是数据库IO, 还有一中可能就是服务器还会接入本地文件IO, 写log,加载配置等操作。 但基本就是些IO类型。
              简单说明一下:
              IO(A)负责处理客户端的接入。
              IO(B)负责和别的服务器进行通信,交换数据。
              IO(C)负责加载和保存数据。

             下面说说这些IO特点,这些io的共同特点都是比较慢的,并且他们的操作都是会阻塞的,最快的也应该是0.001秒的数量级别,这也许在我们看来是很快了,如果每个处理都是0.001秒,那么一秒钟也就处理1000个,这个速度对服务器来说还是太慢,所以我们要这些io的操作放到独立的工作线程里面去处理他们的io读和写操作,逻辑放入到主逻辑线程D里面去操作。因为逻辑需要牵涉到方方面面的数据和操作,如果直接在线程里面处理是需要很多的线程安全机制来保证安全,这样会给逻辑开发带来工作量,也给调试带来困难。这就是我想说的,我们应该如何处理这些io数据。这个时候你心里肯定也知道,我弄个线程安全的队列来交换数据。呵呵,是的你想的很多,大的理论就是这样干的,我想说一些细节上面的东西。



         struct  TDataBlock
         {
                    index;                                //数据库编号
                    enter_time;                       //进入队列的时间
                    live_maxtime;                    //最多生命时间
                  
                    //
                    yourdatabufer;                 //你自己的数据信息
          }

数据处理的大概流程,



        这里的核心部件是A,B,他们是线程安全的队列。这样可以保障两个线程之间的同步关系,同时保证数据的安全。下面是其实现的大概代码,删除了一些细节上的实现。


//一个现场安全的队列template<typename A>class XThreadSaveQueue{    typedef std::list<A> IN_QUEUE;public:    XThreadSaveQueue(){    }    ~XThreadSaveQueue(){}    void push(const A _Val)    {        CXXLocker lock();        m_q.push_back( _Val );    }    void push_front(const A _Val)    {        CXXLocker lock();        m_q.push_front( _Val );    }    A pop( bool &isok )    {        CXXLocker lock();        isok = false;        if( m_q.empty() )            return A();        isok = true;        A item = m_q.front();        m_q.pop_front();        return item;    }    A& front()    {        CXXLocker lock();        return m_q.front();    }    unsigned int size()    {        CXXLocker lock();        return m_q.size();    }    bool empty()    {        CXXLocker lock();        return m_q.empty();    }private:    IN_QUEUE  m_q;};typedef XThreadSaveQueue<TDataBlock*> QueueData;class CYourWorkThread:public CThread{public:    CYourWorkThread();    virtual ~CYourWorkThread(void);public:    void  push_req( TDataBlock *pData ){ m_req_queue.push(pData); }    //这个函数工作在主逻辑线程里面    void  app_run()    {        //处理rep返回的数据    }protected:    //这个函数工作在子线程里面    void  ThreadRun(void)    {        //处理req的请求队列    }private:    QueueData    m_req_queue;    //我自己需要处理的请求队列    QueueData    m_rep_queue;    //我自己需要处理的请求队列};



注意:
        1: 必须注意 app_run(), ThreadRun(),这个两个函数里面的异常处理,保证队列的正常进入和删除,否则会有内存泄漏,或者造成线程死循环,永远在处理一个数据块,而这个数据块也一直在发生异常。
       2:
ThreadRun(void)上面的函数如果是子线程可以直接完成的工作,比如本地文件,数据库等操作,也就是说是同步完成的,是不要什么额外的处理工作的。 如果是异步的处理方式,比如网络。你需要添加一个map来保存正在处理的数据块,并且定时的检查他们得儿完成情况,并且做容错处理,这样一方面保证内存的安全,同时也保证客户端不会死等在一个界面里面。同时也降低客户端的冗余工作量。当然了一个好的客户端还是要有超时处理的。
0 0