Nginx:
Nginx (engine x) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器。Nginx是由Igor Sysoev为俄罗斯访问量第二的Rambler.ru站点开发的,第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like协议下发行。由俄罗斯的程序设计师Igor Sysoev所开发,供俄国大型的入口网站及搜索引擎Rambler使用。其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
Nginx可以在大多数 UnixLinux OS上编译运行,并有Windows移植版。 Nginx 的1.4.0稳定版已经于2013年4月24日发布,2016年10月18日,Nginx1.10.2 稳定版本发布。一般情况下,对于新建站点,建议使用最新稳定版作为生产版本,已有站点的升级急迫性不高。Nginx 的源代码使用2-clause BSD-like license。
代理服务器:一般是指局域网内部的机器通过代理服务器发送请求到互联网上的服务器,代理服务器一般作用在客户端。
一个完整的代理请求过程为:客户端首先与代理服务器创建连接,接着根据代理服务器所使用的代理协议,请求对目标服务器创建连接、或者获得目标服务器的指定资源。 Web代理(proxy)服务器是网络的中间实体。 代理位于Web客户端和Web服务器之间,扮演“中间人”的角色。HTTP的代理服务器即是Web服务器又是Web客户端。
代理服务器是介于客户端和Web服务器之间的另一台服务器,有了它之后,浏览器不是直接到Web服务器去取回网页而是向代理服务器发出请求,信号会先送到代理服务器,由代理服务器来取回浏览器所需要的信息并传送给你的浏览器。
正向代理:是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端必须要进行一些特别的设置才能使用正向代理。
反向代理服务器:在服务器端接受客户端的请求,然后把请求分发给具体的服务器进行处理,然后再将服务器的响应结果反馈给客户端。Nginx就是其中的一种反向代理服务器软件。
说明:客户端必须设置正向代理服务器,当然前提是要知道正向代理服务器的IP地址,还有代理程序的端口。
反向代理正好与正向代理相反,对于客户端而言代理服务器就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理的命名空间(name-space)中的内容发送普通请求,接着反向代理将判断向何处(原始服务器)转交请求,并将获得的内容返回给客户端。
用户A始终认为它访问的是原始服务器B而不是代理服务器Z,但实用际上反向代理服务器接受用户A的应答,从原始资源服务器B中取得用户A的需求资源,然后发送给用户A。由于防火墙的作用,只允许代理服务器Z访问原始资源服务器B。尽管在这个虚拟的环境下,防火墙和反向代理的共同作用保护了原始资源服务器B,但用户A并不知情。
Nginx特点:
跨平台:Nginx 可以在大多数 Unix like OS编译运行,而且也有Windows的移植版本。
配置异常简单:非常容易上手。配置风格跟程序开发一样,神一般的配置
非阻塞、高并发连接:数据复制时,磁盘I/O的第一阶段是非阻塞的。官方测试能够支撑5万并发连接,在实际生产环境中跑到2~3万并发连接数.(这得益于Nginx使用了最新的epoll模型)
事件驱动:通信机制采用epoll模型,支持更大的并发连接。
Nginx优点:
高并发:Nginx 是一个很强大的高性能Web和反向代理服务器,它具有很多非常优越的特性。在连接高并发的情况下,Nginx是Apache服务器不错的替代品,能够支持高达 50,000 个并发连接数的响应。
负载均衡器:Nginx作为负载均衡服务器:Nginx 既可以在内部直接支持 Rails 和 PHP 程序对外进行服务,也可以支持作为 HTTP代理服务器对外进行服务。
代理服务器:Nginx本身就是一个反向代理服务器,可支持邮件服务器代理以及http代理
Nginx相比Apache:
1.轻量级,同样起web 服务,比apache 占用更少的内存及资源
2.抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能
3.高度模块化的设计,编写模块相对简单
4.配置简洁易懂,正则配置让很多事情变得简单
Nginx事件处理机制:
对于一个基本的web服务器来说,事件通常有三种类型,网络事件、信号、定时器。
首先看一个请求的基本过程:建立连接—接收数据—发送数据 。
再次看系统底层的操作 :上述过程(建立连接—接收数据—发送数据)在系统底层就是读写事件。
(1)如果采用阻塞调用的方式,当读写事件没有准备好时,必然不能够进行读写事件,那么久只好等待,等事件准备好了,才能进行读写事件。那么请求就会被耽搁 。阻塞调用会进入内核等待,cpu就会让出去给别人用了,对单线程的worker来说,显然不合适,当网络事件越多时,大家都在等待呢,cpu空闲下来没人用,cpu利用率自然上不去了,更别谈高并发了 。
(2)既然没有准备好阻塞调用不行,那么采用非阻塞方式。非阻塞就是,事件,马上返回EAGAIN, 告诉你,事件还没准备好呢,你慌什么,过会再来吧。好吧,你过一会,再来检查一下事件,直到事件准备好了为止,在这期间,你就可以先去做其它事情,然后再 来看看事件好了没。虽然不阻塞了,但你得不时地过来检查一下事件的状态,你可以做更多的事情了,但带来的开销也是不小的
小结:非阻塞通过不断检查事件的状态来判断是否进行读写操作,这样带来的开销很大。
(3)因此才有了异步非阻塞的事件处理机制。具体到系统调用就是像select/poll/epoll/kqueue这样的系统调用。他们提供了一种机制,让你可以同时监控多个事件,调用他们是阻塞的,但可以设置超时时间,在超时时间之内,如果有事件准备好了,就返回。这种机制解决了我们上面两个问题。
以epoll为例:当事件没有准备好时,就放入epoll(队列)里面。如果有事件准备好了,那么就去处理;如果事件返回的是EAGAIN,那么继续将其放入epoll里面。从而,只要有事件准备好了,我们就去处理她,只有当所有时间都没有准备好时,才在epoll里 面等着。这样,我们就可以并发处理大量的并发了,当然,这里的并发请求,是指未处理完的请求,线程只有一个,所以同时能处理的请求当然只有一个了,只是在 请求间进行不断地切换而已,切换也是因为异步事件未准备好,而主动让出的。这里的切换是没有任何代价,可以理解为循环处理多个准备好的事件
(4)与多线程的比较:
与多线程相比,这种事件处理方式是有很大的优势的,不需要创建线程,每个请求占用的内存也很少,没有上下文切换,事件处理非常的轻量级。并发数再多也不会导致无谓的资源浪费(上下文切换)。
小结:通过异步非阻塞的事件处理机制,Nginx实现由进程循环处理多个准备好的事件,从而实现高并发和轻量级。
master/worker结构:一个master进程,生成一个或多个worker进程
内存消耗小:处理大并发的请求内存消耗非常小。在3万并发连接下,开启的10个Nginx 进程才消耗150M内存(15M*10=150M) 成本低廉:Nginx为开源软件,可以免费使用。而购买F5 BIG-IP、NetScaler等硬件负载均衡交换机则需要十多万至几十万人民币
内置的健康检查功能:如果 Nginx Proxy 后端的某台 Web 服务器宕机了,不会影响前端访问。
节省带宽:支持 GZIP 压缩,可以添加浏览器本地缓存的 Header 头。
稳定性高:用于反向代理,宕机的概率微乎其微。
Nginx架构:
Nginx在启动后,在Unix系统中会以daemon的方式在后台运行,后台进程包含一个master进程和多个worker进程。我们也可以手动地关掉后台模式,让Nginx在前台运行,并且通过配置让Nginx取消master进程,从而可以使Nginx以单进程方式运行。很显然,生产环境下我们肯定不会这么做,所以关闭后台模式,一般是用来调试用的,Nginx是以多进程的方式来工作的,当然Nginx也是支持多线程的方式的,只是我们主流的方式还是多进程的方式,也是Nginx的默认方式。Nginx采用多进程的方式有诸多好处。
Nginx在启动后,会有一个master进程和多个worker进程。master进程主要用来管理worker进程,包含:接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。而基本的网络事件,则是放在worker进程中来处理了。多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。worker进程的个数是可以设置的,一般我们会设置与机器cpu核数一致,这里面的原因与Nginx的进程模型以及事件处理模型是分不开的。Nginx的进程模型,可以由下图来表示:
master进程会接收来自外界发来的信号,再根据信号做不同的事情。所以我们要控制Nginx,只需要通过kill向master进程发送信号就行了。比如kill -HUP pid,则是告诉Nginx,从容地重启Nginx,我们一般用这个信号来重启Nginx,或重新加载配置,因为是从容地重启,因此服务是不中断的。master进程在接收到HUP信号后是怎么做的呢?首先master进程在接到信号后,会先重新加载配置文件,然后再启动新的worker进程,并向所有老的worker进程发送信号,告诉他们可以光荣退休了。新的worker在启动后,就开始接收新的请求,而老的worker在收到来自master的信号后,就不再接收新的请求,并且在当前进程中的所有未处理完的请求处理完成后,再退出。当然,直接给master进程发送信号,这是比较老的操作方式,Nginx在0.8版本之后,引入了一系列命令行参数,来方便我们管理。比如,./nginx -s reload,就是来重启Nginx,./nginx -s stop,就是来停止Nginx的运行。如何做到的呢?我们还是拿reload来说,我们看到,执行命令时,我们是启动一个新的Nginx进程,而新的Nginx进程在解析到reload参数后,就知道我们的目的是控制Nginx来重新加载配置文件了,它会向master进程发送信号,然后接下来的动作,就和我们直接向master进程发送信号一样了。
worker进程之间是平等的,每个进程,处理请求的机会也是一样的。当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接。每个worker进程都是从master进程fork过来,在master进程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。所有worker进程的listenfd会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。
对于每个worker进程来说,独立的进程,不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题查找时,也会方便很多。其次,采用独立的进程,可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断,master进程则很快启动新的worker进程。当然,worker进程的异常退出,肯定是程序有bug了,异常退出,会导致当前worker上的所有请求失败,不过不会影响到所有请求,所以降低了风险。
nginx的模块化体系结构:
nginx的内部结构是由核心部分和一系列的功能模块所组成。这样划分是为了使得每个模块的功能相对简单,便于开发,同时也便于对系统进行功能扩展。为了便于描述,下文中我们将使用nginx core来称呼nginx的核心功能部分。
nginx提供了web服务器的基础功能,同时提供了web服务反向代理,email服务反向代理功能。nginx core实现了底层的通讯协议,为其他模块和nginx进程构建了基本的运行时环境,并且构建了其他各模块的协作基础。除此之外,或者说大部分与协议相关的,或者应用相关的功能都是在这些模块中所实现的。
模块概述:
nginx将各功能模块组织成一条链,当有请求到达的时候,请求依次经过这条链上的部分或者全部模块,进行处理。每个模块实现特定的功能。例如,实现对请求解压缩的模块,实现SSI的模块,实现与上游服务器进行通讯的模块,实现与FastCGI服务进行通讯的模块。
有两个模块比较特殊,他们居于nginx core和各功能模块的中间。这两个模块就是http模块和mail模块。这2个模块在nginx core之上实现了另外一层抽象,处理与HTTP协议和email相关协议(SMTP/POP3/IMAP)有关的事件,并且确保这些事件能被以正确的顺序调用其他的一些功能模块。
目前HTTP协议是被实现在http模块中的,但是有可能将来被剥离到一个单独的模块中,以扩展nginx支持SPDY协议。
nginx的请求处理:
nginx使用一个多进程模型来对外提供服务,其中一个master进程,多个worker进程。master进程负责管理nginx本身和其他worker进程。
所有实际上的业务处理逻辑都在worker进程。worker进程中有一个函数,执行无限循环,不断处理收到的来自客户端的请求,并进行处理,直到整个nginx服务被停止。
worker进程中,ngx_worker_process_cycle()函数就是这个无限循环的处理函数。在这个函数中,一个请求的简单处理流程如下:
1.操作系统提供的机制(例如epoll, kqueue等)产生相关的事件。
2.接收和处理这些事件,如是接受到数据,则产生更高层的request对象。
3.处理request的header和body。
4.产生响应,并发送回客户端。
5.完成request的处理。
6.重新初始化定时器及其他事件。
编译安装Nginx:
[root@localhost ~]# yum -y groupinstall "Development Tools" "Server Platform Development" #安装开发包组 [root@localhost ~]# yum -y install pcre-devel openssl-devel zlib-devel #安装依赖包 [root@localhost ~]# useradd -r nginx # 创建nginx系统用户 [root@localhost~]#./configure--prefix=/usr/local/nginx--conf-path=/etc/nginx/nginx.conf--error-log-path=/var/log/nginx/error.log--http-log-path=/var/log/nginx/access.log--pid-path=/var/run/nginx.pid--lock-path=/var/run/nginx.lock --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_dav_module --with-http_stub_status_module --with-threads --with-file-aio # 配置nginx [root@localhost ~]# make #编译 [root@localhost ~]# make install #安装
或设置连接官网的yum源(官网地址http://nginx.org/packages/centos/7/x86_64/)
[root@localhost ~]#vim /etc/yum.repos.d/nginx.repo [nginx] name=nginx baseurl=http://nginx.org/packages/centos/7/x86_64/ gpgcheck=0 [root@localhost ~]#yum install nginx [root@localhost ~]#systemctl start nginx.service
也可从本地epel直接安装
[root@localhost ~]#yum install nginx [root@localhost ~]#systemctl start nginx.service
修改欢迎页面
[root@nginx1 /etc/nginx]#mkdir /data/nginx/vhost1 -pv[root@nginx1 /etc/nginx]#vim /data/nginx/vhost1/index.html<h1>Nginx Vhost 1</h1>[root@nginx1 /etc/nginx]#vim conf.d/vhost1.confserver { listen 80;#监听80端口 server_name www.ilinux.io;#主机名为www.ilinux.io root /data/nginx/vhost1;#路径}[root@nginx1 /etc/nginx]#nginx -t[root@nginx1 /etc/nginx]#nginx -s reload#重载配置文件[root@nginx1 /etc/nginx]#vim /etc/hosts172.16.254.127 www.ilinux.io
程序环境
配置文件的组成部分:
主配置文件:nginx.conf
include conf.d/*.conf
fastcgi, uwsgi,scgi等协议相关的配置文件
mime.types:支持的mime类型
主程序文件:/usr/sbin/nginx
Unit File:nginx.service
Nginx配置文件:
nginx配置文件组成:主配置文件nginx.conf,conf.d/*.conf;
fastcgi,uwsgi,scgi等协议相关的配置文件;
mime.types:支持的mime类型
main段配置:
分类:
正常运行必备的配置
优化性能相关的配置
用于调试及定位问题相关的配置
事件驱动相关的配置
正常运行必备的配置:
1、user # 指定用于运行worker进程时的用户
Syntax: user user [group];
Default:user nobody nobody;
Context: main
2、pid /PATH/TO/PID_FILE; # 指定存储nginx主进程进程号码的文件路径
Syntax: pid file;
Default:pid nginx.pid;
Context:main
3、include file | mask; # 指明包含进来的其它配置文件片断
Syntax: include file | mask;
Default:—
Context:any
4、load_module file; # 指明要装载的动态模块
Syntax: load_module file;
Default:—
Context:main
性能优化相关的配置:
1、worker_processes number | auto; # worker进程的数量;通常应该为当前主机的cpu的物理核心数
Syntax: worker_processes number | auto;
Default:worker_processes 1;
Context:main
如果只有两个进程
[root@localhost /etc/nginx]#nginx -t [root@localhost /etc/nginx]#nginx -s reload
[root@localhost /etc/nginx]#nginx -t [root@localhost /etc/nginx]#nginx -s reload
2、worker_cpu_affinity cpumask …; # 定义worker进程和cpu的绑定
worker_cpu_affinity auto [cpumask];
Default:—
Context:main
CPU MASK:
00000001:0号CPU
00000010:1号CPU
00000100: 2号CPU
… …
绑定cpu,默认次序0、1、2、3
[root@localhost /etc/nginx]#nginx -t [root@localhost /etc/nginx]#nginx -s reload
反向绑定cpu,3、2、1、0
[root@localhost /etc/nginx]#nginx -t [root@localhost /etc/nginx]#nginx -s reload
3、worker_priority number; # 指定worker进程的nice值,设定worker进程优先级;[-20,20]
Syntax: worker_priority number;
Default:worker_priority 0;
Context:main
默认优先级
[root@localhost /etc/nginx]#ps axo comm,pid,psr,ni | grep nginx nginx 4795 2 0 nginx 43059 3 0 优先级为0 nginx 43060 2 0 优先级为0
设定优先级
[root@localhost /etc/nginx]#nginx -t [root@localhost /etc/nginx]#nginx -s reload [root@localhost /etc/nginx]#ps axo comm,pid,psr,ni | grep nginx nginx 4795 2 0 nginx 43361 3 -5 优先级为-5 nginx 43362 2 -5 优先级为-5
4、worker_rlimit_nofile number; # worker进程所能够打开的文件数量上限
Syntax: worker_rlimit_nofile number;
Default:—
Context:main
[root@localhost /etc/nginx]#vim nginx.conf
调试、定位问题:
1、daemon on|off; # 是否以守护进程方式运行Nignx
Syntax: daemon on | off;
Default:daemon on;
Context:main
2、master_process on|off; # 是否以master/worker模型运行nginx;默认为on
Syntax: master_process on | off;
Default:master_process on;
Context:main
3、error_log file [level]; # 定义错误日志文件路径与级别
Syntax: error_log file [level];
Default:error_log logs/error.log error;
Context:main, http, mail, stream, server, location
事件驱动相关的配置:
events {
…
}
1、worker_connections number; # 每个worker进程所能够打开的最大并发连接数数量
Syntax: worker_connections number;
Default:worker_connections 512;
Context:events
worker_processes * worker_connections得出最大并发连接数
2、use method; # 指明并发连接请求的处理方法
Syntax: use method;
Default:—
Context:events
use epoll;
3、accept_mutex on | off; # 处理新的连接请求的方法;on意味着由各worker轮流处理新请求,Off意味着每个新请求的到达都会通知所有的worker进程;建议使用on
Syntax: accept_mutex on | off;
Default:accept_mutex off;
Context:events
主配置文件结构:
main block:主配置段,也即全局配置段;
event {
…
}:事件驱动相关的配置;
http {
…
}:http/https 协议相关的配置段;
mail {
…
}
stream {
…
}
http协议的相关配置:
http { ... ...:各server的公共配置 server { ... }:每个server用于定义一个虚拟主机; server { ... listen server_name root alias location [OPERATOR] URL { ... if CONDITION { ... } } } }
参考资料:
http://wiki.nginx.org/HttpUpstreamConsistentHash
http://wiki.nginx.org/HttpUpstreamFairModule
http://wiki.nginx.org/HttpUpstreamRequestHashModule
原创文章,作者:Linux.rookie,如若转载,请注明出处:http://www.178linux.com/78370