一个典型的游戏服务器设计中,一般都是用的多线程,服务器中一般运行两类线程,N个SOCKET IO线程,1个逻辑线程, IO线程接受客户端发来的信息,通过消息队列发送给逻辑线程处理后,再发送消息给客户端,发送消息这里一般是IO线程处理实际发送。
其实我认为,如果逻辑线程都是消耗的CPU运算资源的话,服务器完全采用单线程的方式来做。
首先,我们看IO处理,基本就是数据入队、出队,send、recv操作,作为服务器的SOCKET处理一般都是异步SOCKET,也就是说,send、recv操作只是将信息copy到socket底层的发送接收缓冲区去了,不存在IO堵塞的问题。
然后,我们再来看逻辑处理,前面已经说了,采用单线程的前提是逻辑处理只是消耗CPU运算资源,那么,不管你开几个线程,对单核的CPU来说,它的处理速度就是这么多,并不会因为你线程开的越多,就处理的越快。
因此我们可不可以这样说呢,在单核机器上,只消耗CPU运算的服务,多线程并不比单线程能提高多少效率。
接下来,我们再讨论下多核的情况,你肯定要想,我这台服务器是4个双核CPU,就只跑一个单线程的服务器不是亏死了,多线程多好,我开8个线程,就能很好的利用我的机器啦。是啊,我也觉得这样很好,不过在LINUX、UNIX下,对线程的支持并不像WINDOWS下那么好,LINUX、UNIX下一般都是用LWP(轻量级进程)的方式来支持多线程程序的,Linux内核只提供了轻量进程的支持,限制了更高效的线程模型的实现,但Linux着重优化了进程的调度开销,一定程度上也弥补了这一缺陷。同时,滥用多线程也会造成不必要的上下文切换,不必要的同步机制的引入(如pthread_mutex),让程序频繁的在内核和用户间频繁切换。另外,从开发角度来看,单线程开发比多线程环境开发更不容易出错和更加健壮。
在游戏服务器架构中,为了提高玩家在线人数,实现负载均衡,现在一般都是采用分布式的多进程服务器集群的方式,我们来看看服务器集群中,每个服务进程是采用多线程的方式还是单线程的方式好呢?我觉得,对于有慢速IO访问的需求的应用进程,多线程肯定比单线程好,最典型的情况就是数据库访问这块,完全可以采用N个DB线程,一个逻辑线程的架构,而对只是消耗CPU运算资源的应用进程,尽量单线程就行了,如果觉得单线程负载不行的话,完全可以分成多个进程来跑。。
以上只是我自己的一些看法,表达有限,欢迎指正。。。
对于只有游戏逻辑和网络IO的进程而言,你说的只开一个线程,似乎也在理。不过由于网络IO这块情况可能比理论上要复杂很多,例如实际使用的网络IO机制(IOCP)、网络层数据的拷贝、封包组建等,似乎保险的做法还是开多个线程来做。何况,逻辑线程可能还会涉及到限帧问题。拿去运营的服务器一般也是多核的。LINUX下线程实现的效率如果真的太那个啥,或者可以考虑多进程的结构(网络模块和逻辑模块位于不同进程)。
游戏服务器要提高负载,都是集群的方式,一般都有N个网关的,不管你用IOCP,EPOLL还是KQUEUE,甚至是SELECT都可以的,而网关的功能很简单的,就是外网和内网之间的信息转发。因此一个线程就够了。
对于游戏服务器组件之间的通讯(IPC)来说,就那么几个连接,SOCKET的上下文切换的消耗是很小的。
另外,IOCP,EPOLL,KQUEUE这种机制,只有在服务器接受了N个客户端,但是服务器只和其中的很少一部分客户端在交互的情况下(很少的客户端在并发接收和发送)才能体现出它们的优点。 而对于游戏服务器来说,你有1000个客户端在线,这些客户端基本都在同时发包,可读可写事件每个FD都差不多同时产生,这种情况下,EPOLL还是SELECT,效率上来看差别不大。因此,对于需要处理大量高并发的长连接请求的服务器来说,多进程的方式要轻松的多。