Nginx简介

Nginx是什么

没有听过Nginx?那么必定听过它的“同行”Apache吧!Nginx同Apache同样都是一种WEB服务器。基于REST架构风格,以统一资源描述符(Uniform Resources Identifier)URI或者统一资源定位符(Uniform Resources Locator)URL做为沟通依据,经过HTTP协议提供各类网络服务。php

然而,这些服务器在设计之初受到当时环境的局限,例如当时的用户规模,网络带宽,产品特色等局限而且各自的定位和发展都不尽相同。这也使得各个WEB服务器有着各自鲜明的特色。html

Apache的发展时期很长,并且是毫无争议的世界第一大服务器。它有着不少有点:稳定、开源、跨平台等等。可是因为它出现的时间太长了。它兴起的年代,互联网产业远比不上如今。因此它被设计为一个重量级的。不支持高并发的服务器。在Apache上运行数以万计的并发访问,会致使服务器消耗大量内存。操做系统对其进行进程或线程间的切换也消耗了大量的CPU资源,致使HTTP请求的平均响应速度下降。nginx

这些都决定了Apache不可能成为高性能WEB服务器,轻量级高并发服务器Nginx和Lighttpd就应运而生了。web

 

Nginx产生

又是拜大神的时候了,此次被选中的人是俄罗斯的工程师Igor Sysoev,他在为Rambler Media工做期间,使用C语言开发了Nginx。Nginx做为WEB服务器一直为Rambler Media提供出色而又稳定的服务。apache

而后呢,Igor Sysoev将Nginx代码开源,而且赋予自由软件许可证。编程

因为:ubuntu

  • Nginx使用基于事件驱动架构,使得其能够支持数以百万级别的TCP链接
  • 高度的模块化和自由软件许可证是的第三方模块层出不穷(这是个开源的时代啊~)
  • Nginx是一个跨平台服务器,能够运行在Linux, FreeBSD, Solaris, AIX, Mac OS, Windows等操做系统上
  • 这些优秀的设计带来的极大的稳定性。

因而,duang的一下。Nginx火了。vim

 

Nginx基本概念

静态HTTP服务器

首先,Nginx是一个HTTP服务器,能够将服务器上的静态文件(如HTML、图片)经过HTTP协议展示给客户端。
配置:服务器

server {
    listen 80; # 端口号
    location / {
        root /usr/share/nginx/html; # 静态文件路径
    }
}

反向代理服务器

什么是反向代理?网络

客户端原本能够直接经过HTTP协议访问某网站应用服务器,若是网站管理员在中间加上一个Nginx,客户端请求Nginx,Nginx请求应用服务器,而后将结果返回给客户端,此时Nginx就是反向代理服务器。

反向代理配置:

server {
    listen 80;
    location / {
        proxy_pass http://192.168.0.112:8080; # 应用服务器HTTP地址
    }
}

既然服务器能够直接HTTP访问,为何要在中间加上一个反向代理,不是画蛇添足吗?反向代理有什么做用?继续往下看,下面的负载均衡、虚拟主机,都基于反向代理实现,固然反向代理的功能也不只仅是这些。

负载均衡

当网站访问量很是大,也摊上事儿了。由于网站愈来愈慢,一台服务器已经不够用了。因而将相同的应用部署在多台服务器上,将大量用户的请求分配给多台机器处理。同时带来的好处是,其中一台服务器万一挂了,只要还有其余服务器正常运行,就不会影响用户使用。
Nginx能够经过反向代理来实现负载均衡。


 
负载均衡配置:
  vim /etc/nginx/nginx.conf 配置文件http内
    upstream myapp {
    server 39.106.191.226:8000; # 应用服务器1
    server 39.97.119.81:8001; # 应用服务器2
}
在 vim /etc/nginx/conf.d/default.conf 服务配置呢把设置好的应用服务器
server {
    listen 80;
    location / {
        proxy_pass myweb;
    }
}

虚拟主机

有的网站访问量大,须要负载均衡。然而并非全部网站都如此出色,有的网站,因为访问量过小,须要节省成本,将多个网站部署在同一台服务器上。

