如何处理10000 TCP连接 已翻译 100%

sin7777 投递于 2013/06/02 21:30 (共 35 段, 翻译完成于 10-07)
阅读 28118
收藏 95
13
加载中

是时候让 Web 服务器同时处理一万客户端了,你不觉得吗?毕竟,现在的 Web 是一个大地盘了。

并且,计算机也是一样的大。 你可以花 $1200 左右购买一台 1000MHz,2Gb RAM 和一块 1000Mbit/s 以太网卡的机器。我们来看看——在 20000 客户端(是 50KHz,100Kb 和 50Kb/s/客户端)时,它不采取任何更多的马力而是采用 4Kb 的硬盘和为2万客户端中的每个一秒一次的发送它们到网络。(顺便说一句,这是$0.08 每客户端。 那些按 $100/客户端 许可费来收取费用的一些操作系统开始看起来有点沉重!)。所以硬件不再是瓶颈了。

1999年最繁忙的FTP网站: cdrom.com,实际上通过一个千兆的以太网管道同时地处理了 10000 客户端。截至 2001年,同样的速度现在由多个ISP提供,期望它变得越来越受大型商业客户的欢迎。

一碗老酸菜
一碗老酸菜
翻译于 2013/06/03 11:00
3

瘦客户端计算模式显现出回来的风格——这一时刻,在互联网上的服务器正在为数千计的客户端服务。

考虑到这一点,这里有几个关于如何配置操作系统和编写代码以支持数千客户端的注意事项。讨论的中心是围绕类 Unix 操作系统的。因为这是我个人感兴趣的领域。但Windows也包括了一点。

目录

一碗老酸菜
一碗老酸菜
翻译于 2013/06/03 12:39
2

相关站点

阅读Nick Black写的超级棒的 Fast UNIX Servers 文章.
2003年十月, Felix von Leitner 做了一个超级好的网页,展示了网络的可扩展性,他完成了在各种不同的网络系统请求和操作系统下的benchmark比较。他的一个实验观察结果就是linux2.6的内核确实比2.4的要好,但还有有很多很多好的图表数据可以引起OS开发者的深思(如有兴趣可以看看 Slashdot 的评论;是否真的有遵循Felix的实验结果对benchm的提高进行跟踪的)

提前阅读

如果你还没有读过上述,那么先出去买一本W. Richard Stevens写的 Unix Network Programming : Networking Apis: Sockets and Xti (Volume 1)  . 这本书描述了很多编写高性能服务器的I/O策略和误区,甚至还讲解了关于 'thundering herd' 问题。惊群问题
如果你读过了,那么请读这本 Jeff Darcy's notes on high-performance server design.(Cal Henderson写的,对更加倾向于使用一款web 服务器而非开发一款服务器 来构建可扩展web站点的同志,这本书更加有用.)

qingfeng哥
qingfeng哥
翻译于 2013/06/03 13:33
2

I/O 框架

以下所列的为几个包装好的库,它们抽象出了一些下面所表达的技术,并且可以使你的代码与具体操作系统隔离,从而具有更好的移植性。
·      ACE, 一个重量级的C++ I/O框架,用面向对象实现了一些I/O策略和其它有用的东西,特别的,它的Reactor框架是用OO方式处理非阻塞I/O,而Proactor框架是用OO方式处理异步I/O的。
·      ASIO 一个C++的I/O框架,正在成为Boost库的一部分。它像是ACE过渡到STL时代。(译注:ACE内部有自己的容器实现,它和C++ 标准库中的容器是不兼容的。)
·      libevent 由Niels Provos用C 语言编写的一个轻量级的I/O框架。它支持kqueue和select,并且很快就可以支持poll和epoll(翻译此文时已经支持)。我想它应该是只采用了水平触发机制,该机制功过参半。Niels给出了 一张图 来说明时间和连接数目在处理一个事件上的功能,从图上可以看出kqueue和sys_epoll明显胜出。
·     我本人也尝试过写一个轻量级的框架(很可惜没有维持至今):
          o      Poller 是一个轻量级的C++ I/O框架,它使用任何一种准备就绪API(poll, select, /dev/poll, kqueue, sigio)实现水平触发准备就绪API。以其它 不同的API为基准,Poller的性能好得多。该链接文档的下面一部分说明了如何使用这些准备就绪API。
          o      rn 是一个轻量级的C I/O框架,也是我继Poller后的第二个框架。该框架可以很容易的被用于商业应用中,也容易的适用于非C++应用中。它如今已经在几个商业产品中使用。
·     2000年4月,Matt Welsh就构建服务器如何平衡工作线程和事件驱动技术的使用方面写了一篇 论文,在该论文中描述了他自己的Sandstorm I/O框架。
·      Cory Nelson's Scale! library - 一个Windows下的异步套接字,文件和管道的I/O库。
qingfeng哥
qingfeng哥
翻译于 2013/06/03 14:30
2

