四、Nginx虚拟主机
4.1虚拟主机的分类
虚拟主机是一种特殊的软硬件技术,它可以将网络上的每一台计算机分成多个虚拟主机,每个虚拟主机可以独立对外提供www服务,这样就可以实现一台主机对外提供多个web服务,每个虚拟主机之间是独立的,互不影响的。
基于IP的虚拟主机
通过不同的ip访问nginx里面不同的项目,在nginx配置文件nginx.conf中存在一个叫server的配置块,其实,一个server配置快就是一个虚拟主机。但是在工作中用的比较少。
基于端口的虚拟主机
基于域名的虚拟主机
4.2 基于单网卡多ip虚拟主机配置
通过不同的IP区分不同的虚拟主机,此类虚拟主机对应的企业应用非常少见,一般不同业务需要使用多IP的常见都会在负载均衡器上进行VIP绑定,而不是在Web上通过绑定IP区分不同的虚拟机。
需求
一台Linux服务器绑定两个ip:192.168.66.100、192.168.66.103 访问不同的ip请求不同的html目录,即: 访问http://192.168.66.100将访问“html100”目录下的html网页 访问http://192.168.66.103将访问“html103”目录下的html网页
Linux绑定多IP
Linux操作系统允许绑定多IP。是在一块物理网卡上可以绑定多个lP地址。这样就能够在使用单一网卡的同一个服务器上运行多个基于IP的虚拟主机。但是在绑定多IP时需要将动态的IP分配方式修改为静态的指定IP。
#进入网卡的配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33 #在配置文件中绑定多个ip IPADDR0=192.168.66.100 IPADDR1=192.168.66.103 #使配置文件生效 service network restart #查看该虚拟机绑定的ip ip addr
在nginx服务器下创建两个虚拟主机的资源
mkdir html100 mkdir html103
#在html100文件夹下创建index.html文件,文件内容为this is html100 echo "this is html100" > index.html #同理在html103文件夹创建index.html文件 echo "this is html100" > index.html
修改Nginx的配置文件ngnix.conf完成基于IP的虚拟主机配置
vim /usr/local/nginx/conf/nginx.conf #一个Server就是一个虚拟主机 server { listen 80; server_name 192.168.66.100; #为虚拟机指定IP或者是域名 #主要配置路由访问信息 location / { root html100; #用于指定访问根目录时,访问虚拟主机的web目录 index index.html index.htm; #在不指定访问具体资源时,默认的展示资源的列表 } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } #一个Server就是一个虚拟主机 server { listen 80; #为虚拟机指定IP或者是域名 server_name 192.168.66.103; #主要配置路由访问信息 location / { #用于指定访问根目录时,访问虚拟主机的web目录 root html103; #在不指定访问具体资源时,默认的展示资源的列表 index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
#重启nginx服务 [root@localhost sbin]# ./nginx -s reload
4.3 基于域名虚拟主机配置
需求
两个域名指向同一个nginx服务器,用户访问不同的域名时显示不同的内容。
域名规划:
1, http://www.qiantai.cn/ => 前台
2, http://www.houtai.cn/ =》 后台
修改windows的hosts文件配置域名与ip的映射
文件路径:C:\Windows\System32\drivers\etc\hosts
192.168.66.100 www.qiantai.cn www.houtai.cn
在Nginx下创建两个文件夹分别叫qiantai和houtai
分别在两个文件夹中创建index.html文件
[root@localhost qiantai]# echo "this is qiantai" > index.html [root@localhost houtai]# echo "this is houtai" > index.html
修改nginx.conf配置文件
server { listen 80; #为虚拟机指定IP或者是域名 server_name www.qiantai.cn; #主要配置路由访问信息 location / { #用于指定访问根目录时,访问虚拟主机的web目录 root qiantai; #在不指定访问具体资源时,默认的展示资源的列表 index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } #一个Server就是一个虚拟主机 server { listen 80; #为虚拟机指定IP或者是域名 server_name www.houtai.cn; #主要配置路由访问信息 location / { #用于指定访问根目录时,访问虚拟主机的web目录 root houtai; #在不指定访问具体资源时,默认的展示资源的列表 index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } }
重启ngxin服务
[root@localhost sbin]# ./nginx -s reload
浏览器访问
4.4.基于多端口虚拟主机配置
通过不同的端口来区分不同的虚拟主机。此类虚拟主机对应的企业应用主要为公司内部的网站。
需求
Nginx对提供8888与9999两个端口的监听服务
请求8888端口则访问html8888目录下的index.html
请求9999端口则访问html9999目录下的index.html
还原IP地址为192.168.66.100:
vim /etc/sysconfig/network-scripts/ifcfg-ens33 #将: IPADDR0=192.168.66.100 IPADDR1=192.168.66.101 #改为 IPADDR=192.168.66.100 #重启网络服务 systemctl restart network
在在Nginx下创建两个文件夹分别叫html8888和html9999,在文件夹中创建index.html文件
修改Nginx的配置文件完成基于端口的虚拟主机配置
#一个Server就是一个虚拟主机 基于端口 server { listen 8888; #为虚拟机指定IP或者是域名 server_name 192.168.66.100; #主要配置路由访问信息 location / { #用于指定访问根目录时,访问虚拟主机的web目录 root html8888; #在不指定访问具体资源时,默认的展示资源的列表 index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } #一个Server就是一个虚拟主机 server { listen 9999; #为虚拟机指定IP或者是域名 server_name 192.168.66.100; #主要配置路由访问信息 location / { #用于指定访问根目录时,访问虚拟主机的web目录 root html9999; #在不指定访问具体资源时,默认的展示资源的列表 index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } }
重启nginx服务
[root@localhost nginx]# sbin/nginx -s reload
浏览器访问
五、Nginx核心指令
5.1 root和alias指令区别
区别
- 共同点 : root和alias两者都都是用来指定URI和磁盘文件的映射关系;
- 区别: root会将定义路径与URI叠加;而alias只取定义路径;
root示例
在之前的文章中介绍过,nginx的配置文件中的root表示的是目标资源的根路径。
server { listen 80; server_name 192.168.66.100; location /aaa{ root /usr/html/; index index.html; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } }
是不是访问/aaa/aaa.html就能访问到/usr/html/aaa.html这个文件呢?答案是否定的!
因为,使用root指令时,会root会将定义路径与URI叠加。也就是说当我们访问/aaa/aaa.html时,实际上访问的资源路径是/usr/html/aaa/aaa.html ,但实际上根本没有aaa这层目录,所以访问不到aaa.html.
如何修改呢?只需要将/aaa改为/html,并删除root指令中的/html即可。
location /html { root /usr; index index.html; } #重启nginx [root@localhost sbin]# ./nginx -s reload
alias示例
alias不会拼接路径,访问uri就能定位到root 指定的目录。
location /aaa { alias /usr/html/; #最后一定加上/ index index.html index.htm; } #重启nginx [root@localhost sbin]# ./nginx -s reload
5.2 return指令
return功能
- 停止处理请求,直接返回响应码或重定向到其他URL;
- 执行return指令后,location中后序指令将不会被执行;
return语法结构
- return code [text]; # 如果返回2XX的,text才有意义,text会在body中;
- return code URL; #主要用于重定向;
- return URL; #须以http或者https开头的;
常用状态码:
200 请求成功
301 永久转移到其他URL
404 请求资源不存在
500 内部服务器错误
code + text
location / { return 200 "this is return"; } #重启nginx [root@localhost sbin]# ./nginx -s reload [root@localhost sbin]# curl http://192.168.66.100 this is return[root@localhost sbin]#
code + URL
#302 表示临时性重定向。访问一个Url时,被重定向到另一个url上。常用于页面跳转。 location / { return 302 /bbs; } location /bbs { root html; index index.html; }
URL
直接访问百度
location / { return http://baidu.com; }
5.3 rewrite指令
地址重写
地址重写是实际上是为了实现址标准化,就像访问www.baidu.cn可以出现www.baidu.com的首页,服务器会把www.baidu.cn重写成www.baidu.com,浏览器的地址栏也会显示www.baidu.com。
地址转发
地址转发一般发生在同一站点项目内,而地址重写则没有限制。
总结:
- 地址转发后客户端浏览器地址栏中的地址显示是不改变的;而地址重写后客户端浏览器地址栏中的地址改变为服务器选择确定的地址。
- 在一次地址转发整个过程中,只产生一次网络请求;而一次地址重写一般会产生两次请求。地址转发一般发生在同一站点项目内;而地址重写没有该限制。
- 地址转发的速度较地址重定向快。
- 地址转发过程中,可以将客户端请求的request范围内的属性传递给新的页面,但地址重写不可以。
rewrite常用全局变量
变量 | 说明 |
$args | 请求中的参数,如www.123.com/1.php?a=1&b=2的$args就是a=1&b=2 |
$body_bytes_sent | 服务器发送给客户端的响应body字节数 |
$content_length | HTTP请求信息里的"Content-Length" |
$conten_type | HTTP请求信息里的"Content-Type" |
$document_root | nginx虚拟主机配置文件中的root参数对应的值 |
$document_uri | 当前请求中不包含指令的URI,如www.123.com/1.php?a=1&b=2的$document_uri就是1.php,不包含后面的参数 |
$http_referer | 记录此次请求是从哪个连接访问过来的,可以根据该参数进行防盗链设置 |
$host | 主机头,也就是域名 |
$http_user_agent | 客户端的详细信息,也就是浏览器的标识,用curl -A可以指定 |
$http_cookie | 客户端的cookie信息 |
$http_x_forwarded_for | 当前端有代理服务器时,设置web节点记录客户端地址的配置,此参数生效的前提是代理服务器也要进行相关的x_forwarded_for设置 |
$limit_rate | 如果nginx服务器使用limit_rate配置了显示网络速率,则会显示,如果没有设置, 则显示0 |
$remote_addr | 客户端的公网ip |
$remote_port | 客户端的port |
$remote_user | 如果nginx有配置认证,该变量代表客户端认证的用户名 |
$request | 请求的URI和HTTP协议,如“GET /article-10000.html HTTP/1.1” |
$request_body_file | 做反向代理时发给后端服务器的本地资源的名称 |
$request_method | 请求资源的方式,GET/PUT/DELETE等 |
$request_filename | 当前请求的资源文件的路径名称,相当于是$document_root/$document_uri的组合 |
$request_uri | 请求的链接,包括$document_uri和$args |
$scheme | 请求的协议,如ftp,http,https |
$server_protocol | 客户端请求资源使用的协议的版本,如HTTP/1.0,HTTP/1.1,HTTP/2.0等 |
$server_addr | 服务器IP地址 |
$server_name | 服务器的主机名 |
$server_port | 服务器的端口号 |
$status | http状态码,记录请求返回的状态码,例如:200、301、404等 |
$uri | 和$document_uri相同 |
$http_referer | 客户端请求时的referer,通俗讲就是该请求是通过哪个链接跳过来的,用curl -e可以指定 |
$time_local | 记录访问时间与时区,如18/Jul/2014:17:00:01 +0800 |
rewrite指令
该指令通过正则表达式的使用来改变URI。可以同时存在一个或者多个指令,按照顺序依次对URL进行匹配和处理。
该指令可以在server快或者location块中配置,其语法结构为:
rewrite regex replacement [flag]; 关键字 正则 替代内容 flag标记
常用正则表达式
字符 | 描述 |
\ | 将后面接着的字符标记为一个特殊字符或者一个原义字符或一个向后引用 |
^ | 匹配输入字符串的起始位置 |
$ | 匹配输入字符串的结束位置 |
* | 匹配前面的字符零次或者多次 |
+ | 匹配前面字符串一次或者多次 |
? | 匹配前面字符串的零次或者一次 |
. | 匹配除“\n”之外的所有单个字符 |
(pattern) | 匹配括号内的pattern |
rewrite 最后一项flag参数
标记符号 | 说明 |
last | 本条规则匹配完成后继续向下匹配新的location URI规则 |
break | 本条规则匹配完成后终止,不在匹配任何规则 |
redirect | 返回302临时重定向 |
permanent | 返回301永久重定向 |
注意:
regex,用于匹配URI的正则表达式。使用括号 "()" 标记要截取的内容。
示例1
无论/search 后面跟什么内容,最后都会被永久重定向到百度页面
location /search { rewrite ^/(.*) http://baidu.com permanent; }
示例2
创建三个文件夹,每个文件夹下有个1.html文件;
location /images { rewrite /images/(.*) /pics/$1; //将URL /images/1.html 重写为 /pics/1.html } location /pics { rewrite /pics/(.*) /photos/$1; //将URL /pics/1.html 重写为 /photos/1.html } location /photos { }
示例3
将URL /images/1.html 重写为 /pics/1.html, 并且不再匹配其他location段
location /images { rewrite /images/(.*) /pics/$1 break; }
示例4
将URL /images/1.html 重写为 /pics/1.html, 重新进入server段后匹配了/pics location段,然后又被重写。
location /images { rewrite /images/(.*) /pics/$1 last; }
5.4 rewrite实战域名跳转
Nginx的rewrite功能在企业里应用非常广泛
- 可以调整用户浏览的URL,看起来更规范,合乎开发及产品人员的需求。
- 网址换新域名后,让旧的访问跳转到新的域名上。
- 根据特殊变量、目录、客户端的信息进行URL调整等。
域名跳转
旧域名www.test.com直接跳转到新域名http://www.baidu.com
server { listen 80; server_name www.test.com; rewrite ^/(.*) http://www.baidu.com/$1 permanent; } server { listen 80; server_name www.baidu.com; location / { root html; index index.html index.htm; } access_log logs/brian.log main gzip buffer=128k flush=5s; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } [root@localhost sbin]# ./nginx -s reload
修改系统域名与IP映射关系:C:\Windows\System32\drivers\etc\hosts
192.168.66.100 www.test.com
访问www.test.com时会跳转到百度网址。
5.5 if指令
该指令用来支持条件判断,并根据条件判断结果选择不同的Nginx配置,可以在server块或locatio块中配置该指令。
if (condition) { ... }
其中,花括号代表一个作用域,形成一个if配置块,是条件为真时的Nginx配置。condition为判断条件( true/false ),它可以支持以下几种设置方法:
- 变量名-如果变量的值为空字符串或者“0”开头的任意字符串,if指令认为条件为false,其他情况为true,比如:
if ($slow) { ..... #Nginx配置 }
- 使用 "="(等于) 和 "!=" (不等于) 比较变量和字符串是否相等,相等时if指令为条件为true,反之为false。
if ($request_method = POST) { return 405; }
注意:
这里的字符串不需要加引号。
变量与正则表达式
- ~ 表示匹配过程对大小写敏感
- ~* 表示匹配过程对大小写不敏感
- !~ 表示匹配失败是if指令认为条件为true否则为false
if ($http_user_agent ~ MSIE) { # $http_user_agent 的值中是否包含MSIE字符串,如果包含,为true }
示例1
判断ip地址是否为192.168.66.10如果是返回“test if ok in Url /serach”
location /search { if ($remote_addr = "192.168.66.10"){ return 200 "test if ok in url search" } } #发送请求 curl http://192.168.66.100/search
示例2
不允许谷歌浏览器访问,如果用谷歌浏览器报500
if ($http_user_agent ~ Chrome){ return 500; }
5.6 set和break指令
set指令
该指令用于设置一个新的变量。
set variable value
- variable,为变量的名称。注意要用符号“$”作为变量的第一个字符,且变量不能与Nginx服务器预设的全局变量同名。
- value,为变量的值,可以是字符串、其他变量或变量的组合等。
break指令
该指令用于中断当前相同作用域中的其他Nginx配置。
break;
示例:
location / { if ($slow){ set $id $1 ; # 处于break指令之前,配置生效 break; limit_rate 10k; #处于break指令之后,配置无效 } }
5.7 Gzip压缩指令
Nginx开启Gzip压缩功能, 可以使网站的css、js 、xml、html 文件在传输时进行压缩,提高访问速度, 进而优化Nginx性能!
Gzip压缩作用
将响应报⽂发送⾄客户端之前可以启⽤压缩功能,这能够有效地节约带宽,并提⾼响应⾄客户端的速度。Gzip压缩可以配置http,server和location模块下。Nginx开启Gzip压缩参数说明:
指令 | 含义 |
gzip on | 决定是否开启gzip模块,on表示开启,off表示关闭; |
gzip_min_length 1k | 设置允许压缩的页面最小字节(从header头的Content-Length中获取) ,当返回内容大于此值时才会使用gzip进行压缩,以K为单位,当值为0时,所有页面都进行压缩。建议大于1k |
gzip_buffers 4 16k; | 设置gzip申请内存的大小,其作用是按块大小的倍数申请内存空间,param2:int(k) 后面单位是k。这里设置以16k为单位,按照原始数据大小以16k为单位的4倍申请内存 |
gzip_http_version 1.1; | 识别http协议的版本,早起浏览器可能不支持gzip自解压,用户会看到乱码 |
gzip_comp_level 2; | 设置gzip压缩等级,等级越底压缩速度越快文件压缩比越小,反之速度越慢文件压缩比越大;等级1-9,最小的压缩最快 但是消耗cpu |
gzip_types text/plain` `application /x-javascripttext/css` `application /xml``; |
设置需要压缩的MIME类型,非设置值不进行压缩,即匹配压缩类型 |
gzip_vary on; | 启用应答头"Vary: Accept-Encoding" |
gzip_proxied off; | nginx做为反向代理时启用,off(关闭所有代理结果的数据的压缩),expired(启用压缩,如果header头中包括"Expires" 头信息),no-cache(启用压缩,header头中包含"Cache-Control:no-cache" ), no-store(启用压缩,header头中包含"Cache-Control:no-store" ),private(启用压缩,header头中包含"Cache-Control:private" ),no_last_modefied(启用压缩,header头中不包含 "Last-Modified" ),no_etag(启用压缩,如果header头中不包含"Etag" 头信息),auth(启用压缩,如果header头中包含"Authorization" 头信息) |
gzip_disable msie6; | (IE5.5和IE6 SP1使用msie6参数来禁止gzip 压缩 )指定哪些不需要gzip 压缩的浏览器(将和User-Agents进行匹配),依赖于PCRE库 |
如下是线上常使用的Gzip压缩配置:
http { gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.1; gzip_comp_level 9; gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/javascript application/json; gzip_disable "MSIE [1-6]\."; gzip_vary on; }
Nginx的Gzip压缩功能虽然好用,但是下面两类文件资源不太建议启用此压缩功能。
1) 图片类型资源 (还有视频文件) 原因:图片如jpg、png文件本身就会有压缩,所以就算开启gzip后,压缩前和压缩后大小没有多大区别,所以开启了反而会白白的浪费资源。
2) 大文件资源 原因:会消耗大量的cpu资源,且不一定有明显的效果。
六、Nginx场景实践
6.1 浏览器缓存
浏览器缓存是为了加速浏览,浏览器在用户磁盘上,对最近请求过的文档进行存储。当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样,就可以加速页面的阅览,缓存的方式节约了网络的资源,提高了网络的效率。
实现浏览器缓存步骤
浏览器缓存可以通过expires指令输出Header头来实现。
语法:expires[time| epoch | max |off] 默认值:expires off 作用域:http、server、location
一个HTML页面,会引用一些JavaScript文件、图片文件、而这些格式的文件很少会被修改,则可以通过expires设置浏览器缓存。
示例1
比如,对常见格式的图片、Flash文件在浏览器本地缓存30天,对JS、CSS文件在浏览器本地缓存1小时,代码如下
location ~ .*\.(gif|jpg|png)$ { expires 30d; } location ~.*\.(js|css)?$ { expires 1h; }
Cache-Control
设置相对过期时间, max-age指明以秒为单位的缓存时间. 若对静态资源只缓存一次, 可以设置max-age的值为315360000000 (一万年). 比如对于提交的订单,为了防止浏览器回退重新提交,可以使用Cache-Control之no-store绝对禁止缓存,即便浏览器回退依然请求的是服务器,进而判断订单的状态给出相应的提示信息!
Http协议的cache-control的常见取值及其组合释义:
- no-cache: 数据内容不能被缓存, 每次请求都重新访问服务器, 若有max-age, 则缓存期间不访问服务器
- no-store: 不仅不能缓存, 连暂存也不可以(即: 临时文件夹中不能暂存该资源)
- private(默认): 只能在浏览器中缓存, 只有在第一次请求的时候才访问服务器, 若有- - max-age, 则缓存期间不访问服务器
- public: 可以被任何缓存区缓存, 如: 浏览器、服务器、代理服务器等
- max-age: 相对过期时间, 即以秒为单位的缓存时间
location ~ .*\.(gif|jpg|png)$ { add_header Cache-Control no-cache; add_header Cache-Control private; } location ~.*\.(js|css)?$ { add_header Cache-Control no-cache; add_header Cache-Control private; }
缓存流程
企业实战示例
- 禁用html文件缓存,即cache control设置为no-cache;
- 对于js,图片,css,字体等,设置max-age=2592000. 也就是30天;
注意:
缓存控制字段cache-control的配置(add_header)要放在http, server, location区域, 或是放在location的if判断里, 例如"add_header Cache-Control no-cache;"
server{ listen 80; server_name 192.168.66.100; location ~ \. (css|js|gif|jpg|jpeg|png|bmp|swf|ttf|woff|otf|ttc|pfa)$ { root /data/web/kevin; expires 30d; } location ~ \.(html|htm)$ { root /data/web/kevin; add_header Cache-Control no-cache; } }
6.2 防盗链技术
防盗链的概念
内容不在自己的服务器上,通过技术手段将其他网站的内容(比如一些音乐、图片、软件的下载地址)放置在自己的网站中,通过这种方法盗取其他网站的空间和流量。就是在自己的网站上使用其他人的资源作为自己页面的资源。
防盗链技术背景
防止第三方引用链接访问我们的图片,消耗服务器资源和网络流量,我们可以在服务器上做防盗链限制。
防盗链设置思路
首要方式:区别那些请求是非正常的用户请求。
基于http_refer防盗链配置模块
HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器借此可以获得一些信息用于处理。比如从我主页上链接到一个朋友那里,他的服务器就能够从HTTP Referer中统计出每天有多少用户点击我主页上的链接访问他的网站。
server{ listen 80; server_name 192.168.66.100; location ~ \. (css|js|gif|jpg|jpeg|png|bmp|swf|ttf|woff|otf|ttc|pfa)$ { root /data/web/kevin; expires 30d; valid_referers none blocked 192.168.66.100; if ($invalid_referer) { return 403; } } }
valid_referers none blocked 192.168.66.100; if ($invalid_referer) { return 403; }
参数:
- none : 允许没有http_refer的请求访问资源;
- blocked : 允许不是http://开头的,不带协议的请求访问资源;
- 192.168.66.100: 只允许指定ip来的请求访问资源;
6.3 代理服务
正向代理
正向代理,是在用户端的。比如需要访问某些国外网站,我们可能需要购买vpn。简单讲,正向代理就是自己访问外网。
正向代理最大的特点:
- 客户端非常明确要访问的服务器地址;
- 服务器只清楚请求来自哪个代理服务器,而不清楚来自哪个具体的客户端;
- 正向代理模式屏蔽或者隐藏了真实客户端信息。
反向代理
客户端给服务器发送的请求,nginx服务器接收到之后,按照一定的规则分发给了后端的业务处理服务器进行处理了。此时请求的来源也就是客户端是明确的,但是请求具体由哪台服务器处理的并不明确了,nginx扮演的就是一个反向代理角色。简单讲,反向代理就是外网访问nginx服务器再通过nginx服务器将请求发送到我们自己的服务器资源,这样可以保证安全。
反向代理,主要用于服务器集群分布式部署的情况下,反向代理隐藏了服务器的信息。
6.4 反向代理
单体架构
单体架构问题:
- 复杂性高:模块多,模块边界模糊,代码质量参差不齐,每次修改代码都心惊胆战
- 技术债务:随着时间的推移,需求变更和人员迭代,会逐渐形成应用程序的技术债务,并且越积越多
- 部署频率低:随着代码增多,构建和部署的时间也会增多,每次部署都要重新部署整个项目
- 可靠性差:某个应用有bug,可能会导致整个应用崩溃
- 扩展能力受限:单体应用只能作为一个整体应用进行扩展,无法根据业务模块的需要进行伸缩
反向代理设置指令:proxy_pass
学习Nginx服务器的反向代理服务,要涉及与后端代理服务器相关的配置指令比较重要,是为客户端提供正常Web服务的基础,大家应该熟练掌握尤其是proxy_pass指令,该指令用来设置被代理服务器的地址,可以是主机名称、IP地址加端口号等形式。在实际应用过程中需要注意一些配置细节,需要小心使用。
proxy_pass URL;
参数:
URL为要设置的被代理服务器的地址,包含传输协议、主机名称或P地址加端口号、URI等要素。传输协议通常是"http"或者"https://"。
示例:
server{ .... listen 80; server_name *.*; location / { proxy_pass http://192.168.66.101:8080; } }
反向代理实践
通过纯净虚拟机上的nginx访问到zk-1上的tomcta服务器。首先需要在zk-1虚拟机上安装jdk环境和tomcat,并启动tomcat。详见《生产环境部署与协同开发 Linux》然后在纯净虚拟机上配置反向代理,如上示例。在浏览器访问192.168.66.100即可访问到zk-1虚拟机上的tomcta首页。
6.5 负载均衡
什么是负载均衡
负载均衡是高可用网络基础架构的关键组件,通常用于将工作负载分布到多个服务器来提高网站、应用、数据库或其他服务的性能和可靠性。
没有负载均衡的 web 架构
有负载均衡web架构
upstream指令
upstream块不属于虚拟主机(不写在server块中),只能在http块里配置,它定义了在反向代理时Nginx需要访问的后端服务器集群和负载均衡策略。
upstream back_end { # upstream 需要一个名字 server 192.168.66.100 max_conns=2 fail_timeout=1s; server 192.168.66.101 weight = 1; server 192.168.66.102 weight = 1; } server{ lisetn 80; server_name *.*; location / { proxy_set_header Host $host; #转发原始请求的host头部(不是必须) proxy_pass http://back_end; #转发到upstream块定义的服务器集群。 } } [root@localhost sbin]# ./nginx -s reload
upstream参数:
- max_conns 节点的最大连接数
- slow_start 缓慢启动时间
- down 节点下线
- backup 备用节点
- max_fails 允许的最大失败数
- fail_timeout 超过最大失败数后的等待时间
浏览器请求:
就算是一台服务挂了,另一台还会提供响应的服务。
6.6 负载均衡算法
轮询(默认)
轮询方式,依次将请求分配到各个后台服务器中,默认的负载均衡方式。 适用于后台机器性能一致的情况。 挂掉的机器可以自动从服务列表中剔除。轮询不是绝对的。
upstream bakend { server 192.168.66.101; server 192.168.66.102; }
轮询加权重
根据权重来分发请求到不同的机器中,指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
upstream bakend { server 192.168.0.14 weight=10; server 192.168.0.15 weight=10; }
IP_hash
每个请求按访问IP的哈希结果分配,使来自同一个IP的访客固定访问一台后端服务器,并且可以有效解决动态网页存在的session共享问题。俗称IP绑定。
upstream backServer{ server 127.0.0.1:8080 ; server 127.0.0.1:8081 ; ip_hash; } server { listen 80; server_name www.itmayiedu.com; location / { ### 指定上游服务器负载均衡服务器 proxy_pass http://backServer; index index.html index.htm; } }
url_hash
按访问的URL的哈希结果来分配请求,使每个URL定向到一台后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身不支持url_hash,如果需要这种调度算法,则必须安装Nginx的hash软件包。
fair
比 weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间 来分配请求,响应时间短的优先分配。Nginx本身不支持fair,如果需要这种调度算法,则必须安装upstream_fair模块。
6.7 第三方fair模块安装
模块介绍
fair采用的不是内建负载均衡使用的轮换的均衡算法,而是可以根据页面大小、加载时间长短智能的进行负载均衡。
模块安装
将nginx-upstream-fair-master.zip压缩文件上传到Linux并解压到/usr/local下。
切换到nginx-1.20.2目录执行一下操作(之前通过编译该文件得到了nginx文件)
[root@localhost nginx-1.20.2]# ./configure --prefix=/usr/local/nginx/ --add-module=/usr/local/nginx-upstream-fair-mastercal/nginx-upstream-fair-master
编译
[root@localhost nginx-1.20.2]# make
编译完成不需要再次进行安装了因为之前已经安装过nginx-1.20.2了,编译完成后会发现在nginx-1.20.2下多了一个objs文件,该文件下还有一个nginx命令文件,我们要将这个文件替换掉原来的nginx命令文件。因为添加模块之后nginx的指令会变多,所以需要文件的替换。
替换原来的 nginx 命令文件
cp /usr/local/nginx-1.20.2/objs/nginx /usr/local/nginx/sbin/nginx
配置实现fair算法
upstream backserver { fair; server 192.168.66.100; server 192.168.66.101; } #重启 ./nginx -s reload
注意:
已安装Nginx,配置第三方模块时,只需要--add-module=/第三方模块目录,然后make编译一下就可以,不要 make install 安装。编译后复制objs下面的nginx到指定目录下。
6.8 Nginx配置故障转移
当上游服务器(真实访问服务器),一旦出现故障或者是没有及时相应的话,应该直接轮训到下一台服务器,保证服务器的高可用。
故障转移的条件
1.后端返回504、505状态码。
2.执行超时错误
Nginx配置代码
server { listen 80; server_name www.itmayiedu.com; location / { ### 指定上游服务器负载均衡服务器 proxy_pass http://backServer; #http_502 http_504:后端的服务器返回502、50 #error:连接服务器发生错误 #timeout:连接服务器超时错误 #invalid_header:服务器返回空或者错误的应答 proxy_next_upstream http_502 http_504 error timeout invalid_header; #nginx与上游服务器(真实访问的服务器)超时时间 后端服务器连接的超时时间_发起握手等候响应超时时间 proxy_connect_timeout 1s; ###nginx发送给上游服务器(真实访问的服务器)超时时间 proxy_send_timeout 1s; ### nginx接受上游服务器(真实访问的服务器)超时时间(发送消息没回应的时间) proxy_read_timeout 1s; #重试次数 proxy_next_upstream_tries = 3 index index.html index.htm; } }
6.9 跨域问题
为什么会出现跨域问题
出于浏览器的同源策略限制。同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
什么是跨域
当一个请求URL的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。
当前页面url | 被请求页面url | 是否跨域 | 原因 |
http://www.test.com/ | http://www.test.com/index.html | 否 | 同源(协议、域名、端口号相同) |
http://www.test.com/ | https://www.test.com/index.html | 跨域 | 协议不同(http/https) |
http://www.test.com/ | 百度一下,你就知道 | 跨域 | 主域名不同(test/baidu) |
http://www.test.com/ | http://blog.test.com/ | 跨域 | 子域名不同(www/blog) |
http://www.test.com:8080/ | http://www.test.com:7001/ | 跨域 | 端口号不同(8080/7001) |
解决跨域问题
location / { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization'; if ($request_method = 'OPTIONS') { return 204; } }
参数:
- Access-Control-Allow-Origin 表示服务器可以接受所有的请求源(Origin),即接受所有跨域的请求。
- Access-Control-Allow-Methods 明确了客户端所要访问的资源允许使用的方法或方法列表。
- Access-Control-Allow-Headers 设置预检请求。
6.10 动静分离
Nginx动静分离简单来说就是把动态和静态请求分开,不能理解成只是单纯的把动态页面和静态页面物理分离。严格意义上说应该是动态请求和静态请求分开,可以理解成使用Nginx处理静态请求,Tomcat处理动态请求。
什么是动静分离
动:必须依赖服务器生存的文件 比如jsp。 静:不需要依赖容器的比如css/js或者图片等文件
动静分离的好处
- Nginx本身就是一个高性能的静态web服务器
- 其实静态文件有一个特点就是基本上变化不大,所以动静分离以后我们可以对静态文件进行缓存、或者压缩提高网站性能
静态资源的类型
在Nginx的conf目录下,有一个mime.types文件里边列出了 静态资源的类型,如下:
当用户访问一个网站,然后从服务器端获取相应的资源,通过浏览器进行解析渲染最后展示给用户,服务端可以返回各种类型的内容,比如xml、jpg、png、gif、flash、MP4、html、css等等,浏览器就是根据mime-type来决定用什么形式来解析和展示的。服务器返回的资源给到浏览器时,会把媒体类型告知浏览器,这个告知的标识就是Content-Type,比如Content-Type:text/html。
动静分离实战
通过请求分离
将一张图片aaa.jpg上传到/usr/local/nginx/static下,然后在配置文件中添加如下配置:
upstream webservers { server 192.168.66.101:8080 weight=5; server 192.168.66.102:8080 weight=5; } server { listen 80; server_name *.*; location / { root html; index index.html index.htm; proxy_set_header Host $host; proxy_pass http://webservers; } //访问image会定位到/static/image目录 location /image/ { root static; } //访问web会定位到反向代理指定的服务(也就是占8080端口的tomcat) location /web/ { proxy_set_header Host $host; proxy_pass http://webservers; } } }
根据扩展名分离
upstream webservers { server 192.168.66.101:8080 weight=5; server 192.168.66.102:8080 weight=5; } server { listen 80; server_name *.*; location / { root html; index index.html index.htm; proxy_set_header Host $host; proxy_pass http://webservers; } #正则表达式,请求包含如下的会定位到static目录 location ~ .*.(jpg|png|gif|css|js)$ { root static; } } }
6.11 限流
生活中的限流
春运,一项全人类历史上最大规模的迁移活动,抢火车票一直是每年跨年以后的热点话题。
为什么需要限流
系统设计时一般会预估负载,当系统暴露在公网中时,恶意攻击或正常突发流量等都可能导致系统被压垮,而限流就是保护措施之一。
Nginx 的二种限流设置:
- 控制速率。
- 控制并发连接数。
限流算法
漏桶算法
原理:
漏桶算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。
令牌桶算法
原理:
令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。
漏桶 vs 令牌桶的区别
漏桶的天然特性决定了它不会发生突发流量,就算每秒1000个请求到来,那么它对后台服务输出的访问速率永远恒定。而令牌桶则不同,其特性可以“预存”一定量的令牌,因此在应对突发流量的时候可以在短时间消耗所有令牌,其突发流量处理效率会比漏桶高,但是导向后台系统的压力也会相应增多。
限流实现
根据nginx官网提供的说法,有两种算法,一种是漏桶算法,一种是令牌桶算法
- limit_req_zone 用来限制单位时间内的请求数目,以及速度限制。
- limit_req_conn 用来限制同一时间连接数,即并发限制。
Nginx限速配置指令
放在http{} 内
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
参数解析:
- 第一个参数:$binary_remote_addr 限制同一客户端ip地址。
- 第二个参数:zone=mylimit:10m 用来存储访问的频次信息。
- 第三个参数:rate=1r/s表示允许相同标识的客户端的访问频次,这里限制的是每秒1次,还可以有比如30r/m的。
放在server{}内或者location里面
limit_req zone=mylimit burst=1 nodelay;
参数:
- 第一个参数:zone=one 设置使用哪个配置区域来做限制,与上面limit_req_zone 里的name对应。
- 第二个参数:burst=5,重点说明一下这个配置,burst爆发的意思,这个配置的意思是设置一个大小为5的缓冲区当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内。
- 第三个参数:nodelay,如果设置,超过访问频次而且缓冲区也满了的时候就会直接返回503,如果没有设置,则所有请求会等待排队。
Nginx并发限制配置指令
ngx_http_limit_conn_module 提供了限制连接数的能力,利用 limit_conn_zone 和 limit_conn 两个指令即可。
http{ #ip limit limit_conn_zone $binary_remote_addr zone=perip:10m; limit_conn_zone $server_name zone=perserver:10m; }
在需要 限制并发数 和 下载带宽 的网站配置 server {}里加上如下代码:
server { limit_conn perip 10; limit_conn perserver 100; }
补充说明下参数:
- limit_conn perip 10 作用的key 是 $binary_remote_addr,表示限制单个IP同时最多能持有10个连接。
- limit_conn perserver 100 作用的key是 $server_name,表示虚拟主机(server) 同时能处理并发连接的总数。
设置白名单
限流主要针对外部访问,内网访问相对安全,可以不做限流,通过设置白名单即可。利用 Nginx ngx_http_geo_module 和 ngx_http_map_module 两个工具模块即可搞定。
查看是否具有该功能
./configure --help |grep http_limit_req_module ./configure --help |grep http_geo_module ./configure --help |grep http_map_module
在 nginx.conf 的 http 部分中配置白名单:
geo $limit { default 1; 39.100.243.125 0; 192.168.0.0/24 0; 172.20.0.35 0; } map limit limit_key { 0 ""; 1 $binary_remote_addr; } limit_req_zone $limit_key zone=myRateLimit:10m rate=10r/s;
参数:
- geo 对于白名单(子网或IP都可以) 将返回0,其他IP将返回1。
- map 将limit转换为 limit_key,如果是 $limit 是0(白名单),则返回空字符串;如果是1,则返回客户端实际IP。
- limit_req_zone 限流的key不再使用 binary_remote_addr,而是limit_key 来动态获取值。如果是白名单,limit_req_zone 的限流key则为空字符串,将不会限流;若不是白名单,将会对客户端真实IP进行限流。
6.12 WEB缓存机制
核心:
响应速度历来是衡量WEB应用和服务性能优劣的重要指标之一,尤其动态网站在网络上泛滥的今天。除了优化发布内容以外,另外一个主要的办法就是把不需要实时更新的动态页面输出结果转为静态网页形式缓存,进而按照静态网页来访问。
proxy Cache缓存机制
配置proxy_cache模块
在nginx.conf文件中添加如下代码:
http{ ...... proxy_cache_path /data/nginx/tmp-test levels=1:2 keys_zone=tmp-test:100m inactive=7d max_size=1000g; }
参数说明:
- proxy_cache_path 缓存文件路径
- levels 设置缓存文件目录层次;levels=1:2 表示两级目录
- keys_zone 设置缓存名字和共享内存大小
- inactive 在指定时间内没人访问则被删除
- max_size 最大缓存空间,如果缓存空间满,默认覆盖掉缓存时间最长的资源。
当配置好之后,重启nginx,如果不报错,则配置的proxy_cache会生效
查看 proxy_cache_path / data/ nginx / 目录, 会发现生成了 tmp -test 文件夹。
如何使用proxy_cache
location /tmp-test/ { proxy_cache tmp-test; proxy_cache_valid 200 206 304 301 302 10d; proxy_cache_key $uri; proxy_set_header Host $host:$server_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://127.0.0.1:8081/media_store.php/tmp-test/; }
参数:
- Proxy_cache tmp -test 使用名为 tmp -test 的对应缓存配置 proxy_cache_valid 200 206 304 301 302 10d; 对httpcode为200…的缓存10天
- proxy_cache_key $uri 定义缓存唯一key,通过唯一key来进行hash存取
- proxy_set_header 自定义http header头,用于发送给后端真实服务器。
- proxy_pass 指代理后转发的路径,注意是否 需要 最后的 /
提出问题
到此,就完成了最基本的proxy_cache配置和访问过程介绍,但是最基本的配置,往往无法满足我们的业务需求,我们往往会提出以下疑问和需求:
需要主动清理缓存文件
location /tmp-test/ { allow 127.0.0.1; //只允许本机访问 deny all; //禁止其他所有ip proxy_cache_purge tmp-test $uri; //清理缓存 }