服务器的主要事务是处理各种逻辑,为上千上万的用户提供特定的服务。所以,对它的要有能够应对高并发的特点,同时还要有高的响应处理能力,稳定性更是不用多说,这是第一位的,没有玩家希望自己购买的道具丢失。
下面花一个简单的服务器进程的模型图,说明服务器进程的基本事务结构,其实就是和一些他要处理的 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; //你自己的数据信息
}
数据处理的大概流程,
注意:
1: 必须注意 app_run(), ThreadRun(),这个两个函数里面的异常处理,保证队列的正常进入和删除,否则会有内存泄漏,或者造成线程死循环,永远在处理一个数据块,而这个数据块也一直在发生异常。
2: ThreadRun(void)上面的函数如果是子线程可以直接完成的工作,比如本地文件,数据库等操作,也就是说是同步完成的,是不要什么额外的处理工作的。 如果是异步的处理方式,比如网络。你需要添加一个map来保存正在处理的数据块,并且定时的检查他们得儿完成情况,并且做容错处理,这样一方面保证内存的安全,同时也保证客户端不会死等在一个界面里面。同时也降低客户端的冗余工作量。当然了一个好的客户端还是要有超时处理的。
下面花一个简单的服务器进程的模型图,说明服务器进程的基本事务结构,其实就是和一些他要处理的 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; //你自己的数据信息
}
数据处理的大概流程,
//一个现场安全的队列
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来保存正在处理的数据块,并且定时的检查他们得儿完成情况,并且做容错处理,这样一方面保证内存的安全,同时也保证客户端不会死等在一个界面里面。同时也降低客户端的冗余工作量。当然了一个好的客户端还是要有超时处理的。