GimmeS 2020-01-01
nginx(engine x)
是一个高性能的http和反向代理服务器,具有占有内存少、并发能力强等特点,其性能在同类型的网页服务器中表现较好。在中国有大量的网站用户都在使用nginx,比如:京东、新浪、网易等等。当然淘宝也是,不过淘宝基于nginx进行了修改,现在用的是Tengine。
nginx可以作为静态页面的web服务器,同时还支持许多主流编程语言。nginx专门为性能优化而开发,经过测试证明nginx最大能并发支持50000个连接
nginx百科介绍就是可以作为反向代理服务器,那么什么是反向代理呢?不过在介绍反向代理之前,我们先了解一下正向代理
正向代理
举个通俗的例子,当我们想访问某个资源的时候没办法访问,而是需要通过代理服务器来访问,那么这种代理服务就称之为正向代理
事实上,我们挂威批N
去访问谷歌(任意网站)
的时候,都是先向代理服务器发送请求,然后代理服务器将我们的请求转发给谷歌,然后谷歌将响应的结果返回给代理服务器,然后代理服务器再返回给我们。所以我们的资源实际上是从代理服务器那里获取的,而谷歌接收到的也是代理服务器的请求,我们和谷歌之间没有任何的交互。
反向代理
此时的nginx所在的服务器就是反向代理服务器,假设我们部署了多个服务,用户并不会直接访问部署服务的机器、或者说目标服务器,而是会访问反向代理服务器,反向代理服务器会将请求转发到目标服务器上(怎么转化就涉及到了负载均衡)
,目标服务器返回数据给反向代理服务器,然后反向代理服务器再将内容返回给用户。因此此时反向代理服务器和目标服务器对外就相当于是一个整体,那么暴露给用户的就是反向代理服务器的地址,隐藏了真实服务器的ip地址。
早期访问量不大的时候,都是用户直接访问服务器,服务器处理请求,比如从数据库里面拿数据等等,处理完毕之后,服务器再将结果返回给客户端。但是这种方式只是适用于并发量比较小的情况,如果并发量一多,就会陷入瓶颈。即便是增加机器的硬件配置,对于现如今这个信息量庞大的社会来说,单台机器也是杯水车薪。
因此我们需要采用多台机器的方式,增加服务器的数量。将请求分发到不同的机器上,不会让某台机器承受的负载过重,这就是所谓的覆盖均衡
。
n个请求,理想的情况就是每台服务器都负责n/3个请求,当然事实情况不一定完全相等,但也会尽量使得每台机器上的负载一样。
有些请求是访问服务器的某个静态资源,比如我就想查看某一张图片等等,那么直接把图片返回即可,该资源不涉及和其它组件的交互;而那些需要访问缓存、数据库等等则称之为动态资源。因此为了加快网站的解析速度,会把动态资源和静态资源交给不同的服务器来解析,从而降低单个服务器的压力,并且分工更明确。而返回静态资源的任务正好可以交给nginx
nginx除了能将请求转发到目标服务器之外,还指向了一台存放静态资源(html、css、图片等等)
的服务器,如果用户访问的是静态资源,那么nginx直接从静态资源的服务器将静态资源拿出来返回给用户即可,如果访问动态资源,然后再通过负载均衡转发到目标服务器。
安装nginx我们只介绍如何在linux上安装,因为nginx底层用到了epoll,而这个Windows是不支持的。
直接通过docker?pull?nginx
拉取nginx镜像即可。
我们来看看服务是否可用。
docker安装是成功了的。
如果机器上有一个docker的话,安装nginx很方便。但是我们为了学习nginx,还是要知道nginx直接在linux机器上怎么安装,我们后面讲解的时候也会按照普通安装的方式来讲解。因为学习的是nginx,如果涉及到docker也不太好,毕竟用到了另外一项技术。熟悉nginx的时候,可以推荐docker直接一键部署。
nginx安装的话,直接去http://nginx.org/
去下载即可
我们注意到上面有三个版本,mainline?version
是增在主力开发的版本,stable?version
是最新稳定版本,而legacy?version
则是以前的稳定的老版本。我们这里下载最新稳定版,nginx-1.16.1
但是注意的是,nginx需要依赖,否则编译nginx的时候会报错。安装nginx的依赖直接通过yum install gcc gcc-c++ make automake autoconf libtool pcre* zlib openssl openssl-devel
安装即可。
安装完依赖之后,解压tar包,进入到nginx目录。
执行./configure
,然后执行make?&&?make install
然后进入到/usr/local
目录,会发现一个名为nginx的目录。因为编译完之后,会默认安装到/usr/local
目录下面。
我们进入到这个目录
然后在sbin目录里面会有一个nginx可执行程序。
我们把之前的docker服务停掉,启动现在的nginx,看看能不能访问。
依旧可以访问,证明安装并且启动成功。
另外,可能会因为防火墙的原因,使得端口未对外开方,导致无法访问,这时候我们需要开放需要让外界访问的端口号。我这里使用的是阿里云,因此可以通过阿里提供的web页面的方式开放端口,如果通过命令的话该怎么做呢?
firewall-cmd --list-all:查看所有开放的端口号
firewall-cmd --add-service=http --permanent
firewall-cmd --add-port=80/tcp --permanent:设置要开放的端口号
firewall-cmd --reload:重启防火墙
nginx -v:查看nginx当前的版本号
nginx:启动nginx
nginx -s stop:关闭nginx
nginx -s reload:重新加载nginx,如果配置文件修改了,那么无需重启nginx,可以通过重新加载的方式。
我们来看看nginx的配置文件长什么样子,nginx的配置文件在nginx目录下的conf目录下,名字为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的配置文件分为三部分
第一部分:全局块
从配置文件开始到events块之间的内容,主要会设置一些影响nginx服务器整体运行的配置指令,主要包括配置运行nginx服务器的用户(组)、允许生成work process数,进程pid的存放路径,日志存放路径和类型,以及配置文件的引入等等。
#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;
比如这里的worker_processes,这是nginx并发处理服务的关键配置,该值越大,可以支持的并发处理处理也越多,但是会受到硬件、软件等设备的制约。
第二部分:events块
events { worker_connections 1024; }
events块主要设置nginx服务器与用户的网络连接,常用的设置包括是否开启对多worker process下的网络连接进行序列化,是否允许同时接收多个网络连接,选取哪种事件驱动模型来处理连接请求,每个work process可以同时支持的最大连接数。
worker_connections 1024;就表示每个work process支持的最大连接数是1024。这部分的配置对nginx的性能影响较大,在实际中应该灵活配置。
第三部分:http块
这算是nginx中最频繁配置的部分,代理、缓存、日志定义依旧我们说的负载均衡、动静分离等绝大部分功能都是在这里面进行配置的。另外,http块也可以分为http全局块和server块
http全局块配置的指令包括文件的引入,MIME-TYPE定义,日志自定义,连接超时时间,单链接请求数上限等等。
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;
这一部分就是http全局块
而server块就是我们改动最频繁的部分了
server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; }
listen 80表示nginx监听80端口,server_name表示监听的主机为本机。实际上这一部分也可以叫做server全局块,而下面是location块
location /则表示当你访问的路径是/的时候,进行相应的处理,怎么处理我们后面会详细介绍。我们很多时候都只是使用nginx做一层转发,而转发所进行的配置就是配置location。
需求:
当我打开浏览器,输入阿里云的ip的时候,会自动跳转到百度页面。
修改配置文件:
我们只需要加上一行proxy_pass?http://www.baidu.com
,由于该机器监听的是80端口,而浏览器默认访问80端口,那么当我们直接输入ip地址访问该机器的时候,会自动转发到百度。然后不要忘记nginx -s?reload
,为了方便,可以办nginx的sbin目录配置到环境变量,我这里就不配了。
当我输入阿里云的ip地址的时候,自动跳转到了百度,就是nginx帮我们做了转发。而原来是没有proxy_pass
这一行的,当没有这一行的时候,会来到nginx的指定页面。对,那个root?html
就是指nginx的主目录下的html目录,index?index.html?index.htm
就是html目录下的文件,当没有指定proxy_pass
的时候,就会到html目录下面找index选项指定的文件。
这里我们把proxy_pass这一项给去掉,然后让nginx访问我们自己指定的页面,假设访问satori.html
,那么显然此时的location就应该这么配。
location / { root /root; index satori.html index.html index.htm; }
配置中有有三个html文件,会依次从左向右查找。我把配置文件给改掉了,但是/root
目录下还没有satori.html
这个文件,所以出现了403。
下面我们就来写一个这样的文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1 style="text-align: center">古明地さとりの避難小屋へようこそ</h1> </body> </html>
丢进去再次访问,会发现依旧报出了403,这是因为我们看到全局块中是#user nobody;
,这个user表示nginx的启动用户,但是这行被注释掉了,表示nginx没有权限。一种解决办法就是把#user
这个注释打开,然后把用户换成/root
这个目录的所属用户,或者说是有权限读的用户。另一种办法就是chmod?-R?755?/root
,让这个目录所有用户都有权限执行。我们使用chmod命令修改权限,之后再来访问,会发现访问成功。
需求:
访问不同的路径,跳转到不同的url。比如当我输入ip/satori
的时候跳转到bilibili页面,输入ip/koishi
的时候跳转到知乎页面。显然此时就需要两个location了,因为这是两个不同的url
修改配置文件:
# 这个原来的location,就还让它保持原样 location / { root html; index index.html index.htm; } location /satori { proxy_pass http://www.bilibili.com; } location /koishi{ proxy_pass http://www.zhihu.com; }
首先访问成功了,但是我们看到指定的location的路径参数(ip:port或者http://www.xxx.com后面的那部分内容,我习惯称之为路径参数。)
会自动跟在要映射的url后面。就像我们要将/satori
映射到http://www.bilibili.com
,但是当我们输入/satori
的时候,这个/satori
会跟在http://www.bilibili.com
的后面,也就是http://www.bilibili.com/satori
,同理知乎也是。但是如果我们访问的路径参数很多呢?
显然从图中我们可以看到,不管访问的url有多长,只要location和访问的url从头开始对比是相同的,比如这里的/koishi
匹配上了,那么不管后面还跟着多少参数,都会加在http://www.zhihu.com
的屁股后面。
新需求:
我们目前算是完成了url的转发,但是我们发现这里貌似访问的都是80端口。这个时候,如果我希望访问阿里云主机的90端口,让其跳转到百度呢?
修改配置文件:
如果仔细看一下配置文件,我们发现之前配置的多个location实际上都是在一个server下面的。而一个server只能监听一个端口,一个server可以配置多个location,不管什么url,只要你访问的是server指定的端口,那么就会匹配对应server端口下面的location,如果匹配上了进行转发。但是现在问题是,我们不访问80端口了,而是要访问90端口,那么显然就再需要一个server了,让这个server监听90端口,然后在监听90端口的server下,配置location。我们来试一下
我们看到nginx已经给我提供了多个server,但是这些server是被注释掉的,所以我们可以把注释打开,然后修改,但是我们也可以自己写一个server,粘贴过去
server { # 监听90端口 listen 90; # 主机依旧是本地 server_name localhost; # 配置location location / { proxy_pass http://www.baidu.com; } }
我们看到nginx已经给我提供了多个server,但是这些server是被注释掉的,所以我们可以把注释打开,然后修改,但是我们也可以自己写一个server,粘贴过去
我们看到此时我们访问的都只是ip,所以匹配/
,都没有任何路径参数,但是却得到了不同的结果,就是因为端口不一样。我们访问80端口,只会匹配监听80端口的server下的location,同理90端口也是,因此不同端口下的location是互不影响的。
需求:
现在我要求,只要访问的url中包含了satori,就跳转到bilibili。就是说url可以是ip/satori,ip/aaa/satori,ip/aaa/bbb/c/satori/aa
,只要路径参数中包含了satori,那么就跳转到bilibili,怎么做呢?
修改配置文件:
这种做法显然要使用正则来实现,对于我们上面的需求,location使用正则简直不要太好配,直接~?/satori/
即可,此时同样匹配上之后,所有的路径参数都会跟在跳转的url的后面。
此时就配好啦,访问/a/b/c/satori/abc
只要路径有/satori
,那么就能匹配成功,另外这里面跳转的url是不能够有路径参数的
关于nginx中的正则,我们见到了,那么location还支持一下几种方式。
=:表示url不包含正则表达式,要求请求的url和location指定的url完全一致。
~:url包含正则表达式,并且区分大小写
**~*:url包含正则表达式,并且不区分大小写**
^~:url不包含正则表达式,要求nginx找到请求的url和location指定的url相似度最后的location,然后用该location进行请求处理。
需求:
假设我们有两台服务器,我们需要实现当访问ip:90/satori
的时候,能够把请求转发到不同的服务器当中去,也就是实现负载均衡的效果
修改配置文件:
这里就要修改http全局块,不记得这是哪一部分的可以回头看看。
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; # 我们需要加上一个upstream,然后给upstream起一个名字 # 假设就叫my_server upstream my_server { # 写上你要转发的机器 server 1.1.1.1:5555; server 2.2.2.2:6666; }
然后修改location
server { # 监听90端口 listen 90; # 主机依旧是本地 server_name localhost; # 配置location location /satori { # 这里将proxy_pass改成http://my_server即可,这个my_server就是给upstream起的名字 proxy_pass http://my_server; } }
随着互联网信息的爆炸式增长,负载均衡(load balance)已经不再是一个陌生的话题,就是把负载分摊到不同的存储单元,既保证服务的可用性,又保证服务足够快。但是负载均衡只是平摊负载吗?如果有的机器性能好,那么它的负载是不是应该要多一些呢?所以nginx在负载均衡方面有如下策略:
轮询(默认):
每个请求按照时间顺序逐一分配到不同的后端服务器,如果后端服务器宕机了,会自动剔除。
weight(权重):
权重越高,被分配的请求越多,权重与请求数成正比。默认是1,比如 server 1.1.1.1:80 weight=10,此时1.1.1.1这台服务器的权重就是10
ip_hash:
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题
upstream my_server { ip_hash; server 1.1.1.1:80; server 2.2.2.2:88; }
fair:
按照服务器的响应时间来分配,响应时间短的优先分配
upstream my_server { server 1.1.1.1:80; server 2.2.2.2:88; fair; }
nginx动静分离简单来说,就是把动态和静态请求分开,不能简单理解成只是把动态页面和静态页面物理分离。严格来讲,是动态请求和静态请求分开,可以理解成使用nginx处理静态页面,uwsgi处理动态页面,动静分离从实现角度上大致分为两种:
纯粹把静态文件独立出来,放在一个单独服务器上,也是目前主流方案
把动态和静态文件放在一起,通过nginx来分开。
现在在我/root/bg
目录下有很多图片,下面我们就进行配置,通过nginx直接访问静态资源。我们希望当输入/pic/1.png
的时候,就会访问/root/bg
下面的1.png
。可能有人觉得这个是不是做过类似的,之前弄那个satori.html
的时候好像弄过,确实如此。
server { listen 80; server_name localhost; # 配置location location /pic { root /root/bg; # 当我们访问/pic/1.png,那么会返回/root/bg/1.png # 而这个autoindex指的是当输入/pic的时候,会把/root/bg里面的内容全部列出来,像索引一样 # 可以通过点击的方式访问指定的静态资源 autoindex on; } }
。。。。。。。。。。。。
某些公司会墙特定网站,如果你有一个可访问的域名和服务器,就可以通过nginx反向代理来来解决这些问题。比如现在我们用mirror.example.com镜像www.baidu.com,以下是详细操作。