- 多线程模型适用于处理短连接,且连接的打开关闭非常频繁的情形,但不适合处理长连接。多线程模型默认情况下,(在Linux)每个线程会开8M的栈空间,再TCP长连接的情况下,2000/分钟的请求,几乎可以假定有上万甚至十几万的并发连接,假定有10000个连接,开这么多个线程需要10000*8M=80G的内存空间!即使调整每个线程的栈空间,也很难满足更多的需求。甚至攻击者可以利用这一点发动DDoS,只要一个连接连上服务器什么也不做,就能吃掉服务器几M的内存,这不同于多进程模型,线程间内存无法共享,因为所有线程处在同一个地址空间中。内存是多线程模型的软肋。
- 在UNIX平台下多进程模型擅长处理并发长连接,但却不适用于连接频繁产生和关闭的情形。Windows平台忽略此项。 同样的连接需要的内存数量并不比多线程模型少,但是得益于操作系统虚拟内存的Copy on Write机制,fork产生的进程和父进程共享了很大一部分物理内存。但是多进程模型在执行效率上太低,接受一个连接需要几百个时钟周期,产生一个进程 可能消耗几万个CPU时钟周期,两者的开销不成比例。而且由于每个进程的地址空间是独立的,如果需要进行进程间通信的话,只能使用IPC进行进程间通 信,而不能直接对内存进行访问。在CPU能力不足的情况下同样容易遭受DDos,攻击者只需要连上服务器,然后立刻关闭连接,服务端则需要打开一个进程再关闭。
- 同时需要保持很多的长连接,而且连接的开关很频繁,最高效的模型是非阻塞、异步IO模型。而且不要用select/poll,这两个API的有着O(N)的时间复杂度。在Linux用epoll,BSD用kqueue,Windows用IOCP,或者用libevent封装的统一接口(对于不同平台libevent实现时采用各个平台特有的API),这些平台特有的API时间复杂度为O(1)。 然而在非阻塞,异步I/O模型下的编程是非常痛苦的。由于I/O操作不再阻塞,报文的解析需要小心翼翼,并且需要亲自管理维护每个链接的状态。并且为了充分利用CPU,还应结合线程池,避免在轮询线程中处理业务逻辑。
但这种模型的效率是极高的。以知名的http服务器nginx为例,可以轻松应付上千万的空连接+少量活动链接,每个连接连接仅需要几K的内核缓冲区,想要应付更多的空连接,只需简单的增加内存(数据来源为淘宝一位工程师的一次技术讲座,并未实测)。这使得DDoS攻击者的成本大大增加,这种模型攻击者只能将服务器的带宽全部占用,才能达到目的,而两方的投入是不成比例的。
时间: 2024-11-05 06:29:17