例如将www.aaa.comwww.bbb.com两个网站部署在同一台服务器上,两个域名解析到同一个IP地址,可是用户经过两个域名却能够打开两个彻底不一样的网站,互相不影响,就像访问两个服务器同样,因此叫两个虚拟主机。

配置:
server {
    listen 80 default_server;
    server_name _;
    return 444; # 过滤其余域名的请求,返回444状态码
}
server {
    listen 80;
    server_name www.aaa.com; # www.aaa.com域名
    location / {
        proxy_pass http://localhost:8080; # 对应端口号8080
    }
}
server {
    listen 80;
    server_name www.bbb.com; # www.bbb.com域名
    location / {
        proxy_pass http://localhost:8081; # 对应端口号8081
    }
}

在服务器8080和8081分别开了一个应用,客户端经过不一样的域名访问,根据server_name能够反向代理到对应的应用服务器。

虚拟主机的原理是经过HTTP请求头中的Host是否匹配server_name来实现的,有兴趣的同窗能够研究一下HTTP协议。

另外,server_name配置还能够过滤有人恶意将某些域名指向你的主机服务器。

 

FastCGI

Nginx自己不支持PHP等语言,可是它能够经过FastCGI来将请求扔给某些语言或框架处理(例如PHP、Python、Perl)。

server {
    listen 80;
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /PHP文件路径$fastcgi_script_name; # PHP文件路径
        fastcgi_pass 127.0.0.1:9000; # PHP-FPM地址和端口号
        # 另外一种方式:fastcgi_pass unix:/var/run/php5-fpm.sock;
    }
}

配置中将.php结尾的请求经过FashCGI交给PHP-FPM处理,PHP-FPM是PHP的一个FastCGI管理器。有关FashCGI能够查阅其余资料,本篇再也不介绍。

 

fastcgi_pass和proxy_pass有什么区别?
下面一张图带你看明白:
 

Nginx经常使用命令

1. 启动 Nginx

poechant@ubuntu:sudo ./sbin/nginx

2. 中止 Nginx

poechant@ubuntu:sudo ./sbin/nginx -s stoppoechant@ubuntu:sudo ./sbin/nginx -s quit

-s都是采用向 Nginx 发送信号的方式。

3. Nginx 重载配置

poechant@ubuntu:sudo ./sbin/nginx -s reload

上述是采用向 Nginx 发送信号的方式,或者使用:

poechant@ubuntu:service nginx reload

4. 指定配置文件

poechant@ubuntu:sudo ./sbin/nginx -c /usr/local/nginx/conf/nginx.conf

-c表示configuration,指定配置文件。

5. 查看 Nginx 版本

有两种能够查看 Nginx 的版本信息的参数。第一种以下:

poechant@ubuntu:/usr/local/nginx$ ./sbin/nginx -v
nginx: nginx version: nginx/1.0.0

另外一种显示的是详细的版本信息:

poechant@ubuntu:/usr/local/nginx$ ./sbin/nginx -V
nginx: nginx version: nginx/1.0.0
nginx: built by gcc 4.3.3 (Ubuntu 4.3.3-5ubuntu4) 
nginx: TLS SNI support enabled
nginx: configure arguments: --with-http_ssl_module --with-openssl=/home/luming/openssl-1.0.0d/

6. 检查配置文件是否正确

poechant@ubuntu:/usr/local/nginx$ ./sbin/nginx -t
nginx: [alert] could not open error log file: open() "/usr/local/nginx/logs/error.log" failed (13: Permission denied)
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
2012/01/09 16:45:09 [emerg] 23898#0: open() "/usr/local/nginx/logs/nginx.pid" failed (13: Permission denied)
nginx: configuration file /usr/local/nginx/conf/nginx.conf test failed

若是出现如上的提示信息,表示没有访问错误日志文件和进程,能够sudo(super user do)一下:

poerchant@ubuntu:/usr/local/nginx$ sudo ./sbin/nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

