高性能 Socket 组件 HP-Socket v3.2.1-RC1 发布
http://bbs.csdn.net/topics/390763397
The C10K problem 高性能服务器设计
http://www.cnblogs.com/fll/archive/2008/05/17/1201540.html
http://blog.sina.com.cn/s/blog_4aec22920100itkf.html
这周看了Dan Kegel那篇"The C10K problem",以下和大家分享一下。 故名思义,这文章是分析如果编写一个服务器程序来支持上万的客户端连接的。 其关注的重点是io效率,目前大规模网络程序的关键瓶颈。 作者认为需要权衡利弊的要点有以下 1.单线/进程处理多个I/O的做法: 其可选的方案有
a. 不这样做。 也就是对单线程使用阻塞同步的IO(意为依靠多线/进程来处理多个IO)。 b. 使用非阻塞IO。 即以非阻塞方式启动IO,并等待IO就绪通知(select / poll等,通常对网络IO有效,但不能提高磁盘IO的效率) c. 使用异步IO。 调用 aio_write启动IO,并等待IO的完成通知。 2.如果管理client连接 a. 一个进程处理一个client (经典unix方式) b. 一个OS-level线程处理多个client, 对应每个client使用一个user-level线程(即线程库,虚拟机提供的线程,协同等方式) c. 一个OS-level线程处理一个client (java的内置线程等) d. 一个OS-level线程处理一个活跃的client 3. 是否使用标准OS服务,还是把依靠具体OS内核 以上的选择互相组合,产生一些常见解决方案. 1. 单线程处理多client,使用非阻塞IO 和水平触发的就绪通知 **水平触发(level-trigger)与边界触发(edge-trigger)对应,简要说水平触发意为根据状态 也判断是否触发某事件, 而边界触发根据变化来判断是否触发某事件。 也就是传统的io多路复用,这是目前大部分网络程序的解决方式,它可以方便的在一个线程里管理多个 io。不需要考虑多线/程等问题, 代码的逻辑不需要加入额外的复杂性。 但这里所指的非阻塞,是指在某个fd未就绪时的read或write操作, 程序不等待它的就绪而已。 这边存在上面说对于磁盘IO并没有提高效率的问题。 在大规模地对磁盘读写操作时, read 或write还是会导致整个程序长时间阻塞。 如果要避免这种情况,必须引入异步IO。 对于部分缺乏AIO的系统,就只能建立子线/进程来完成该操作。 另外一种对磁盘IO的优化方式是使用 内存映像IO。 这种方式的关键是判断一组非阻塞的socket何时处于就绪状态。 实现它们的方法有 select, poll, dev/poll 等。 2. 单线程处理多client,使用非阻塞IO 和就绪变化通知 意为使用边界触发的就绪通知。其实这种方案只是1的加速方式, 即只在fd状况发生变化时 才去检查fd是否变为就绪。这种实现必须去处理假事件,因为有些实现方式是fd接到任何包的时候都假定其变为就绪了。 实现它们的有 kqueue, epoll(指定ET模式)等。 一些通用的库如libevent ACE等,把这些底层不同实现封装起来,并提供不同IO策略的选择。 按照 libevent作者Niels的测试数据 kqueue在所有之中性能最高。 3. 多个线程处理多个client, 使用异步IO 这是对于网络IO和磁盘IO都很高效的方式。通常当异步IO发起后, 它使用边界触发的方式来提供IO的完成通知。 然后把完成的通知放到消息队列里,等待程序的后续相应。 但这需要os提供AIO的支持,并且需要以异步的方式来设计程序,有一定复杂性。 4. 多个线程处理client 这是最简单方式,就是让但进/线程 的read 和 write 直接阻塞。 这个方法的最大缺点是, 需要支付每个进/线程的栈空间,并引入额外的上下文切换的开销。 如果需要上万个client, 建立上万的进/线程目前是不太可能的。 5. 把服务器整合到os内核里 部分系统有这样的尝试,例如Liunx下的 khttpd 就是把web服务器整合到 系统内核里。 但这对我们的游戏参考价值不大。