目录
反向代理
使用
1. 创建代理目标服务端:
2. 配置 nginx 反向代理目标服务端:
3. 测试使用:
负载均衡
使用
1. 准备服务端
2. 修改 nginx 配置
3. 测试
负载均衡策略
负载均衡的额外参数
缓存服务
代理缓存
语法介绍
使用例子
代理缓存补充:
浏览器缓存
测试
静态资源访问
前置知识章节:
1. 介绍, 安装, hello world,location 匹配
2.反向代理, 负载均衡, 缓存服务, 静态资源访问
3. 日志管理, http 限流, https 配置, http_rewrite 模块, 第三方模块安装, 结语.
反向代理
代理有正向代理和反向代理
正向代理:
所谓的正向, 就是以请求发起为角度的, 此时代理的是用户发起的请求.
用户 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 来返回结果.
反向代理有时候用在内网中, 用来做请求转发到内网, 这样一定程度的保护了内网的资源和利用上了内网的服务器. 当然对于我们业务来说, 服务器有可能并不是部署在内网中的, 也可以用在外网服务器代理上.
反向代理是一个非常重要的功能, 可以说 nginx 你最常用的功能或许就是反向代理了, 动静分离, 负载均衡和缓存有时候你可能用不上.
使用
1. 创建代理目标服务端:
我们首先需要创建一个 web 服务端, 由于我是主攻 java 的, 所以我这里部署一个 java 的 Web 服务器, 并让他监听 8081 端口.(192.168.48.131 是我的虚拟机的 IP,nginx 和这个 java Web 服务端都部署在这里.)
访问一下这个要被代理的服务端的接口
http://192.168.48.131:8081/user/list
, 发现调用成功.
2. 配置 nginx 反向代理目标服务端:
2.1 修改 server:
因为此时是作为一个代理服务器, 所以我们要新建一个 server 块来充当代理服务器.
有人有点疑惑, 上面的例子只配了 location, 我们为什么要配 server 呢?
其实这里是从业务需求来做区分的. 因为我们现在的目标是弄一个代理服务器, 当然了你也可以不创建, 然后把下面的 location 放到之前的 server 下即可. 什么时候是必须创建的呢? 当 server_name 不一样的时候, 但这个也是需要你根据业务来判断的, 比如你原本使用 80 端口接收发过来的请求, 而现在使用 8080 端口接收发过来的请求的时候就需要一个新的 server_name.
这里新建一个 server 其实也有介绍 server_name 的用法的意思.
修改 server_name:server_name 是当前服务端监听的地址的意思,
server_name 支持几种语法:
基于确切的域名, 域名可以一个或多个, 多个使用空格隔开
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 不同, 请求的资源也是不一样的:
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
是有一个接口的.
我们试一下使用 8080 来代理一下这个 8081 这个服务端:
注意下面的这个 server 如果写在 default.conf 的时候, 是与其它 server 块同级的.
如果我们能够通过 8080 来访问到 http://192.168.48.131:8081/user/list 这个接口, 那么就说明了我们的反向代理成功了. 此时发向 8080 端口的, 以 / API 开头的请求都会代理到 8081 中.
## 其他反向代理指令 下面的其他反向代理指令我解释了一下用途, 有需要就自己去了解一下吧. * 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 接口返回的数据也不同, 这样就可以测试是否达到了负载均衡.
部署服务端, 使用 --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 中的.
3. 测试
访问 http://192.168.31.128:9001/user/info, 看是否是轮询的分发请求, 如果响应的时候返回了不同的端口, 那么就证明了是轮询的分发请求.
负载均衡策略
默认轮询负载均衡: 在不指定负载均衡策略的时候, 默认的策略是按顺序给负载均衡服务端发送请求, 并且一次顺序中每个服务端处理两次请求, 比如两个服务端, 那么就是 AABBAABB 这样循环下去. 如果服务端有宕机的, 会从负载均衡顺序中去除. 等到宕机的服务端可用后, 会在 30S(好像是)之后加入到可用负载均衡服务端列表中.
加权轮询负载均衡: 在轮询的基础上加上权重的考虑, 假如服务端 A 的权重是 1, 服务端 B 的权重是 2, 那么 6 个请求中, 服务端 A 会收到 2 个请求, 服务端 B 会收到 4 个请求.
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 的缓存是代理缓存, 因为它其实是将代理的服务端的结果缓存了. 代理缓存使用的指令来配置.
(除了这两种, 还有 (后端) 服务端缓存, 也就是使用 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 例子):
2. 配置反向代理:
3. 一开始先测试一下
http://192.168.31.128/api/user/info
, 看每一次访问是否都向后端发起了请求, 确实是每一次都发请求的:
4. 然后配置代理缓存:
5. 再次测试访问 http://192.168.31.128/api/user/info 是否每一次访问都向后端发起了请求, 结果应该是后端服务端只会打出一次, 或者你可以尝试关闭后端服务端, 然后访问, 如果还是能够访问, 那么应该是已经代理缓存成功了.
代理缓存补充:
永久缓存: 上面使用 proxy_cache 配置的其实是临时缓存. 也就是说一定时间后会自动过期. 如果你的数据在很长很长时间都不会过期, 那么可以考虑使用 proxy_store.
如果你想让部分请求不要缓存, 可以使用 proxy_no_cache [string...],string 可以是变量, 如果存在某个 string 值不为空也不为 0, 那么此请求不会被缓存.
缓存清理: incative 配置会帮我们清除过期的缓存文件, 但还没过期的不会清除, 需要我们手动清除(场景是比如说你更新了大量数据, 此时缓存中的数据很多都错误了, 此时需要清除所有缓存.), 如果你需要清除的话, 那么一种方法是手动定义一个 Linux 脚本来清除缓存; 一种方法是使用模块 https://github.com/perusio/nginx-cache-purge . 这些由于篇幅问题, 不讲述.
浏览器缓存
浏览器是怎么判断缓存是否需要使用本地缓存以及缓存是否过期的呢? 它通过响应头中的 expire,cache-control,Last-Modified,Etag 等头信息来判断的.
用于本地校验是否过期的头: expire,
Cache-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 模块:
浏览器缓存机制你可以自己了解一下: 博客园 - 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 作为文件资源访问的服务端.
来源: https://www.cnblogs.com/progor/p/13338484.html