若是显示如上,则表示配置文件正确。不然,会有相关提示。

7. 显示帮助信息

poechant@ubuntu:/user/local/nginx$ ./sbin/nginx -h

或者:

poechant@ubuntu:/user/local/nginx$ ./sbin/nginx -?

以上这些涵盖了 Nginx 平常维护的全部基本操做,另外还有向 master 进程发送信号的相关命令,咱们会在后续看到。

初探nginx架构

进程模型

众所周知,nginx性能高,而nginx的高性能与其架构是分不开的。那么nginx到底是怎么样的呢?这一节咱们先来初识一下nginx框架吧。

nginx在启动后,在unix系统中会以daemon的方式在后台运行,后台进程包含一个master进程和多个worker进程。咱们也能够手动地关掉后台模式,让nginx在前台运行,而且经过配置让nginx取消master进程,从而可使nginx以单进程方式运行。

很显然,生产环境下咱们确定不会这么作,因此关闭后台模式,通常是用来调试用的,在后面的章节里面,咱们会详细地讲解如何调试nginx。因此,咱们能够看到,nginx是以多进程的方式来工做的,固然nginx也是支持多线程的方式的,只是咱们主流的方式仍是多进程的方式,也是nginx的默认方式。nginx采用多进程的方式有诸多好处,因此我就主要讲解nginx的多进程模式吧。

刚才讲到,nginx在启动后,会有一个master进程和多个worker进程。

master进程主要用来管理worker进程,包含:接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常状况下),会自动从新启动新的worker进程。而基本的网络事件,则是放在worker进程中来处理了。多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。worker进程的个数是能够设置的,通常咱们会设置与机器cpu核数一致,这里面的缘由与nginx的进程模型以及事件处理模型是分不开的。nginx的进程模型,能够由下图来表示:

nginx进程操做

在nginx启动后,若是咱们要操做nginx,要怎么作呢?

从上文中咱们能够看到,master来管理worker进程,因此咱们只须要与master进程通讯就好了。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进程发送信号同样了。

事件模型

如今,咱们知道了当咱们在操做nginx的时候,nginx内部作了些什么事情,那么,worker进程又是如何处理请求的呢?咱们前面有提到,worker进程之间是平等的,每一个进程,处理请求的机会也是同样的。当咱们提供80端口的http服务时,一个链接请求过来,每一个进程都有可能处理这个链接,怎么作到的呢?

首先,每一个worker进程都是从master进程fork过来,在master进程里面,先创建好须要listen的socket(listenfd)以后,而后再fork出多个worker进程。全部worker进程的listenfd会在新链接到来时变得可读,为保证只有一个进程处理该链接,全部worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该链接。当一个worker进程在accept这个链接以后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开链接,这样一个完整的请求就是这样的了。咱们能够看到,一个请求,彻底由worker进程来处理,并且只在一个worker进程中处理。

那么,nginx采用这种进程模型有什么好处呢?固然,好处确定会不少了。首先,对于每一个worker进程来讲,独立的进程,不须要加锁,因此省掉了锁带来的开销,同时在编程以及问题查找时,也会方便不少。其次,采用独立的进程,可让互相之间不会影响,一个进程退出后,其它进程还在工做,服务不会中断,master进程则很快启动新的worker进程。固然,worker进程的异常退出,确定是程序有bug了,异常退出,会致使当前worker上的全部请求失败,不过不会影响到全部请求,因此下降了风险。固然,好处还有不少,你们能够慢慢体会。

nginx事件处理

上面讲了不少关于nginx的进程模型,接下来,咱们来看看nginx是如何处理事件的。

有人可能要问了,nginx采用多worker的方式来处理请求,每一个worker里面只有一个主线程,那可以处理的并发数颇有限啊,多少个worker就能处理多少个并发,何来高并发呢?非也,这就是nginx的高明之处,nginx采用了异步非阻塞的方式来处理请求,也就是说,nginx是能够同时处理成千上万个请求的。

