按部就班nginx(二):反向代理、负载均衡、缓存服务、静态资源访问


前置知识章节:
1.介绍、安装、hello world、location匹配
2.▶▶反向代理、负载均衡、缓存服务、静态资源访问✅
3.日志管理、http限流、https配置,http_rewrite模块,第三方模块安装,结语。✅html


反向代理


💡代理有正向代理和反向代理
💡正向代理:
所谓的正向,就是以请求发起为角度的,此时代理的是用户发起的请求。
用户A没法访问G网,但A能访问B网,而B网能访问G网。那么若是A先经过访问B网,B网帮他访问G网,返回数据给他,那么此时B网就做为了一个代理服务器,并且是正向代理。此时A实际上是知道它要访问G网的,G网并不知道是A访问它的,因此此时是代理了A,正向代理。前端

💡反向代理:
相对于正向代理的代理用户发起的请求,反向代理代理的是资源服务器的请求。
用户A访问B网的某个资源,但B网没有,而后它发给C网,C网返回给B网,B网返回给C网。此时用户A不知道它访问的是C网,因此此时代理的是C网,反向代理。
场景通常是,A能访问B,但不能访问C,B能访问C。咱们使用B这台机来对外提供服务,让关于C的请求都先通过B,B再请求C来返回结果。
反向代理有时候用在内网中,用来作请求转发到内网,这样必定程度的保护了内网的资源和利用上了内网的服务器。固然对于咱们业务来讲,服务器有可能并非部署在内网中的,也能够用在外网服务器代理上。java

💡反向代理是一个很是重要的功能,能够说nginx你最经常使用的功能或许就是反向代理了,动静分离、负载均衡和缓存有时候你可能用不上。nginx


使用

1.建立代理目标服务端:

咱们首先须要建立一个web服务端,因为我是主攻java的,因此我这里部署一个java的web服务器,并让他监听8081端口。(192.168.48.131是个人虚拟机的IP,nginx和这个java web服务端都部署在这里。)
20200614012418
访问一下这个要被代理的服务端的接口http://192.168.48.131:8081/user/list,发现调用成功。
20200614001834git


2.配置nginx反向代理目标服务端:

💡2.1 修改server:
由于此时是做为一个代理服务器,因此咱们要新建一个server块来充当代理服务器。


❓有人有点疑惑,上面的例子只配了location,咱们为何要配server呢?
其实这里是从业务需求来作区分的。由于咱们如今的目标是弄一个代理服务器,固然了你也能够不建立,而后把下面的location放到以前的server下便可。何时是必须建立的呢?当server_name不同的时候,但这个也是须要你根据业务来判断的,好比你本来使用80端口接收发过来的请求,而如今使用8080端口接收发过来的请求的时候就须要一个新的server_name。
这里新建一个server其实也有介绍server_name的用法的意思。github


修改server_name:server_name是当前服务端监听的地址的意思,
server_name支持几种语法:web

  • 基于确切的域名,域名能够一个或多个,多个使用空格隔开server_name example.com www.example.com
  • 支持通配符的方式,通配符*只能使用在开头或者结尾,不能使用在中间。当有多个匹配结果的时候,会选择最长的匹配结果server_name *.example.com www.example.*
  • 基于正则表达式,当使用正则表达式的时候,开头必须加上一个~server_name ~^www\d+\.example\.com$;
  • 正则表达式支持<>来获取一个变量到后面使用以实现二级域名的功能,这里不讲,有兴趣自查。
  • 上面几个语法的优先级是:
    • 精确域名 > 通配符在前 > 通配符在后 > 正则表达式

