工作中需要做一个服务器,要处理成千上万的连接,决定采用ACE的Proactor模型(即Windows下的IOCP)服务器的业务是接受客户端的连接,简单交互后,收到一个数据包,对该数据包进行一些处理后,返回给客户端,断开。数据包的处理可能会比较耗时,包括加解密运算,可能还要查询数据库等用ACE Proactor时,现在有两种选择(方案):
第一种:从ACE_Service_Handler派生一个类,在这个类中进行网络收发,并进行运算处理(在handle_read_stream中收到后处理)。也就是说所有的工作都在一个线程中完成,整个服务器基本只有这一个线程。
第二种:从ACE_Service_Handler派生的类中只做网络收发,把收到的数据包放入一个队列,让另一个线程去循环处理,处理后的结果再通过网络发出去。也就是说基本是两个线程,再加一个队列。
第一种做法因为是单线程,基本不需要考虑任何线程同步的问题,实现会比较简单。
第二种做法可能结构会比较好,但实现上麻烦一些,需要考虑两个线程间的同步,并要在队列的数据包中保存一个连接句柄,用于处理完后的发送,还要考虑数据包处理完后它的所属连接可能已经关闭的问题。 我个人认为ACE的Proactor(即IOCP)本身是异步IO模型,它设计的目的就是为了减少多线程所带来的复杂性,避免CPU线程同步而造成的性能退化。个人认为两个线程一个队列的方法,对于效率的提高没有帮助。
因此我比较倾向于第一种方案,而有同事则认为应该用第二种方案。 请有相关ACE服务器设计经验的朋友谈谈看法,谢谢。
第一种:从ACE_Service_Handler派生一个类,在这个类中进行网络收发,并进行运算处理(在handle_read_stream中收到后处理)。也就是说所有的工作都在一个线程中完成,整个服务器基本只有这一个线程。
第二种:从ACE_Service_Handler派生的类中只做网络收发,把收到的数据包放入一个队列,让另一个线程去循环处理,处理后的结果再通过网络发出去。也就是说基本是两个线程,再加一个队列。
第一种做法因为是单线程,基本不需要考虑任何线程同步的问题,实现会比较简单。
第二种做法可能结构会比较好,但实现上麻烦一些,需要考虑两个线程间的同步,并要在队列的数据包中保存一个连接句柄,用于处理完后的发送,还要考虑数据包处理完后它的所属连接可能已经关闭的问题。 我个人认为ACE的Proactor(即IOCP)本身是异步IO模型,它设计的目的就是为了减少多线程所带来的复杂性,避免CPU线程同步而造成的性能退化。个人认为两个线程一个队列的方法,对于效率的提高没有帮助。
因此我比较倾向于第一种方案,而有同事则认为应该用第二种方案。 请有相关ACE服务器设计经验的朋友谈谈看法,谢谢。
国内专业的ACE网络编程、开发论坛开通:
www.acejoy.com
www.acedevelop.com
涉及ACE使用和开发,中间件、服务器端软件的设计,P2P技术,
socket网络编程、应用开发等内容。
欢迎加入,大家一起交流、学习成长!
你都说到了,你的数据包处理比较耗时,
我对完成端口应该开多少工作者线程的理解在你这个例子中就是,
你处理数据包需要的时间/网络收发的时间=(你要建立的工作者线程数目-1)/主线程数目(就是1)=你要建立的工作者线程数目-1。比如说你的服务器不需要进行任何处理,那么就直接开一个主线程就够了,如果处理数据包时间片基本和处理连接时间相同,只要开两个工作者线程就可以了,这两个线程可以跑同样的线程函数,这样的话,你的服务器可以一直不断的接受连接,两个线程函数轮流值班执行数据包处理,就是说任何时候到来的连接,都可以找到相应的处理线程处理。
如果你的数据包处理过程明显比较耗时,就应该考虑多开几个,因为你的主线程每接受一个连接的话,可能因为开的线程数不够不能即时找到相应的处理线程,这样,CPU使用效率明显下降。
注意,上面说的这些都不包括I/O操作,I/O处理时间片是不用考虑的,因为它根本不用你的程序来处理
好像大多赞成第二种方案啊,第二种方案确实结构比较清晰,网络处理和数据处理分离提出一些不同意见啊我觉得把网络I/O用单独的线程来处理的话(即第二种方案),如果单个数据包处理的时间比较长,就会出现网络I/O很快,CPU处理较慢,即大量的任务会堆积在队列中等待处理。这样总的吞吐量其实是一样的。如楼上所说,IOCP异步模式下,可以认为CPU在网络I/O上是不花时间的,所以服务器CPU的全部时间都花在数据处理运算上。所以我觉得在服务器性能、数据吞吐率等指标上,两种方案应该是一样的,第一种不会比第二种低。楼上acejoy说第二种方案“可以避免数据处理占用了I/O处理的时间”,应该是指单线程中CPU的计算会阻碍对网络I/O的及时响应吧,确实会阻碍,不过我觉得即使不阻碍,CPU也来不及处理不断到来的数据包,CPU的计算能力是一定的。也许有人会说这样可以把数据放在队列中可以缓存起来,弹性更好,可以在瞬间接受大量的连接,但是我觉得这个弹性可以通过ACE_Asynch_Acceptor的open函数的backlog参数(内部是通过多次的AcceptEx来实现的)来解决。两种方法都提供了对连接的缓存,一种是自己把数据先收下来缓存在内存中,另一种是靠操作系统本身提供的机制来缓存。第一种方案的缺点应该在结构上,不便于以后可能把数据计算任务交给另外一台计算机来处理(即分布式的处理)不同意见欢迎讨论
另外IOCP本身就可以指定超过一个线程来处理IO Request的。
-----
是可以开多个处理线程,最好是cpu个数或者乘以2吧。这可以认为还是第一种方案,因为没有队列,基本不需要线程同步。
另外,如果数据处理时间太长,会导致网络这块性能低下,看看ace书,上面对数据处理时间和网络消息频率有限制。