正向代理和反向代理
正向代理
简单理解,正向代理能够代表客户去访问互联网,它和NAT不同的是,NAT是4层的代理,而正向代理是7层的代理,所以用7层的代理效率不如NAT,但是它有个好处,就是能分析报文的应用层数据,举个简单例子,我想要禁止HTTP请求报文中包含"taobao"字样的报文,用NAT是实现不到的,而用代理则可以通过分析报文内容实现。
反向代理
和正向代理刚刚相反,正向代理是代表客户去访问互联网的服务器,反向代理则代表服务器提供服务。例如,我的网站部署了一台反向代理供互联网访问,那么客户访问反向代理服务器时,再由反向代理服务器转达客户的请求报文给背后真正的服务器,而后端的服务器响应也是响应给反向代理服务器,最后由反向代理服务器响应给客户端。
对于NGINX,正向和反向代理都支持,但普遍我们都是使用NGINX的反向代理功能,NGINX的代理功能都是由ngx_http_proxy_module模块提供。
再官方文档中有对该模块的各种指令的详细说明。
ngx_http_proxy_module
该模块默认已经安装,模块主要功能就是把请求传递给另一台服务器。它有很多的指令
附上模块指令手册地址:
http://nginx.org/en/docs/http/ngx_http_proxy_module.html
主要指令介绍:
proxy_pass
Syntax: proxy_pass URL;
Default: —
Context: location, if in location, limit_except
这指令应该是该模块的核心指令之一了,从字面意思能理解是定义代理到哪台服务器或哪个upstream池中的。它的常见用法如下:
proxy_pass http://localhost:8000/uri/;
注意:proxy_pass后面的路径不带uri时,其会将location的uri传递给后端主机;
例如:
server {
...
server_name www.ilinux.io;
location /pam/ {
proxy http://10.1.1.11:80
}
...
}
此时,访问www.ilinux.io/pam/时,实际上访问http://10.1.1.11:80/pam/
proxy_pass后面的路径是一个uri时,其会将location的uri替换为proxy_pass的uri;
例如:
server {
...
server_name www.ilinux.io;
location /pam/ {
proxy http://10.1.1.11:80:/blog/ #则后端收到的是:http://10.1.1.11:80:/blog/ ,location的被替换掉了
}
...
}
总之,location的只是URL的表示方式,一旦location被匹配了,则执行{}里的内容,而{}的内容才是真正有效的配置。如上面的例子:
访问http://www.ilinux.io/pam/,而实际上真正访问的是http://10.1.1.11:80:/blog/
还有,如果location定义其uri时使用了正则表达式的模式,或在if语句或limt_execept中使用proxy_pass指令,则proxy_pass之后必须不能使用uri; 用户请求时传递的uri将直接附加代理到的服务的之后;
http://HOSTNAME/uri/ --> http://host/uri/;
例如:
server {
...
server_name www.ilinux.io;
location ~* \.jpg$ {
proxy_pass http://10.1.1.11;
}
...
}
以上例子就PASS_pass就不能使用URI,例如访问:www.ilinux.io/blog/a.jpg
,则实际上访问http://10.1.1.11/blog/a.jpg
proxy_set_header
该指令是设定发往后端主机的请求报文的请求首部的值,当需要反代到后端服务器时,有2个值默认被修改:
1:proxy_set_header Host $proxy_host;
2:proxy_set_header Connection close;
Syntax: proxy_set_header field value;
Default:
proxy_set_header Host $proxy_host;
proxy_set_header Connection close;
Context: http, server, location
例如我需要把真正发起请求的客户端,把它的IP包含进请求头部中,然后反代给后端,在后端记录日志时,记录真正的客户端IP,则可以使用该指令配置。
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
下面这条只是记录转发的IP,因为反代可能有多级,如果有多级的情况下,下面这条记录的仅仅是上次转发的IP
默认在后端服务器上记录的是代理服务器的请求:
[root@node1 html]# tail /var/log/nginx/access.log
10.1.1.99 - - [26/Jun/2018:11:32:55 +0800] "GET /test.html HTTP/1.0" 200 26 "-" "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0" "-"
配置反代NGINX,在发送请求时添加一个X_forward字段
[root@nginx_proxy ~]# vim /etc/nginx/conf.d/proxy.conf
server {
listen 80;
server_name www.ilinux.io;
location / {
proxy_pass http://10.1.1.11;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
然后配置后端服务器的日志格式
在默认情况下,main格式的日志已经把x_forward_for字段的内容包含上去了,只要在请求报文带上,再查看日志时就能看出
在客户端中测试,并在后端服务器上查看日志。
[root@node1 ~]# tail /var/log/nginx/access.log
10.1.1.99 - - [26/Jun/2018:14:52:38 +0800] "GET /test.html HTTP/1.0" 200 26 "-" "curl/7.59.0" "192.168.30.148"
值得注意的是,像上面的例子我定义的字段为X-Forwarded-For,想要正常调用应该写成X_Forwarded_For 不知为何。。
proxy_hide_header
Syntax: proxy_hide_header field;
Default: —
Context: http, server, location
默认情况下,nginx不会传递标题字段“日期”、“服务器”、“X-Pad”和“X-Accel”。从一个代理服务器的响应到一个客户端。proxyhideheader指令设置了一些不会被传递的字段。相反,如果需要允许字段的传递,可以使用proxypassheader指令。
proxy_connect_timeout和proxy_read_timeout
Syntax: proxy_connect_timeout time;
Default:
proxy_connect_timeout 60s;
Context: http, server, location
反向代理面对服务侧的超时时间。发起方是proxy方,即等待握手成功的时间,3次握手。
而proxy_read_timeout则定义代理服务器和后端服务器的某一次传输完成以后,多久超时。例如设置为60s后,数据传输完成以后,60s内没有再次传输数据,则关闭连接。
proxy_send_timeout
Syntax: proxy_send_timeout time;
Default:
proxy_send_timeout 60s;
Context: http, server, location
设置一个超时,以便将请求发送到后端服务器。超时只设置在两个连续的写操作之间,而不是整个请求的传输,如果超过指定时间没收到后端服务器的数据,则断开连接,例如,proxy跟后端服务器正在传输数据,突然后端因为某原因挂了,proxy会一直在等,直到超过这个时间,才断开连接。
缓存
ngx_http_proxy自带了缓存功能,能对以下几种资源进行缓存:静态资源、日志、打开文件、动态资源。
NGinx的缓存功能的缺陷,摘自骏马金龙博客http://www.cnblogs.com/f-ck-need-u/p/7684732.html#2-nginx-
nginx的缓存功能主要用于缓存体积较小的页面资源,当数据较大时很容易出现瓶颈。在页面资源的缓存功能上,它属于业余玩家。而squid是科班出身,功能最全面,可以缓存大量数据,但架构太老,性能一般。varnish则是此类缓存的新贵,架构较新,内存管理完全交由操作系统内核,性能是squid的几倍之强,但缓存的内容不足squid
proxy_cache_path
定义可用于proxy功能的缓存路径,nginx的缓存一部分保存在内存上,一部分则保存在磁盘上,在磁盘上保存的缓存的URI名称和内容,所以,需要定义缓存保存的本地路径,在磁盘上保存的并不是缓存URI的名字,它的名字被哈希计算后作为CacheKEY,保存在内存的一张哈希表中,哈希表在内存中也有自己的名字,如果需要启用该缓存,则调用这个哈希表名字。
例如我在location / {}中使用了缓存,则请求匹配以后,它首先把请求的URL进行哈希计算,然后对比内存的中的哈希表,如果能在哈希表中找到,就根据该哈希的最右部分设定去指定目录下找该缓存。如果没有则
定义的相关指令主要有3个:proxy_cache_path、proxy_cache、proxy_cache_valid。要使用缓存,这3个指令都是需要配置的
proxy_cache_path
Syntax: 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];
Default: —
Context: http
其中,proxy_cache_path path [levels=levels] keys_zone=name:size [max_size=size]一般都是需要配置项
path:定义缓存路径
levels:定义缓存的目录级别,同一也是目录的字符数量,最大3级目录,使用16进制命令,例如levels 2:2:2,定义了3级目录,每个目录为2个字符
keys_zone:定义缓存标识名称和内存中缓存的最大空间。name部分必须唯一,在后面会引用name来表示使用该缓存方法。
max_size:定义磁盘中缓存目录的最大空间。即path定义的文件最大空间
例如:
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m;
定义/data/nginx/cache为缓存的路径,2级目录结构,key_zone名字为one,大小为10M
/data/nginx/cache/c/29/b7f54b2df7773722d382f4809d65029c
proxy_cache
Syntax: proxy_cache zone | off;
Default:
proxy_cache off;
Context: http, server, location
指明要调用的缓存,或关闭缓存机制;
proxy_cache_valid
根据状态码来指定缓存有效期
proxy_cache_valid 200 302 1h;
proxy_cache_valid 404 1m;
proxy_cache_valid any 5m;
简单的缓存实验
在开始之前我先使用ab工具压测一下,我请求的资源是一个1M的图片资源
[root@localhost ~]# ab -n 2000 -c 200 http://www.ilinux.io/test.jpg
Server Software: nginx/1.12.2
Server Hostname: www.ilinux.io
Server Port: 80
Document Path: /test.jpg
Document Length: 1244535 bytes
Concurrency Level: 200
Time taken for tests: 56.357 seconds
Complete requests: 2000
Failed requests: 0
Total transferred: 2489552000 bytes
HTML transferred: 2489070000 bytes
Requests per second: 35.49 [#/sec] (mean)
...
Requests per second: 没开启缓存时,每秒处理35个请求。
先在http上下文定义缓存路径
[root@nginx_proxy ~]# vim /etc/nginx/nginx.conf
http {
proxy_cache_path /data/web/nginx/cache levels=1:2 keys_zone=cache_one:10m max_size=1g;
}
创建缓存目录与配置权限
[root@nginx_proxy ~]# mkdir /data/web/nginx/cache -pv
[root@nginx_proxy ~]# chown -R nginx:nginx /data
在www.ilinux.io虚拟主机的location启用该缓存
server {
listen 80;
server_name www.ilinux.io;
location / {
proxy_pass http://10.1.1.11;
proxy_cache cache_one;
proxy_cache_valid any 5m;
}
}
测试。
使用客户端访问资源后查看缓存情况
[root@localhost ~]# curl http://www.ilinux.io/test.jpg
[root@nginx_proxy cache]# ls
7
在proxy中已经存在把资源缓存下来了。
最后使用ab压测一下
[root@localhost ~]# curl http://www.ilinux.io/test.jpg
Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: " to save to a file.
[root@localhost ~]# ab -n 2000 -c 200 http://www.ilinux.io/test.jpg
Server Software: nginx/1.12.2
Server Hostname: www.ilinux.io
Server Port: 80
Document Path: /test.jpg
Document Length: 1244535 bytes
Concurrency Level: 200
Time taken for tests: 26.773 seconds
Complete requests: 2000
Failed requests: 0
Total transferred: 2489552000 bytes
HTML transferred: 2489070000 bytes
Requests per second: 74.70 [#/sec] (mean)
能看到开启了缓存之后,每秒处理请求为74.7个,和没开启缓存相比,性能提高了一倍。
其他的缓存指令
proxy_cache_key
Syntax: proxy_cache_key string;
Default:
proxy_cache_key $scheme$proxy_host$request_uri;
Context: http, server, location
定义缓存的键,简单来讲就是以什么做键,默认是以:schemeproxy_host$request_uri;定义的层级越高,越难命中,但是越准确。
例如我只把request_uri做键可以这样做:
[root@nginx_proxy cache]# vim /etc/nginx/conf.d/proxy.conf
server {
listen 80;
server_name www.ilinux.io;
location / {
proxy_pass http://10.1.1.11;
proxy_cache cache_one;
proxy_cache_valid any 5m;
proxy_cache_key $request_uri;
}
}
这样,如果有请求时候,则只使用request_uri的哈希来匹配,这样的有点后遗症,例如有2台虚拟主机,有相同的资源名称,则该URI的缓存就适用用2台虚拟主机了,这样会出现缓存不准确的情况。有利弊
可以通过查看缓存来查看该缓存键值,例如:
[root@nginx_proxy cache]# cat c/97/5804fd1475122d946b51ef022d2cb97c 2"5b31b1c3-1a"
KEY: /test.html
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Tue, 26 Jun 2018 09:26:58 GMT
Content-Type: text/html
Content-Length: 26
Last-Modified: Tue, 26 Jun 2018 03:23:47 GMT
Connection: close
ETag: "5b31b1c3-1a"
Accept-Ranges: bytes
KEY:后跟的就是键,用于匹配缓存的是否命中的依据
proxy_cache_use_stale
Syntax: proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | http_429 | off ...;
Default: proxy_cache_use_stale off;
Context: http, server, location
这个指令定义当在与代理服务器进行通信时发生错误时,可以使用失效的缓存响应给客户端。
proxy_cache_methods
Syntax: proxy_cache_methods GET | HEAD | POST ...;
Default: proxy_cache_methods GET HEAD;
Context: http, server, location
This directive appeared in version 0.7.59.
默认情况下GET和HEAD方法会缓存内容,可以使用该指令来增加其他方法,但一般情况下不修改。