💡2.2 修改location
🔵proxy_pass用于设置被代理服务器的地址,能够是主机名称(https://www.baidu.com这样的)、IP地址(域名加端口号)的形式。
🔵下面的这个location的意思是,若是请求路径开头是/api的,那么都代理到proxy_pass指定的地址,好比访问了/api/user/list,那么获得的结果是http://localhost:8081/user/list的结果。正则表达式

server {
  listen 8080;

  location /api/ {
    proxy_pass http://localhost:8081/;
  }
}

🔵在lcoation都是location /api/时,proxy_pass不一样,请求的资源也是不同的:redis

  • proxy_pass http://localhost:8081;:请求nginx主机IP:8080/api/user/list,nginx会将该请求代理转发到http://locahost:8081/api/user/list
  • proxy_pass http://localhost:8081/;:请求nginx主机IP:8080/api/user/list,nginx会将该请求代理转发到http://locahost:8081/user/list
  • proxy_pass http://localhost:8081/test;请求nginx主机IP:8080/api/user/list,nginx会将该请求代理转发到http://locahost:8081/testuser/list
  • proxy_pass http://localhost:8081/test/;请求nginx主机IP:8080/api/user/list,nginx会将该请求代理转发到http://locahost:8081/test/user/list


3.测试使用:

在前面,我建立了一个8081的web服务端,并且http://nginx服务器IP:8081/user/list是有一个接口的.
20200614001834
咱们试一下使用8080来代理一下这个8081这个服务端:
注意下面的这个server若是写在default.conf的时候,是与其它server块同级的
20200614002940后端

若是咱们可以经过8080来访问到http://192.168.48.131:8081/user/list这个接口,那么就说明了咱们的反向代理成功了。此时发向8080端口的,以/api开头的请求都会代理到8081中。

20200614003048


## 其余反向代理指令 下面的其余反向代理指令我解释了一下用途,有须要就本身去了解一下吧。 * proxy_set_header:在将客户端请求发送给后端服务器以前,更改来自客户端的请求头信息。 * proxy_connect_timout:配置nginx与后端服务器尝试创建链接的超时时间。 * proxy_read_timeout:定义用于从代理服务器读取响应的超时。 * proxy_send_timeout:设置用于将请求传输到代理服务器的超时。 * proxy_redirect:用于修改后端服务器返回的响应头中的Location和Refresh。[proxy_redirect](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_redirect)

负载均衡

负载均衡其实也算是基于反向代理的。

上面的反向代理提到一点反向代理能够为咱们的服务端进行代理,你有时候多是多个服务端提供同一功能的,为了让他们可以平摊压力,那么你可能须要负载均衡功能。


使用

1.准备服务端

🔴准备负载均衡用的业务服务端,我这里给的是一个java Spring Boot端的简单代码,我部署成多个服务端的时候,因为端口不一样,访问info接口返回的数据也不一样,这样就能够测试是否达到了负载均衡。
20200615152747

🔴部署服务端,使用--server.port来部署到不一样的端口,例如java -jar a-simple-web-0.0.2-SNAPSHOT.jar --server.port=8082就把个人jar程序部署到了8082端口。



2.修改nginx配置

咱们这里新建一个conf.d/loadbalance.conf,因为nginx.conf内部有一个include /etc/nginx/conf.d/*.conf;能够把conf.d下的配置文件都导入到nginx.conf的http块中,因此咱们新建的这个conf也是能够导入到nginx.conf中的。
20200615155759



3.测试

访问http://192.168.31.128:9001/user/info,看是不是轮询的分发请求,若是响应的时候返回了不一样的端口,那么就证实了是轮询的分发请求。



负载均衡策略

💡默认轮询负载均衡:在不指定负载均衡策略的时候,默认的策略是按顺序给负载均衡服务端发送请求,而且一次顺序中每一个服务端处理两次请求,好比两个服务端,那么就是AABBAABB这样循环下去。 若是服务端有宕机的,会从负载均衡顺序中去除。等到宕机的服务端可用后,会在30S(好像是)以后加入到可用负载均衡服务端列表中。


💡加权轮询负载均衡:在轮询的基础上加上权重的考虑,假如服务端A的权重是1,服务端B的权重是2,那么6个请求中,服务端A会收到2个请求,服务端B会收到4个请求。
20200615155954


💡ip_hash负载均衡:每一个请求按ip的哈希结果分配到指定的负载服务端响应,(原理相似求余数,把服务端排序以后,根据哈希处理再处理以后获得的余数来选取服务端),这样同一个ip响应的服务端是固定的。能够在必定程度解决服务端Session共享的问题。但这样可能负载压力就分配的不平均了。
语法例子:

upstream ip-hash {
  ip_hash;
  server localhost:8081;
  server localhost:8082;
}

💡也可使用第三方模块来负载均衡。但因为咱们上面没有讲过第三方模块,引入前置知识须要占用大量篇幅,因此这里只引出一下,有须要的自查吧。

  • 第三方模块fair:能够基于响应时间分配请求,优先分配到响应时间短的服务端上。
  • 第三方模块url_hash:能够基于url的hash结果来分配请求。


负载均衡的额外参数

上面介绍了使用weight参数来实现加权轮询负载均衡,其实还有一些其余的参数。

  • max_fails:容许请求失败的次数。默认值是1。
  • fail_timeout:从新检测服务的时间,当服务端请求失败后,会在fail_timeout时间内标记成不可用,fail_timeout时间以后再次检测是否可用,不行就再等fail_timeout时间以后再次检测是否可用。默认是10S.
  • backup:预留的备份服务端。只有当其余服务端都宕机或者处于忙碌状态时,才会分发请求给backuo标注的服务端。
  • down:暂时不参与负载均衡的服务端,只有其余服务端都宕机的时候,这个服务端才参与负载均衡。
  • max_conns:限制接收的最大的链接数。
upstream web-server {
  server localhost:8081 max_fails=1 fail_timeout=10;
  server localhost:8082;
  server localhost:8083 backup;
  server localhost:8084 down;
}

💡使用ip_hash时,不能使用weight和backup。




缓存服务


💡在浏览器访问某个网站的资源的时候,会看浏览器是否缓存了这个数据。若是本地有这个缓存数据,那么就会直接从本地中获取了,不会再请求网络了。这是客户端缓存。nginx传递的响应的某些数据会影响浏览器是否缓存数据以及缓存多久。
💡除了客户端缓存,还有一种代理缓存nginx的缓存是代理缓存,由于它实际上是将代理的服务端的结果缓存了。代理缓存使用ngx_http_proxy_module的指令来配置。
💡(除了这两种,还有(后端)服务端缓存,也就是使用redis等技术进行的后端服务端缓存。)。


代理缓存

语法介绍

💡proxy_cache_path path:用来定义缓存文件路径。只能用在http块。
语法:

proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
  • path是缓存文件存放路径
  • levels用于定义文件存放层级,能够有一层(1),两层(1:2),三层(1:2:2)。目录会根据请求URL地址的哈希结果来建立(从末尾开始截取,),假如哈希结果是9cad383e7b0ee3d1d4b7099aace20b3f,那么levels=1:2表明第一层目录为长度为一的字符f,第二层是长度为2的字符b3
  • keys_zone用来定义当前这个缓存存放空间的名字,10m是一个大小
  • max_size用来定义目录最大的大小。
  • inactive用来定义不活跃的缓存多久清除。
  • use_temp_path:缓存临时目录。通常能够不定义。


💡proxy_cache zone|off:能够用在http, server, location。zone是proxy_cache_path的keys_zone.
💡proxy_cache_valid [code...] time:缓存过时周期,过时以后就不返回代理缓存了。 code是http状态码。
💡proxy_cache_key string:配置缓存的标识,好比说请求不同的话那确定不返回一样的代理缓存了,这就是是否返回同一个缓存的区分标识。默认值是proxy_cache_key $scheme$proxy_host$request_uri;,能够用在http, server, location。其余例子:proxy_cache_key $scheme$proxy_host$uri$args;



使用例子

下面来作一个实验:尝试使用nginx的代理缓存做为代理的响应。


1.咱们新建一个后端接口。这个后端接口能在每一次访问的时候都在控制台打印出一个消息(下面是一个Spring Boot例子):
20200622222735
2.配置反向代理:
20200622223500

3.一开始先测试一下http://192.168.31.128/api/user/info,看每一次访问是否都向后端发起了请求,确实是每一次都发请求的:
20200622223617

4.而后配置代理缓存:
20200626003102

5.再次测试访问http://192.168.31.128/api/user/info是否每一次访问都向后端发起了请求,结果应该是后端服务端只会打出一次,或者你能够尝试关闭后端服务端,而后访问,若是仍是可以访问,那么应该是已经代理缓存成功了。



代理缓存补充:

💡永久缓存:上面使用proxy_cache配置的实际上是临时缓存。也就是说必定时间后会自动过时。若是你的数据在很长很长时间都不会过时,那么能够考虑使用proxy_store.
💡若是你想让部分请求不要缓存,可使用proxy_no_cache [string...],string能够是变量,若是存在某个string值不为空也不为0,那么此请求不会被缓存。
💡缓存清理:incative配置会帮咱们清除过时的缓存文件,但还没过时的不会清除,须要咱们手动清除(场景是好比说你更新了大量数据,此时缓存中的数据不少都错误了,此时须要清除全部缓存。),若是你须要清除的话,那么一种方法是手动定义一个Linux脚原本清除缓存;一种方法是使用模块nginx-cache-purge。这些因为篇幅问题,不讲述。



浏览器缓存

浏览器是怎么判断缓存是否须要使用本地缓存以及缓存是否过时的呢?它经过响应头中的expirecache-controlLast-ModifiedEtag等头信息来判断的。


  • 用于本地校验是否过时的头:expireCache-control(max-age),若是有本地缓存,那么会使用expire来判断是否过时,不过时,直接使用本地缓存,本地过时以后,再进行远程校验。
  • 用于远程校验的Last-Modified头信息:用于远程文件修改校验的,是一个GMT时间,例如Thu, 02 Jul 2020 01:05:03 GMT,若是校验时间不一致,那么不使用本地缓存,一致则使用本地缓存。
  • 用于远程校验的的Etag头信息:用于远程文件修改校验的,是一个类时间戳的数据,例如:"5efd32bf-3fa8e",若是校验时间不一致,那么不使用本地缓存,一致则使用本地缓存。Etag与Last-Modified的区别是,Etag更精确,因此会优先判断Etag,而后再判断Last-Modified。
  • 相关nginx模块:ngx_http_headers_module

❗浏览器缓存机制你能够本身了解一下:博客园-HTTP缓存机制


💡Expire

  • 语法:expires [modified] time;
    • modified用于执行修改后过时,好比expires modified +24h;就表明修改后24小时内不过时。
    • time:过时时间,例若有expires 24h;24天不过时,expires 30d;30天不过时,expires -1;表明永不过时

测试

测试以前咱们先要提几点:

  • 浏览器有默认的缓存策略,Etag和Last-Modified默认是自带的,会有基于对文件修改的缓存,第一次响应200以后,第二次响应为304的时候,浏览器仍是会发请求,但此时发请求用于测试文件是否过时,不过时则不会传输文件
  • 当配置了expires的时候怎么判断它生效了呢?响应码为200,而且不对nginx发起请求。
  • expires并不会在任何状况都生效,好比说F5刷新Ctrl+F5刷新就无效,此时测试应该使用在连接栏按回车发请求来测试。expires可用于什么状况能够参考博客园-HTTP缓存机制

1.咱们先在/usr/share/nginx/html下面存储一张图片,后面会经过访问这种图片,来测试客户端缓存。


2.咱们一开始先不要配置expires:

server {
    listen       80;
    server_name  127.0.0.1;
    location / {
      root /usr/share/nginx/html;
    }
}

3.屡次访问一下http://192.168.31.128/a.jpg看看nginx的访问日志/var/log/nginx/access.log是否有添加
此时要多留意每次请求的响应码,应当有如下几个状况:

  • 若是你使用F5刷新,那么第一次响应码为200,后面都是304,第一个响应码为200的时候用于获取文件,后面的304会检查文件是否修改,不修改则使用缓存的文件,此时每次刷新都应该发了一次请求,access.log中能够观察到。
  • 若是你使用在连接栏按回车发请求来测试,那么响应码应当都是200,是每一次都会去请求文件,此时每次按回车都应该发了一次请求,access.log中能够观察到。

4.加了expires以后看看,咱们简单的使用两分钟看看。

server {
    listen       80;
    server_name  127.0.0.1;
    location / {
      expires 2m;
      root /usr/share/nginx/html;
    }
}

5.屡次访问一下http://192.168.31.128/a.jpg看看nginx的访问日志是否有添加

  • 若是你使用F5刷新,因为expires不会在F5刷新时生效,因此效果应该和未配置以前是同样的。
  • 若是你使用在连接栏按回车发请求来测试,那么一开始响应码应当都是200但不会真正的发起请求,而是使用缓存中的数据,access.log中不会看到请求。(由于我上面定义缓存是两分钟)两分钟以后第一次请求是304,用于校验文件是否修改,若是没修改,那么后面的两分钟以内的响应又是不发请求的200。


静态资源访问

💡nginx能够对外接收静态资源访问的请求。

💡在上面的location的内容的时候,其实有讲到返回文件资源的知识。
好比location ~* \.(gif|jpg|jpeg)$匹配任何以.gif、.jpg 或 .jpeg 结尾的请求,而后你发的请求匹配成功的时候,会使用root+location获得的路径的资源做为响应。其实这些也就是静态资源了,因此其实也能够经过nginx来达到静态资源的访问。

💡在先后端分离以后,前端做为静态资源访问,应该部署到哪里呢?由于咱们此时是不该该把前端静态资源部署到后端服务器上的。那么这时候放到nginx服务端上也是能够的。利用nginx的静态资源访问做为前端的服务端。

💡或者你也能够把nginx做为文件资源访问的服务端。

相关文章
相关标签/搜索