1,TCP/IP协议概述
首先回顾一下TCP/IP模型,并知道各个层次在操作系统的哪一个层次;
看上图,OSI模型的底下两层是随系统提供的设备驱动程序和网络硬件。通常情况下,除需知道数据链路的某些特性外,
我们不用关心这两层的情况。网络层由IPv4和IPv6两个协议处理,可以选择的传输层有TCP或UDP。OSI模型的顶上三层
被合并为一层,称为应用层,这就是web客户(浏览器)、telnet客户、web服务器等都所在的层。
可见OSI模型以传输层为分界线;套接字编程接口为应用层进入传输层的接口;也符合抽象的目的:对应用层隐藏网络
协议的具体通信细节,应用层只处理具体网络应用;而底下四层(从传输层到物理层)对具体网络应用了解不多,却处理
所有的通信细节:发送数据,等待确认,给无序到达的数据排序,计算并验证校验和,等等。其次,顶上三层通常构成所
谓的用户进程,底下四层却通常作为操作系统内核的一部分提供。完全符合Unix和其他现代操作系统都提供分割用户进程
和内核的机制。
2, 基本TCP 套接字编程
TCP:传输控制协议(Transmission Control Protocol).TCP是一个面向连接的协议,为用户进程提供可靠的全双工字节流。
TCP套接字是一种流套接字(stream socket). TCP关心确认,超时和重传之类的细节。大多数网络应用程序使用TCP;
TCP基本的客户\服务器模型如下图:
以Nginx为例它 工作在应用层,并监听某一个我们指定的一个端口,并通过进程池或线程池来接受客户端的连接,
这里有个“惊群”的概念,先来看看什么是“惊群”?简单说来,多线程/多进程(linux下线程进程也没多大区别)等
待同一个socket事件,当这个事件发生时,这些线程/进程被同时唤醒,就是惊群。可以想见,效率很低下,许多
进程被内核重新调度唤醒,同时去响应这一个事件,当然只有一个进程能处理事件成功,其他的进程在处理该事
件失败后重新休眠(也有其他选择)。这种性能浪费现象就是惊群。惊群通常发生在server 上,当父进程绑定一
个端口监听socket,然后fork出多个子进程,子进程们开始循环处理(比如accept)这个socket。每当用户发起一
个TCP连接时,多个子进程同时被唤醒,然后其中一个子进程accept新连接成功,余者皆失败,重新休眠。
nginx 解决惊群的方法,简单了说,就是同一时刻只允许一个nginx worker在自己的epoll中处理监听句柄(这里采用
互斥锁机制,获得accept锁,多个worker仅有一个可以得到这把锁。)
3,探讨现代流行的应对高并发请求网络服务端设计架构;
什么是客户/服务器程序设计范式?简单点儿说就是在写客户/服务器相关程序时的一些特定的设计方式,将这些设计方式总结起来,形成几种固定
的模式,就叫客户/服务器程序设计范式.这里叫客户/服务器程序设计范式,其实大部分讲的是服务器程序设计范式,而客户程序的编写通常比服务
器程序容易一些,因为客户中进程控制要少的多.尽管如此,我们还是会提及编写客户程序的各种方式.
从总体上来说服务器设计范式分为循环设计和并发设计两种,也就是我们经常说的“迭代服务器”和“并发服务器”.
迭代服务器程序是最简单,也是最容易编写的,但这种类型适用情形极为有限,因为这样的服务器在完成对当前客户的服务之前无法处理已等待服务的新客户.
并发服务器实现起来有很多种方式:
方式.比如,客户程序阻塞在用户输入其间,对网络连接相关事件是看不到的.此时可以用select使得进程在等待用户输入其间能够得到网络事件通知;也可以用非阻塞I/O来实现。当然客户程序也可以用fork派生子进程,或者用多线程来实现并发。
在下面我们会对各种服务器设计范式进行测试,并讨论这些范式之间的差异。(UNPv1)主要包括以下几种范式:
经过比较我们可以得出以下几点总结性意见:
1,当系统负载较轻时,每来一个客户请求现场派生一个子进程为之服务的传统并发服务器程序模型就够了。
2,相比传统的每个客户fork一次设计范式,预先创建一个子进程池或子线程池的设计范式能够把进程控制
CPU时间降低10倍或以上。
3,某些实现允许多个子进程或线程阻塞在同一个accept调用中,另一些实现却要求包绕accept调用安置某种
类型的锁加以保护。文件上锁或Pthread互斥锁都可以。
4,让所有子进程或线程自行调用accept通常比让父进程或主线程独自调用accept并把描述符字传递给子进程或
线程来得简单而快速。
5,由于潜在select冲突的原因,让所有子进程或线程阻塞在同一个accept调用中比让他们阻塞在同一个select调用
中更为可取。
6,使用线程通常远快于使用进程。不过选择每个客户一个子进程还是每个客户一个线程取决于操作系统提供什么支持,
还可能取决于为服务每个客户连接的服务器调用fork和exec,那么fork一个单线程的进程可能快于fork一个多线程
的进程。
参考:http://blog.chinaunix.net/uid-20788107-id-485103.html