1、Nginx的基本介绍
Nginx 就是一个服务器软件,它是俄罗斯人编写的十分轻量级的 HTTP服务器,它的发音为“engine X”,是一个高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/SMTP 代理服务器。
其特点是占有内存少,并发能力强,易于开发,部署方便。Nginx 支持多语言通用服务器。Nginx 专为性能优化而开发,能够经受高负载的考验,有报告表名能支持高达 50000 个并发连接数。
Nginx 启动特别容易,并且几乎可以做到7*24不间断运行,即使运行数个月也不需要重新启动。你还能够不间断服务的情况下进行软件版本的升级。
缺点:Nginx 只适合静态和反向代理。
优点:负载均衡、反向代理、处理静态文件优势。Nginx 处理静态请求的速度高于Apache。
Nginx有动态分离机制,静态请求直接就可以通过Nginx处理,动态请求才转发请求到后台交由Tomcat进行处理。
2、Nginx的相关概念
2.1、正向代理和反向代理
正向代理即是客户端代理,代理客户端,服务端不知道实际发起请求的客户端。反向代理即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端。
正向代理类似一个跳板机,代理访问外部资源。比如我们国内访问谷歌,直接访问访问不到,我们可以通过一个正向代理服务器,请求发到代理服,代理服务器能够访问谷歌,这样由代理去谷歌取到返回数据,再返回给我们,这样我们就能访问谷歌了。
正向代理即是客户端代理,代理客户端,服务端不知道实际发起请求的客户端。
正向代理的用途:
(1)访问原来无法访问的资源,如google
(2) 可以做缓存,加速访问资源
(3)对客户端访问授权,上网进行认证
(4)代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息
反向代理(Reverse Proxy)实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。
反向代理即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端。
反向代理时,客户端对代理是无感知的,因为客户端无需任何配置。客户端只需将请求发送给反向代理服务器,由反向代理服务器发送请求给目标服务器并获取数据,再返回给客户端。此时反向代理服务器和目标服务器对外就像是一个服务器,暴露的是代理服务器的地址,隐藏了真实服务器的 ip 地址。
反向代理的作用:
(1)保证内网的安全,阻止web攻击,大型网站,通常将反向代理作为公网访问地址,Web服务器是内网
(2)负载均衡,通过反向代理服务器来优化网站的负载
参考:https://www.cnblogs.com/taostaryu/p/10547132.html
2.2、负载均衡
负载均衡,英文名称为Load Balance,其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务。形象来说就是,单个服务器解决不了,我们就增加服务器的数量,然后将请求分发到各个服务器上,将原先请求集中到单个服务器上的情况改为将请求分发到多个服务器上,将负载分发到不同服务器上。
2.3、动静分离
Nginx有动态分离机制,静态请求直接就可以通过 Nginx 处理,动态请求才转发请求到后台交由 Tomcat 进行处理。动静分离简单来说就是把动态请求和静态请求分开,可以理解为使用 Nginx 处理静态页面,tomcat 处理动态页面。动静分离可以简单理解为:动态文件与静态文件的分离。
动静分离将网站静态资源(HTML,JavaScript,CSS,img等文件)与后台应用分开部署,提高用户访问静态代码的速度,降低对后台应用访问。比如,我们可以将静态资源放到nginx中,动态资源转发到tomcat服务器中。
如果不是动静分离,即将静态资源(html、css、js)和动态资源(jsp、servlet)都放在同一台服务器上,对服务器的压力会很大。而如果采用动静分离,则可以将动态资源与静态资源分离,不部署在同一台服务器上,减轻服务器的压力。
3、Nginx的安装
nginx是C语言开发,建议在linux上运行。
先安装Linux系统,看参考:https://www.cnblogs.com/wenxuehai/p/14966942.html
安装好后,利用远程攻击 putty 连接 Linux 系统。
3.1、安装依赖
先安装依赖 gcc、pcre-devel、zlib-devel、openssl-devel,通过 putty 连接 Linux 后,直接在命令行输入以下命令进行安装:
yum -y install gcc pcre-devel zlib-devel openssl openssl-devel
可能会提示需要 root 权限,此时是因为你没有使用 root 用户连接 Linux,只需退出重新连接,通过 root 用户来连接即可,该用户的密码一般是 123456。
3.2、下载上传Nginx
先下载 Nginx,下载连接:https://nginx.org/download/,比如我们下 1.12.2 版本:
然后通过 PuTTY 提供的 PSCP 工具来实现将本地 window 系统的文件上传到 Linux 系统上,将 Nginx 压缩包上传到 Linux 上。
先找到 pscp.exe 程序的所在位置,一般跟 putty.exe 程序在同一目录,在该目录下打开命令行,输入以下命令:
pscp 本地文件完整路径 用户名@Linux系统ip:目录 -- 示例如下: pscp f: ginx-1.12.2.tar.gz root@192.168.32.128:/usr/src
或者可以通过 SSH Secure File Transfer Client 软件来上传文件到 Linux 上,更加方便
3.3、解压和安装
上传成功后进行解压,注意,使用 putty 的连接而不是 pscp 的连接来进行解压。
先切换到 Nginx 的目录下,即 /usr/src,然后执行解压命令:
cd /usr/src tar -xvf nginx-1.12.2.tar.gz
解压之后,进入到解压后的 Nginx 目录,执行安装命令。依次执行以下命令:
cd nginx-1.12.2/ ./configure make && make install
3.4、检验安装是否成功
安装成功之后,可以在 /usr/local 目录下看到 nginx 目录
切换到该目录下,启动 Nginx:
cd /usr/local/nginx/sbin ./nginx -- 启动nginx
启动过后,在本地浏览器(即你的 window 系统的浏览器)中通过 ip 访问即可访问到 Nginx:
因为防火墙的问题,在 window 系统中访问 Linux 系统的 Nginx 默认是无法访问的。此时我们可以将防火墙关闭或者是开启访问的 80 端口号即可。
下面我们来开启 80 端口号,可以在服务器中执行如下命令来验证 80 端口号是否已开启:
firewall-cmd --query-port=80/tcp
提示 no 信息,即表示 80 端口未开启,下面我们开启80端口:
firewall-cmd --add-port=80/tcp --permanent // 重启防火墙 systemctl restart firewalld
--permanent #永久生效,没有此参数重启后失效
示例:
4、Nginx的常用命令
要想使用 Nginx 的一些命令,连接到 Nginx 后,需要切换到 Nginx 的脚本目录下:
cd /usr/local/nginx/sbin
4.1、查看Nginx的版本
查看 nginx 的版本可以用 ./nginx -v 命令:
4.2、查看Nginx的状态
查看 Nginx 进程的状态的命令:
ps -ef | grep nginx
Nginx 已启动时的状态:
Nginx 关闭时的状态:
4.2、启动Nginx
启动命令:
./nginx
4.3、关闭Nginx
关闭命令:
./nginx -s stop
4.4、重新加载Nginx
在我们修改 Nginx 的配置文件后,可以重启来让它重新加载配置文件,但也可以不重启,直接让它重新加载即可,此时用重新加载命令即可:
./nginx -s reload
5、Nginx的配置文件
Nginx 的配置文件在 usr/local/nginx/conf 目录下,在该目录可以看到 nginx.conf 文件,该文件就是 Nginx 的配置文件:
默认的 nginx.conf 文件如下:
#user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ .php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ .php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /.ht { # deny all; #} } # another virtual host using mix of IP-, name-, and port-based configuration # #server { # listen 8000; # listen somename:8080; # server_name somename alias another.alias; # location / { # root html; # index index.html index.htm; # } #} # HTTPS server # #server { # listen 443 ssl; # server_name localhost; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 5m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # location / { # root html; # index index.html index.htm; # } #} }
nginx.conf 可以看做是由三个部分组成,main、events 和 http。
main(全局设置)、server(主机设置)、upstream(负载均衡服务器设置)和 location(URL匹配特定位置的设置)。
- main块设置的指令将影响其他所有设置;
- server块的指令主要用于指定主机和端口;
- upstream指令主要用于负载均衡,设置一系列的后端服务器;
- location块用于匹配网页位置。
这四者之间的关系式:server继承main,location继承server,upstream既不会继承其他设置也不会被继承。
在这四个部分当中,每个部分都包含若干指令,这些指令主要包含Nginx的主模块指令、事件模块指令、HTTP核心模块指令,同时每个部分还可以使用其他HTTP模块指令,例如Http SSL模块、HttpGzip Static模块和Http Addition模块等。
5.1、全局块(main)
全局块主要是从配置文件到 events 块之间的内容,即最外围的内容。主要是用来设置一些影响 Nginx 服务器整体运行的配置指令。主要包括配置运行 Nginx 服务器的用户(组)、允许生成的 worker process 数,进程 PID 存放路径、日志存放路径和类型以及配置文件的引入等。main块设置的指令将影响其他所有设置。
比如上面的:
worker_processes 1;
这是 Nginx 服务器并发处理服务的关键配置, worker_process 值越大,可以支持的并发处理量也越多,但是会受到硬件、软件等设备的制约。
worker_processes是个主模块指令,指定了Nginx要开启的进程数。每个Nginx进程平均耗费10M~12M内存。建议指定和CPU的数量一致即可。
5.2、events块
events 块主要影响 Nginx 服务器与用户的网络连接,常用的设置包括是否开启对多 work process 下的网络连接进行序列化,是否允许同时接收多个网络连接,选取哪种事件驱动模型来处理连接请求,每个 work process 可以同时支持的最大连接数等。
比如上面的:
events { worker_connections 1024; }
表示每个 work process 支持的最大连接数是 1024。worker_connections 用来定义 Nginx 每个进程的最大连接数,默认是1024。
最大客户端连接数由 worker_processes 和 worker_connections 决定,即 Max_client = worker_processes * worker_connections。
5.3、HTTP块
这里是 Nginx 中配置最频繁的部分,代理、缓存和日志定义等绝大多数功能和第三方模块的配置都在这里进行配置。
6、Nginx的工作原理
Nginx 默认采用多进程工作方式,Nginx启动后,会运行一个master进程和多个worker进程。其中master充当整个进程组与用户的交互接口,同时对进程进行监护,管理worker进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。worker用来处理基本的网络事件,worker之间是平等的,他们共同竞争来处理来自客户端的请求。
Nginx在启动后,会有一个master进程和多个worker进程。
nginx的进程模型:
6.1、master进程(管理进程)
master进程主要用来管理worker进程,具体包括如下4个主要功能:
(1)接收来自外界的信号。
(2)向各worker进程发送信号。
(3)监控woker进程的运行状态。
(4)当woker进程退出后(异常情况下),会自动重新启动新的woker进程。
用户交互接口:master进程充当整个进程组与用户的交互接口,同时对进程进行监护。它不需要处理网络事件,不负责业务的执行,只会通过管理worker进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。
重启work进程:我们要控制nginx,只需要通过kill向master进程发送信号就行了。比如kill -HUP pid,则是告诉nginx,从容地重启nginx,我们一般用这个信号来重启nginx,或重新加载配置,因为是从容地重启,因此服务是不中断的。
直接给master进程发送信号,这是比较传统的操作方式,nginx在0.8版本之后,引入了一系列命令行参数,来方便我们管理。比如,./nginx -s reload,就是来重启nginx,./nginx -s stop,就是来停止nginx的运行。如何做到的呢?我们还是拿reload来说,我们看到,执行命令时,我们是启动一个新的nginx进程,而新的nginx进程在解析到reload参数后,就知道我们的目的是控制nginx来重新加载配置文件了,它会向master进程发送信号,然后接下来的动作,就和我们直接向master进程发送信号一样了。
6.2、worker进程(处理请求)
而基本的网络事件,则是放在worker进程中来处理了。多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个worker进程中处理,一个worker进程不可能处理其它进程的请求。
worker进程的个数是可以设置的,一般我们会设置与机器cpu核数一致。每个 worker 的线程可以把一个 CPU 的性能发挥到极致,所以 worker数和服务器的 CPU 数相等是最合适的。设少了会浪费 CPU,设多了会造成 CPU 频繁切换上下文带来的损耗。
worker进程之间是平等的,每个进程,处理请求的机会也是一样的。当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接,怎么做到的呢?
Nginx采用异步非阻塞的方式来处理网络事件,类似于Libevent,具体过程如下:
1)接收请求:首先,每个worker进程都是从master进程fork过来,在master进程建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。所有worker进程的listenfd会在新连接到来时变得可读,每个work进程都可以去accept这个socket(listenfd)。当一个client连接到来时,所有accept的work进程都会受到通知,但只有一个进程可以accept成功,其它的则会accept失败。为保证只有一个进程处理该连接,Nginx提供了一把共享锁accept_mutex来保证同一时刻只有一个work进程在accept连接。所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。
2)处理请求:当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。
我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。worker进程之间是平等的,每个进程,处理请求的机会也是一样的。
6.3、Nginx多进程模型的好处
首先,对于每个worker进程来说,独立的进程,不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题查找时,也会方便很多。
其次,采用独立的进程,可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断,master进程则很快启动新的worker进程。当然,worker进程的异常退出,肯定是程序有bug了,异常退出,会导致当前worker上的所有请求失败,不过不会影响到所有请求,所以降低了风险。
6.4、Nginx支持的并发数
单个进程的连接数有上限,Nginx 上配置单个 Worker 进程的最大连接数:worker_connections ,上限为 nofile。Nginx 上配置 Worker 进程的数量:worker_processes。
- Nginx 的最大连接数:Nginx 的最大连接数 = Worker 进程数 * 单个 Worker 进程的最大连接数。
- Nginx 的最大并发数:Nginx 作为反向代理服务器时,每个请求建立1条连接,所以此时最大的并发数 = 最大连接数。Nginx 作为反向代理时,会建立 Client 的连接和后端 Web Server 的连接,需要占用 2 个连接,此时最大并发数 = 最大连接数 / 2。
nginx采用了异步非阻塞的方式来处理请求,nginx 是可以同时处理成千上万个请求的,一个worker进程可以同时处理的请求数只受限于内存大小,而且在架构设计上,不同的worker进程之间处理并发请求时几乎没有同步锁的限制,worker进程通常不会进入睡眠状态,因此,当Nginx上的进程数与CPU核心数相等时(最好每一个worker进程都绑定特定的CPU核心),进程间切换的代价是最小的。
对于Nginx来讲,一个进程只有一个主线程,那么它是怎么实现高并发的呢?Nginx 采用了IO多路复用的原理,通过异步非阻塞的事件处理机制,实现了轻量级和高并发。
简单来说:每进来一个request,会有一个worker进程去处理。但不是全程的处理,处理到什么程度呢?处理到可能发生阻塞的地方,比如向上游(后端)服务器转发request,并等待请求返回。那么,这个处理的worker不会这么傻等着,他会在发送完请求后,注册一个事件:“如果upstream返回了,告诉我一声,我再接着干”。于是他就休息去了。此时,如果再有request 进来,他就可以很快再按这种方式处理。而一旦上游服务器返回了,就会触发这个事件,worker才会来接手,这个request才会接着往下走。由于web server的工作性质决定了每个request的大部份生命都是在网络传输中,实际上花费在server机器上的时间片不多,这就是几个进程就能解决高并发的秘密所在。
为什么 Nginx 不使用多线程?
Apache: 创建多个进程或线程,而每个进程或线程都会为其分配 cpu 和内存(线程要比进程小的多,所以worker支持比perfork高的并发),并发过大会耗光服务器资源。
Nginx: 采用单线程来异步非阻塞处理请求(管理员可以配置Nginx主进程的工作进程的数量)(epoll),不会为每个请求分配cpu和内存资源,节省了大量资源,同时也减少了大量的CPU的上下文切换。所以才使得Nginx支持更高的并发。