想一想apache的经常使用工做方式(apache也有异步非阻塞版本,但因其与自带某些模块冲突,因此不经常使用),每一个请求会独占一个工做线程,当并发数上到几千时,就同时有几千的线程在处理请求了。这对操做系统来讲,是个不小的挑战,线程带来的内存占用很是大,线程的上下文切换带来的cpu开销很大,天然性能就上不去了,而这些开销彻底是没有意义的。

为何nginx能够采用异步非阻塞的方式来处理呢,或者异步非阻塞究竟是怎么回事呢?

咱们先回到原点,看看一个请求的完整过程。首先,请求过来,要创建链接,而后再接收数据,接收数据后,再发送数据。具体到系统底层,就是读写事件,而当读写事件没有准备好时,必然不可操做,若是不用非阻塞的方式来调用,那就得阻塞调用了,事件没有准备好,那就只能等了,等事件准备好了,你再继续吧。

阻塞调用会进入内核等待,cpu就会让出去给别人用了,对单线程的worker来讲,显然不合适,当网络事件越多时,你们都在等待呢,cpu空闲下来没人用,cpu利用率天然上不去了,更别谈高并发了。好吧,你说加进程数,这跟apache的线程模型有什么区别,注意,别增长无谓的上下文切换。因此,在nginx里面,最忌讳阻塞的系统调用了。

不要阻塞,那就非阻塞喽。非阻塞就是,事件没有准备好,立刻返回EAGAIN,告诉你,事件还没准备好呢,你慌什么,过会再来吧。好吧,你过一会,再来检查一下事件,直到事件准备好了为止,在这期间,你就能够先去作其它事情,而后再来看看事件好了没。虽然不阻塞了,但你得不时地过来检查一下事件的状态,你能够作更多的事情了,但带来的开销也是不小的。

因此,才会有了异步非阻塞的事件处理机制,具体到系统调用就是像select/poll/epoll/kqueue这样的系统调用。它们提供了一种机制,让你能够同时监控多个事件,调用他们是阻塞的,但能够设置超时时间,在超时时间以内,若是有事件准备好了,就返回。

这种机制正好解决了咱们上面的两个问题,拿epoll为例(在后面的例子中,咱们多以epoll为例子,以表明这一类函数),当事件没准备好时,放到epoll里面,事件准备好了,咱们就去读写,当读写返回EAGAIN时,咱们将它再次加入到epoll里面。这样,只要有事件准备好了,咱们就去处理它,只有当全部事件都没准备好时,才在epoll里面等着。这样,咱们就能够并发处理大量的并发了,固然,这里的并发请求,是指未处理完的请求,线程只有一个,因此同时能处理的请求固然只有一个了,只是在请求间进行不断地切换而已,切换也是由于异步事件未准备好,而主动让出的。这里的切换是没有任何代价,你能够理解为循环处理多个准备好的事件,事实上就是这样的。

与多线程相比,这种事件处理方式是有很大的优点的,不须要建立线程,每一个请求占用的内存也不多,没有上下文切换,事件处理很是的轻量级。并发数再多也不会致使无谓的资源浪费(上下文切换)。更多的并发数,只是会占用更多的内存而已。 我以前有对链接数进行过测试,在24G内存的机器上,处理的并发请求数达到过200万。如今的网络服务器基本都采用这种方式,这也是nginx性能高效的主要缘由。

咱们以前说过,推荐设置worker的个数为cpu的核数,在这里就很容易理解了,更多的worker数,只会致使进程来竞争cpu资源了,从而带来没必要要的上下文切换。并且,nginx为了更好的利用多核特性,提供了cpu亲缘性的绑定选项,咱们能够将某一个进程绑定在某一个核上,这样就不会由于进程的切换带来cache的失效。像这种小的优化在nginx中很是常见,同时也说明了nginx做者的苦心孤诣。好比,nginx在作4个字节的字符串比较时,会将4个字符转换成一个int型,再做比较,以减小cpu的指令数等等。

 
 
 

https://www.jianshu.com/p/630e2e1ca57f?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-friends

相关文章
相关标签/搜索