I/O 策略

网络软件设计者往往有很多种选择,以下列出一些:
  • 是否处理多个I/O?如何处理在单一线程中的多个I/O调用?
    • 不处理,从头到尾使用阻塞和同步I/O调用,可以使用多线程或多进程来达到并发效果。
    • 使用非阻塞调用(如在一个设置O_NONBLOCK选项的socket上使用write)读取I/O,当I/O完成时发出通知(如poll,/dev/poll)从而开始下一个I/O。这种主要使用在网络I/O上,而不是磁盘的I/O上.
    • 使用异步调用(如aio_write())读取I/O,当I/O完成时会发出通知(如信号或者完成端口),可以同时使用在网络I/O和磁盘I/O上.
  • 如何控制对每个客户的服务?
    • 对每个客户使用一个进程(经典的Unix方法,自从1980年一直使用)
    • 一个系统级的线程处理多个客户,每个客户是如下一种:
      • 一种用户级的线程(如GNU 状态线程,经典的java绿色线程)
      • 一个状态机 (有点深奥,但一些场景下很流行; 我喜欢)
      • 一个延续性线程 (有点深奥,但一些场景下很流行)
    • 一个系统级的线程对应一个来自客户端的连接 (如经典的java中的带native 线程)
    • 一个系统级的线程对应每一个活动的客户端连接(如Tomcat坐镇而 apache 做前端的;NT完成端口; 线程池)
  • 是否使用标准的操作系统服务,还是把一些代码放入内核中(如自定义驱动,内核模块,VxD)

下面的五种组合应该是最常用的了:

  1. 一个线程服务多个客户端,使用非阻塞I/O水平触发的就绪通知
  2. 一个线程服务多个客户端,使用非阻塞I/O和就绪改变时通知
  3. 一个服务线程服务多个客户端,使用异步I/O
  4. 一个服务线程服务一个客户端,使用阻塞I/O
  5. 把服务器代码编译进内核
qingfeng哥
qingfeng哥
翻译于 2013/06/03 15:06
2

1.一个线程服务多个客户端,并使用非阻塞I/O和电平触发的就绪通知

... 将所有网络处理单元设置为非阻塞状态,并使用select() 或 poll()识别哪个网络处理单元有等待数据。这是传统所推崇的。在这种场景,内核会告诉你一个文件描述符是否已经具备,自从上次内核告诉你这个文件描述符以后,你是否对它完成了某种事件。(名词“电平触发”(level triggered)来自于计算机硬件设计领域;它是'边缘触发' (edge triggered)的对立面。Jonathon Lemon在他的BSDCON 2000 关于kqueue()的论文 中引入了这些术语。)

注意:特别重要的是,要记住来自内核的就绪通知只是一个提示;当你准备从文件描述符读的时候,它可能还未准备就绪。这就是为什么当使用就绪通知的时候要使用非阻塞状态如此重要了。

super0555
super0555
翻译于 2013/06/03 12:35
2
这个方法中的一个重要瓶颈,就是read()或sendfile()对磁盘块操作时,如果当前内存中并不存在该页;将磁 盘文件描述符设置为非阻塞将没有效果。同样的问题也发生在内存映射磁盘文件中。当一个服务端第一次需要磁盘I/O时,它的进程模块,所有的客户端都必须等待,因此最初的非线程的性能就被消耗了。
这也是异步I/O的目的所在,但是仅仅是在没有AIO的系统,处理磁盘I/O的工作线程或工作进程也可能遭遇此 瓶颈。一条途径就是使用内存映射文件,如果mincore()指明I/O为必需的话,那么就会要求一个工作线 程来完成此I/O,然后继续处理网络事件。Jef Poskanzer提到Pai,Druschel和Zwaenepoel的1999 Flash web服务器就使用了这个方法;他们还就此在 Usenix '99上做了一个演讲。看上去就好像BSD衍生出来的 Unixes如 FreeBSD和Solaris 中提供了mincore()一样,但是它并不是 单一 Unix 规范的一部分,在Linux的2.3.51 的内核中提供了该方法, 感谢Chuck Lever
super0555
super0555
翻译于 2013/06/03 12:49
2
但在 2003年11月的 freebsd-hackers列表中,Vivek Pei报告了一个不错的成果,他们对它们的Flash Web服务器在系统范围做性能分析,然后再攻击其瓶颈。他们找到的一个瓶颈就是mincore(猜测根本就不是什么好方法),另外一个事实就是sendfile在磁盘块访问时阻塞了;他们引入了一个修改后的sendfile(),当需要读 取的磁盘页不在内核中时,返回类似EWOULDBLOCK的值,这样便提高了性能。(不确定你怎样告诉用户页面现在位于何处…在我看来这里真正需要的是aio_sendfile()。) 他们优化的最终结果是SpecWeb99,在1GHZ/1GB FreeBSD沙箱上跑了约800分,这要比在spec.org存档的任何记录都要好。
super0555
super0555
翻译于 2013/06/03 13:07
2

