Nginx--整理
第二步:事件模块 (分阶段建立连接、选择进入HTTP模块 还是 添加定时器)
1 建立TCP连接:分成SYN到达事件、ACK到达事件。每次事件到达时,将它加入到epoll_ctl机制的红黑树事件中,然后等待epoll_wait的调用。
2 连接建立后,得到已连接的socket句柄,但是此时并不将连接加到事件驱动器中,调用ngx_http_init_connection中:
若可读调用ngx_http_init_request();若不可读,添加定时器。(只有真的有数据在连接上进行才处理)
第三步:HTTP模块(HTTP开始行、首部、主体进行解析、业务逻辑处理(server、location定位、访问控制、产生响应并发送、日志记录))
3 一个链接建立并有数据可读的入口ngx_http_init_request(),用于初始化ngx_request_t结构。
4 解析HTTP的开始行:ngx_http_process_request_line(),操作方法(GET、POST)空格URL空格版本回车换行
5 解析HTTP头部:格式(名称:空格值)【Host: 主机域名】 【User-Agent: 表明用户代理是使用什么浏览器】 【Accept-Language: 】 与主体之间有一个换行。
1 NGX_HTTP_POST_READ_PHASE: 接收完请求头之后的第一个阶段,它位于uri重写之前,实际上很少有模块会注册在该阶段,默认的情况下,该阶段被跳过;
2 找server:NGX_HTTP_SERVER_REWRITE_PHASE: server级别的uri重写阶段,也就是该阶段执行处于server块内,location块外的重写指令,前面的章节已经说明在读取请求头的过程中nginx会根据host及端口找到对应的虚拟主机配置;
3 找location:NGX_HTTP_FIND_CONFIG_PHASE: 寻找location配置阶段,该阶段使用重写之后的uri来查找对应的location,值得注意的是该阶段可能会被执行多次,因为也可能有location级别的重写指令;
4 重写location级别Uri:NGX_HTTP_REWRITE_PHASE: location级别的uri重写阶段,该阶段执行location基本的重写指令,也可能会被执行多次;
5 找到location:NGX_HTTP_POST_REWRITE_PHASE: location级别重写的后一阶段,用来检查上阶段是否有uri重写,并根据结果跳转到合适的阶段;
6 访问控制:NGX_HTTP_PREACCESS_PHASE: 访问权限控制的前一阶段,该阶段在权限控制阶段之前,一般也用于访问控制,比如限制访问频率,链接数等;
7 访问控制:NGX_HTTP_ACCESS_PHASE: 访问权限控制阶段,比如基于ip黑白名单的权限控制,基于用户名密码的权限控制等;
8 根据权限处理:NGX_HTTP_POST_ACCESS_PHASE: 访问权限控制的后一阶段,该阶段根据权限控制阶段的执行结果进行相应处理;
9 NGX_HTTP_TRY_FILES_PHASE: try_files指令的处理阶段,如果没有配置try_files指令,则该阶段被跳过;
10 产生内容并发送:NGX_HTTP_CONTENT_PHASE:内容生成阶段,该阶段产生响应,并发送到客户端;
11 日志记录:NGX_HTTP_LOG_PHASE: 日志记录阶段,该阶段记录访问日志;
7 HTTP响应报文:状态行:(版本空格状态码空格简单短语)
server:多个主机域名对应同一个ip地址,此时每一个server块为一个虚拟主机,只处理与之对应的主机域名请求。
是一个十分轻量级的HTTP服务器、反向代理服务器。以事件驱动的方式编写,所以性能非常的好。
web服务器定义:以统一资源描述符作为沟通依据,通过HTTP为浏览器等客户端提供各种网络服务。Apache、lighttpd、tomcat、jetty、IIS。
web服务器约束:性能(网络性能、单词请求延迟性)、可扩展性、可移植性、可靠性。
(1)高度模块化设计,可以轻松的复用各种已有模块(配置、日志记录、HTTP、mail等模块)
(2)服务器进程的管理上优秀(进程模型):动态升级,子进程的监控、管理
(4)封装了许多平台无关的的接口、容器(双向链表、动态数组)
高性能快(事件模型、进程模型、内存分配):(处理速度快和资源占用小是典型特性,尤其是当服务器遇到C10K问题的时候)
1 post机制:解决惊群问题时,拿到锁的进程对锁的释放时机。以及一般情况下也不用信号量互斥锁。
2 并发模型的设计相当重要。设置CPU个数的进程数,每个进程处理上万个链接。用这样的进程模型取代每个连接一个进程的方式,减少进程间的切换。
4 基于磁盘的异步IO,也就是说,当前进程调用磁盘IO函数时,并不死等结果,而是让系统内核处理好数据后,收到信号再处理该IO事件。
6 连接池,事件池(一一对应):由全局的ngx_cycle_t中的两个链表connections和free_connections组成。大小可由配置项初始化
7 低内存消耗:10000个非活跃HTTP keep-alive连接只要2.5MB内存。
http://www.cricode.com/2962.html
Nginx模块架构
1 每个模块都包含ngx_module_t结构体,包括每个模块的具体信息结构指针void *ctx、每个模块处理配置项的方法ngx_command_t、本模块的初始化与结束退出。用一个全局的ngx_module_s结构指针数组记住每一个模块。根据ctx指针可以访问到具体模块类型,每个模块将ctx具体化为ngx_xxx_module_t类型,(或者是具体化为ngx_core_module_t类型,针对核心类型模块)该类型提供本模块的配置项结构生成及初始化、一些公用接口。
2 基础核心类型模块的细分:ngx_core_module,ngx_errlog_module,ngx_events_module,ngx_openssl_module,ngx_http_module,ngx_mail_module。定义这些核心模块的作用是 使得非模块化的框架代码只需要关注如何调用这6个核心模块。
根据核心模块进行分工处理,事件处理有ngx_events_module处理,所有HTTp类型的模块由ngx_http_module模块管理。
3 多层次、分类别的模块化设计(类别模块):核心模块、配置模块(由框架代码定义)、事件模块、HTTP模块、mail模块
进程模型,优点
1 一个master管理进程、多个完全相同的worker进程、可选的cache manager进程和cache loader进程。
master进程作用:主要用来管理worker进程,包含:接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,当 worker进程退出后(异常情况下),会自动重新启动新的worker进程。读取并验正配置信息;创建、绑定及关闭套接字;
worker进程作用:处理基本的网络事件。接收、传入并处理来自客户端的连接;提供反向代理及过滤功能;nginx任何能完成的其它任务;
2 多个worker进程带来的优点:
1) 利用多核系统的并发能力;进程间地位平等不会出现某一级的进程成为瓶颈。
2) 负载均衡:多个worker进程间通过进程间通信来实现负载均衡。
3 master进程带来的优点:
3) master进程充当整个进程组与用户的交互接口,负责监控工作进程的状态,并负责管理其行为;当工作进程出现问题时,master进程可以重新启动一个工作进程来避免系统性能的下降。
4) master进程支持平滑升级、配置项修改的能力。
设置worker的个数为cpu的核数,在这里就很容易理解了,更多的worker数,只会导致进程来竞争cpu资源了,从而带来不必要的上下文切换。
如果负载以CPU密集型应用为主,如SSL或压缩应用,则worker数应与CPU数相同;如果负载以IO密集型为主,如响应大量内容给客户端,则worker数应该为CPU个数的1.5或2倍。
worker进程的事件循环就是监听网络事件并处理(如新建连接、断开连接、处理请求、发送响应等),所以真正的连接最终是连到了worker进程,但是worker进程之间是怎么调用accept()函数呢?
所有的worker进程都有监听套接字,都能够accept一个连接,但是nginx准备了一个accept锁,因此所有的子进程在走到处理新连接这一步的时候都要争下accept锁,争到锁的worker进程可以调用accept()并接受新连接。
这样做的目的就是为了防止多个进程同时accept,当一个连接来的时候多个进程同时被唤起,即惊群。
http://cjhust.blog.163.com/blog/static/17582715720121274746505/
多线程模型 VS 多进程模型
nginx采用这种进程模型有什么好处:
1 对于每个worker进程来说,独立的进程,不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题查找时,也会方便很多。
2 独立的进程,一个进程退出后,其它进程正常工作。
http://www.cricode.com/2962.html
进程通信
1 管道(Pipe)及有名管道(named pipe FIFO):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。
无名管道:int pipe(int filedes[2]); //filedes[0]为读
有名管道:int mkfifo(const char *pathname,mode_t mode);可用于进程间的非线性连接,所以可用于客户进程/服务器进程之间的通信(大家都往众所周知的FIFO写,再维护一个独立客户进程与服务器进程间的FIFO)
2 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了 支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了 实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数);
3 报文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
int msgget(key_t key,int flag);//将键变成标识符,一个唯一的ID。
4 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步访问及互斥。
5 信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。实际上是同步原语而不是IPC,常用于共享资源的同步访问。(创建信号量集、对信号量赋初值、。。。)
(3、4、5)具有类似结构,《标识符,键》前者为内部名,后者为外部名,键由内核变换成标识符。
6 套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。
服务器进程是守护进程时,所有客户进程用某种形式的IPC与其联系:需要使用有名的IPC(FIFO、消息队列)
Nginx进程通信
1 共享内存
通过mmap()或者shmget()创建一块线性地址空间;
void* mmap(coid *start,size_t length,ing prot,int flags,int fd,off_t offset);
mmap可以将磁盘文件映射到内存,fd即为操作的磁盘文件,prot指操作方式。
通过munmap()或者shmdt()释放。
ngx_shm_alloc(ngx_shm_t *shm);ngx_shm_free():
封装mmap,且无映射文件;封装以/dev/zero文件进行映射的mmap;封装shmget。
Nginx一般由master进程创建共享内存,然后fork子进程,所有进程开始使用这块内存。是Nginx进程间共享数据的主要方式。
例子:统计整个服务器HTTP连接状况的功能。共享的统计变量,存放于共享内存中。
2 套接字 ==》封装形成Nginx频道 Nginx进程间通信
ngx_channel_t结构封装要传递的命令、socketpair申请到的一个套接字。主要用于master进程向worker进程传递命令。
将套接字设置为无阻塞模式。
3 信号
进程同步
信号量
记录锁
Nginx进程同步
1 原子操作
2 信号量
Nginx仅把它作为简单的互斥锁使用,使用信号量作为互斥锁有可能导致进程睡眠。
3 文件锁
基于文件的互斥锁。
int fcntl(int fd,in cmd,struct flock *lock);
根据fd文件描述符确定对谁操作,lock说明锁类型,锁区域起始,偏移量,哪个进程拥有这个锁。
基于原子操作、信号量、文件锁封装了一个ngx_shmtx_t互斥锁。根据不同的配置选用不同的锁。
一般情况下不直接利用信号量,因为一旦获取信号量互斥锁失败,进程则睡眠,其它请求就会饿死。
事件处理模块、架构
事件驱动架构:
由一些事件发生源产生事件,由一个或多个事件收集器来收集、分发事件,然后许多事件处理器会注册自己感兴趣的事件,同时”消费“这些事件。
事件发生源:网卡、磁盘、定时器。
事件收集器:事件模块。
事件处理器:所有模块。
传统事件模型:每个事件消费者独占一个进程资源。
Nginx事件模型:事件消费者只是被事件分发着进程短期调用而已。缺点:每个事件消费者不能有阻塞行为。
请求的多阶段异步处理:(获取静态文件的HTTP请求)(如何划分请求阶段:找到阻塞方法或者代码段)
(1)接收到SYN包==》建立TCP连接
(2)接收到TCP的ACK包==》开始接受用户请求
(3)接收到用户的数据包==》分析接收的请求是否完整
(4)接收到用户的数据报==》收到完整请求,开始处理用户请求
(5)收到已连接的TCP的ACK包==》读取部分静态文件内容发给用户
(6)收到最后数据的ACK包==》对于非keep-alive请求,主动关闭连接
(7)接收到FIN包==》用户结束连接
多阶段异步处理 + 时间驱动框架 :能够极大的提高网络性能,使得每个进程能够全力运转,不会或者尽量少的出现进程休眠状态。
ngx_events_module模块:解析events配置项,并管理这些存储配置项的结构体。
ngx_event_core_module模块:连接池的创建、决定使用哪种事件驱动机制,初始化要用的事件模块。
ngx_connect_t:用于表示一个TCP连接,该结构体中包含读写事件ngx_event_t。
ngx_epoll_module模块:实现两个结构体ngx_module_t 和 ngx_event_module_t。
HTTP模块
1 所有的server虚拟主机以散列表组织起来。关键字为每个server name字符串;hash的值就是每个表示server结构的结构体地址ngx_http_core_srv_conf_t。使用开放寻址法处理冲突。
所有的location表达式会以一个静态的二叉查找树组织起来。
2 HTTP框架目的:
事件框架对应传输层TCP;HTTP框架对应应用层,解决HTTP的网络传输、解析、组装;
HTTP框架为HTTP模块屏蔽事件驱动架构。
负载均衡模块
1 区分worker进程间的负载均衡 和 后端服务器之间的负载均衡。
2 nginx还可以按照调度规则实现动态、静态页面的分离,可以按照轮询、ip哈希、URL哈希、权重等多种方式对后端服务器做负载均衡。如果只有一台服务器时,这个服务器挂了,那么对于网站来说是个灾难.因此,这时候的负载均衡就会大显身手了,它会自动剔除挂掉的服务器
3 nginx 的 upstream目前支持 4 种方式的分配 :
1)轮询(默认):每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
2)weight :指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
3)ip_hash :每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
4)fair(第三方):按后端服务器的响应时间来分配请求,响应时间短的优先分配。
5)url_hash(第三方)
Nginx默认采用round_robin加权算法。
加权轮询策略
优点:适用性更强,不依赖于客户端的任何信息,完全依靠后端服务器的情况来进行选择。能把客户端请求更合理更均匀地分配到各个后端服务器处理。
缺点:同一个客户端的多次请求可能会被分配到不同的后端服务器进行处理,无法满足做会话保持的应用的需求。
IP哈希策略
优点:能较好地把同一个客户端的多次请求分配到同一台服务器处理,避免了加权轮询无法适用会话保持的需求。
缺点:当某个时刻来自某个IP地址的请求特别多,那么将导致某台后端服务器的压力可能非常大,而其他后端服务器却空闲的不均衡情况、
Nginx与Apache性能比较
nginx采用了异步非阻塞的方式来处理请求,也就是说,nginx是可以同时处理成千上万个请求的。想想apache的常用工作方式(apache也有 异步非阻塞版本,但因其与自带某些模块冲突,所以不常用),每个请求会独占一个工作线程,当并发数上到几千时,就同时有几千的线程在处理请求了。这对操作 系统来说,是个不小的挑战,线程带来的内存占用非常大,线程的上下文切换带来的cpu开销很大,自然性能就上不去了,而这些开销完全是没有意义的。
Nginx是一款面向性能设计的HTTP服务器,相较于Apache、lighttpd具有占有内存少,稳定性高等优势。
apache三种工作模式
Apache有三种工作模块,分别为prefork、worker、event。
prefork:多进程,每个请求用一个进程响应,这个过程会用到select机制来通知。
worker:多线程,一个进程可以生成多个线程,每个线程响应一个请求,但通知机制还是select不过可以接受更多的请求。
event:基于异步I/O模型,一个进程或线程,每个进程或线程响应多个用户请求,它是基于事件驱动(也就是epoll机制)实现的。
对于那些需要更强大的Web应用服务器(比如大小、可定制、响应速度、可扩展性等方面)的人而言,Apache明显不符合他们的要求
http://blog.csdn.net/jackem/article/details/2871958 Apache的最新更新 增加了 event MPM模型
如何提高Web服务器的并发连接处理能力
有几个基本条件:
- 基于线程,即一个进程生成多个线程,每个线程响应用户的每个请求。
- 基于事件的模型,一个进程处理多个请求,并且通过epoll机制来通知用户请求完成。
- 基于磁盘的AIO(异步I/O)
- 支持mmap内存映射,mmap传统的web服务器,进行页面输入时,都是将磁盘的页面先输入到内核缓存中,再由内核缓存中复制一份到web服务器上,mmap机制就是让内核缓存与磁盘进行映射,web服务器,直接复制页面内容即可。不需要先把磁盘的上的页面先输入到内核缓存去。
Apache和Nginx比较
功能对比
Nginx和Apache一样,都是HTTP服务器软件,在功能实现上都采用模块化结构设计,都支持通用的语言接口,如PHP、Perl、Python等,同时还支持正向和反向代理、虚拟主机、URL重写、压缩传输、SSL加密传输等。
- 在功能实现上,Apache的所有模块都支持动、静态编译,而Nginx模块都是静态编译的,
- 对FastCGI的支持,Apache对Fcgi的支持不好,而Nginx对Fcgi的支持非常好;
- 在处理连接方式上,Nginx支持epoll,而Apache却不支持;
- 在空间使用上,Nginx安装包仅仅只有几百K,和Nginx比起来Apache绝对是庞然大物。
Nginx相对apache的优点
- 轻量级,同样起web 服务,比apache 占用更少的内存及资源
- 静态处理,Nginx 静态处理性能比 Apache 高 3倍以上
- 抗并发,nginx 处理请求是异步非阻塞的,而apache则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能。在Apache+PHP(prefork)模式下,如果PHP处理慢或者前端压力很大的情况下,很容易出现Apache进程数飙升,从而拒绝服务的现象。
- 高度模块化的设计,编写模块相对简单
- 社区活跃,各种高性能模块出品迅速啊
apache相对nginx的优点
- rewrite,比nginx 的rewrite 强大
- 模块超多,基本想到的都可以找到
- 少bug,nginx的bug相对较多
- 超稳定
- Apache对PHP支持比较简单,Nginx需要配合其他后端用
选择Nginx的优势所在
- 作为Web服务器: Nginx处理静态文件、索引文件,自动索引的效率非常高。 那是因为静态文件本身也是磁盘IO操作
- 作为代理服务器,Nginx可以实现无缓存的反向代理加速,提高网站运行速度。
- 作为负载均衡服务器,Nginx既可以在内部直接支持Rails和PHP,也可以支持HTTP代理服务器对外进行服务,同时还支持简单的容错和利用算法进行负载均衡。
- 在性能方面,Nginx是专门为性能优化而开发的,在实现上非常注重效率。它采用内核Poll模型(epoll and kqueue ),可以支持更多的并发连接,最大可以支持对50 000个并发连接数的响应,而且只占用很低的内存资源。
- 在稳定性方面,Nginx采取了分阶段资源分配技术,使得CPU与内存的占用率非常低。Nginx官方表示,Nginx保持10 000个没有活动的连接,而这些连接只占用2.5MB内存,因此,类似DOS这样的攻击对Nginx来说基本上是没有任何作用的。
- 在高可用性方面,Nginx支持热部署,启动速度特别迅速,因此可以在不间断服务的情况下,对软件版本或者配置进行升级,即使运行数月也无需重新启动,几乎可以做到7×24小时不间断地运行。
同时使用Nginx和Apache
由于Nginx和Apache各自的优势,现在很多人选择了让两者在服务器中共存。在服务器端让Nginx在前,Apache在后。由Nginx做负载均衡和反向代理,并且处理静态文件,讲动态请求(如PHP应用)交给Apache去处理。
http://yansu.org/2014/02/15/apache-and-nginx.html Nginx与Apache 分析的挺好
http://www.juziku.com/wiki/15028.htm
首先就聊了下Nginx,什么进程模型,优点等等。然后问了select、poll和epoll的区别。
Nginx的优越性在哪?我就提提进程模型、epoll了,再和Apache简单做了下比较。