对于单线程来说有很多方法来分辨一组非阻塞socket中哪一个已经准备好I/O了:

  • 传统的select()
    很不幸select()受FD_SETSIZE限制。这个限制已经被编译到了标准库和用户程序。(有些版本的C语言库允许在用户应用编译的时候提高这个值。)

    参照 Poller_select (cc, h) 做为一个如何使用select()替代其它就绪通知场景例子。

  • 传统的poll()
    poll()可以处理的文件描述符数量没有硬编码的限制,但是这种方式会慢几千倍,因为大部分文件描述符总是空闲的,扫描上千个文件描述符是耗时的。
    有些操作系统(比如Solaris8)使用类似轮询暗示(poll hinting)的办法来加速poll(),在1999年Niels Provos实现了这种办法并且做了基准测试


    参照 Poller_poll (cc, h, benchmarks) 做为一个如何使用poll()替代其它就绪通知场景的例子。

  • /dev/poll
    在Solaris系统中,这是推荐的poll的替代品。
    隐藏在/dev/poll后面的想法是利用poll()经常以相同的参数调用很多次的事实。使用/dev/poll,你获取一个对/dev/poll的打开的句柄,通过写这个句柄就可以一次性告诉操作系统你对哪些文件感兴趣;从这以后,你只需要读从那个句柄返回的当前准备好的文件描述符。


    这一特性悄悄出现在了Solaris7 (see patchid 106541) 但是首先公开出现是在 Solaris 8; 参照Sun的数据,在750个客户端的时候,这种实现仅占poll()开销的10%。

    在Linux上/dev/poll有很多种实现,但是没有一种性能与epoll一样好,这些实现从来没有真正完整过。在linux上/dev/poll是不建议的。

    参照 Poller_devpoll (cc, h benchmarks ) 做为一个如何使用/dev/poll替代其它就绪通知场景的例子。(注意-这个例子是针对linux /dev/poll的,在Solaris上可能是无法工作的。)

  • kqueue()
    在FreeBSD(以及NetBSD)上,这是推荐的poll的替代品。
    看下面,kqueue()可以被指定为边缘触发或者水平触发。


袁不语
袁不语
翻译于 2013/06/10 20:55
2

2. 一个线程服务多个客户端,并使用非阻塞 I/O 和变更就绪通知

变更就绪通知(或边沿触发就绪通知)意味着你给内核一个文件描述符,这之后,当描述符从 未就绪 变换到 就绪 时,内核就以某种方式通知你。然后假设你知道文件描述符是就绪的,直到你做一些事情使得文件描述符不再是就绪的时才不会继续发送更多该类型的文件描述符的就绪通知(例如:直到在一次发送,接收或接受调用,或一次发送或接收传输小于请求的字节数的时候你收到 EWOULDBLOCK 错误)。

当你使用变更就绪通知时,你必须准备好伪事件,因为一个共同的信号就绪实现时,任何接收数据包,无论是文件描述符都已经是就绪的。

这是“水平触发”就绪通知的对立面。这是有点不宽容的编程错误,因为如果你错过了唯一的事件,连接事件就将永远的卡住。不过,我发现边沿触发就绪通知使使用 OpenSSL 编程非阻塞客户端更容易,所以是值得尝试的。

[Banga, Mogul, Drusha '99] 在1999年描述了这种方案。

一碗老酸菜
一碗老酸菜
翻译于 2013/06/12 10:45
2
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(14)

qwfys
qwfys
good
Aaaaaaaron
Aaaaaaaron
诶 那个 正弦行 翻译的 他自己真的读过吗.....
b
biblioma
挺好的文章
crossmix
crossmix
good
super0555
super0555

引用来自“pangee”的评论

@红薯 可否让这些文章,形成PDF,便于俺们码农平时在地铁上PAD阅读。

好主意
pangee
pangee
@红薯 可否让这些文章,形成PDF,便于俺们码农平时在地铁上PAD阅读。
crossmix
crossmix
别的提示
kimmking
kimmking

引用来自“sincoder”的评论

引用来自“Lax”的评论

c10k已经是上个世纪的经典话题,最近开始讨论c10m了。

已经超过端口号了。。。。。

跟端口号没关系。
sin7777
sin7777

引用来自“Lax”的评论

c10k已经是上个世纪的经典话题,最近开始讨论c10m了。

已经超过端口号了。。。。。
Lax
Lax
c10k已经是上个世纪的经典话题,最近开始讨论c10m了。
返回顶部
顶部