@yulongsun
2018-08-16T16:04:13.000000Z
字数 8068
阅读 1600
网关技术调研
设计为一个主进程,多个工作进程,每个进程都是单线程处理请求;
语法:location [=|~|~*|^~] /uri/
表达式 | 含义 |
---|---|
= | 表示开头精准匹配 |
^~ | 表示以某个常规字符串开头 |
~ | 表示区分大小写 |
~* | 表示不区分大小写 |
!~ | 表示区分大小写不匹配 |
!~* | 表示不区分大小写不匹配 |
.* | .表示任意字符串,*表示0到正无穷 |
\. | 转义字符,\.匹配. |
匹配顺序规则:= > ^~ >正则匹配顺序 > 通配符/
匹配成功之后,停止匹配,按当前匹配规则处理。
例如:
location = / {
#规则A
}
location = /login {
#规则B
}
location ^~ /static/ {
#规则C
}
location ~ \.(gif|jpg|png|js|css)$ {
#规则D
}
location ~* \.png$ {
#规则E
}
location !~ \.xhtml$ {
#规则F
}
location !~* \.xhtml$ {
#规则G
}
location / {
#规则H
}
比如:
1. 访问localhost/会匹配规则A。
2. 访问localhost/login会匹配规则B, 访问localhost/register会匹配规则H。
3. 访问localhost/static/a.html会匹配规则C。
4. 访问localhost/a.png将匹配规则D/E。但是因为规则D顺序优先,所以规则D生效,E不生效。
5. 访问localhost/a.PNG会匹配规则E,不会匹配D,因为区分大小写。
6. 访问localhost/a.xhtml不会匹配F/G。访问localhost/a.XHTML不会匹配规则G。
7. 访问localhost/a/b/c/d,则最终匹配到规则H,因为以上规则都匹配。
实验设备 | IP | 端口 | 备注 |
---|---|---|---|
node0 | 192.168.142.1 | 通讯机1:6610 通讯机2:7610 | |
node1 | 192.168.142.21 | httpd:8001 nginx:80 | 图片资源服务器 |
node2 | 192.168.142.22 | httpd:8002 tomcat:8080 | Tomcat服务器 |
node3 | 192.168.142.23 | httpd:8003 | 镜像服务器 |
node4 | 192.168.142.24 | nginx:80 | 自编译nginx代理服务器 |
Nginx1.9.0版本,新增支持TCP代理和负载均衡的stream模块。
【启用】./configure --with-stream
#设定TCP服务器
stream{
#上游服务器集群
upstream commserver{
server 192.168.142.1:6610;
server 192.168.142.1:7610;
}
#虚拟节点
server{
listen 6610;
proxy_pass commserver;
}
}
http{
#上游服务器集群
upstream backend{
server 192.168.142.21:8001 weight=1;
server 192.168.142.22:8002 weight=1;
server 192.168.142.23:8003 weight=1;
server 192.168.142.24:8004 weight=1;
}
#虚拟主机
server {
...
}
}
通讯机负载均衡实验
Nginx+Consul+Consul-template
#定义静态资源集群
upstream image{
server 192.168.142.21:8001 weight=1 max_fails=2 fail_timeout=10s;
}
#静态页面集群
upstream html{
server 192.168.142.21:8001;
server 192.168.142.22:8002 backup;
}
#动态资源集群
upstream backend{
server 192.168.142.22:8080;
}
server {
#监听端口
listen 80;
#域名可以有多个,用空格隔开
server_name node4;
#转发图片资源
location ~ .*\.(jpg)$ {
proxy_pass http://image;
}
#静态资源
location ~ .*\.(html|htm)$ {
proxy_pass http://html;
}
#动态站点转发
location ~ .*\.(do|jsp)$ {
proxy_pass http://backend;
}
}
静态页面:http://node4/index.html
图片:http://node4/icons/hundsun.jpg
JSP/Servlet:http://node4/demo/hello.do http://node4/demo/index.jsp
虽然经过多重测试,但是由于生产环境中的情况很复杂,测试环境很难完全模拟线上环境。为了能够更真实的测试,可以使用线上真实的流量来对测试环境中的服务器进行测试。通常用于仿真压测。
方案1:ngx_http_mirror_module
方案2:TcpCopy
#源地址配置
location / {
#指定镜像uri为/mirror
mirror /mirror;
#指定是否镜像请求body部分
mirror_request_body on;
proxy_pass http://192.168.142.21:8001/$request_uri;
}
#镜像地址配置
location /mirror {
proxy_pass http://192.168.142.23:8003$request_uri;
}
电商平台营销时候,经常会碰到的大流量问题,除了做流量分流处理,可能还要做用户黑白名单、信誉分析,进而根据用户ip信誉权重做相应的流量拦截、限制流量。
Nginx自身有的请求限制模块ngx_http_limit_req_module、流量限制模块ngx_stream_limit_conn_module基于漏桶算法,可以方便的控制令牌速率,自定义调节限流,实现基本的限流控制。
高并发三大利器:目的是保证核心服务可用,即使是有损的。
1、连接限流
#limit_conn_zone用来配置限流Key和存放Key的内存大小。
#$binary_remote_addr是IP维度限流。
limit_conn_zone $binary_remote_addr zone=conn_ip_zone:10m;
#$server_name是域名维度限流。
limit_conn_zone $server_name zone=conn_servername_zone:10m;
server {
...
location ~ .*\.(html|htm)$ {
#配置限流后返回码
limit_conn_status 503;
#IP维度,每个IP最大并发连接数
limit_conn conn_ip_zone 2;
#配置限流后的日志级别
limit_conn_log_level error;
#域名维度。每个域名最大并发数
limit_conn conn_servername_zone 1;
}
}
PS: Zone定义IP状态及URL访问频率的共享内存区域。zone=keyword标识区域的名字,以及冒号后面跟区域大小。16000个IP地址的状态信息约1MB,所以示例中区域可以存储160000个IP地址。
前提:模拟每个请求耗时1s
第一次:
1. 每个域名最大并发数设置为1
2. 压测5个并发 ab -c 5 -n 5 http://node4/demo/test.do
3. 结果
第二次:
1. 每个域名最大并发数调整为5
2. 压测5个并发 ab -c 5 -n 5 http://node4/demo/test.do
3. 结果
2、请求限流
#漏桶算法实现。配置Key及存放Key的内存大小。
#rate固定请求速率。如rate=500r/s 即每秒500个请求,每2毫秒一个请求
limit_req_zone $binary_remote_addr zone=req_ip_zone:10m rate=500r/s;
server {
...
location ~ .*\.(html|htm)$ {
#配置限流区域。桶容量(burst 默认为0) 是否延迟(默认延迟)
limit_req zone=req_ip_zone burst=0 delay;
#配置限流后返回码
limit_req_status 503;
#配置限流后的日志级别
limit_conn_log_level error;
}
}
前提:模拟每个请求耗时1s
第一次:
1. 桶容量=0,延迟
2. 压测5个并发 ab -c 5 -n 5 http://node4/demo/test.do
3. 结果
第二次:
1. 每个域名最大并发数调整为5
2. 压测5个并发 ab -c 5 -n 5 http://node4/demo/test.do
3. 结果
#代理层缓存定义:
#代理缓存路径。1:2表示创建2层目录。inactive表示缓存多久不被访问将被移除,默认10m。use_temp_path=on缓存会先被临时写入proxy_temp_path
proxy_cache_path /opt/nginx/cache levels=1:2 keys_zone=my_cache:200m inactive=10m max_size=1g use_temp_path=off;
proxy_connect_timeout 90; #nginx跟后端服务器连接超时时间(代理连接超时)
proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时)
proxy_read_timeout 90; #连接成功后,后端服务器响应时间(代理接收超时)
proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小
proxy_buffers 4 32k; #proxy_buffers缓冲区,网页平均在32k以下的设置
...
location ...{
#指定哪个缓存
proxy_cache my_cache;
#为不同状态码设定缓存时间
proxy_cache_valid 200 1h;
#响应头中添加缓存命中状态。 HIT命中 MISS未命中 EXPIRED过期
add_header cache_status $upstream_cache_status;
}
allow 127.0.0.1;
deny all;
【依赖第三方模块】:nginx_upstream_check_module https://github.com/yaoweibin/nginx_upstream_check_module
【踩坑】nginx_upstream_check_module不支持nginx_1.14.0。
Github Issues:
https://github.com/yaoweibin/nginx_upstream_check_module/issues/135
upstream backend{
server 192.168.142.21:8001 weight=1;
server 192.168.142.22:8002 weight=1;
server 192.168.142.23:8003 weight=1;
server 192.168.142.24:8004 weight=1;
check interval=3000 rise=1 fall=3 timeout=2000 type=http;
#check_http_send "HEAD /status HTTP /1.0\r\n\r\n";
#check_http_expect_alive http_2XX http_3XX;
}
注意:心跳时间不能太短,否则会导致上游服务器关掉。
效果图
我们可以将一个“新的版本代码”发布到集群中的少数几台(组)机器上,以便引入线上少量真实用户进行测试,用于验证产品改进的收益、小规模试错等
扩展:
nginx提供了“nginx_http_split_clients_module”、“nginx_stream_split_clients_module”,分别适用于http和tcp,可以帮助我们简单实现这些功能
#演示环境
upstream staging{
server 192.168.142.21:8001 weight=1;
}
#生产环境
upstream prod{
server 192.168.142.22:8002 weight=1;
}
server {
listen 80;
server_name node4;
set $group "default";
if ($http_cookie ~* "version=V1"){
set $group staging;
}
if ($http_cookie ~* "version=V2"){
set $group prod;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://$group/$request_uri;
}
}
#演示环境
upstream staging{
server 192.168.142.21:8001 weight=1;
}
#生产环境
upstream prod{
server 192.168.142.22:8002 weight=1;
}
server {
listen 80;
server_name node4;
set $group "default";
#本地走演示环境
if ($remote_addr ~ "127.0.0.1"){
set $group staging;
}
#外部走生产环境
if ($remote_addr !~ "127.0.0.1"){
set $group prod;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://$group/$request_uri;
}
}
location ~ .*.(html|htm)$ {
#连接超时,默认60s
proxy_connect_timeout 5s;
#读取超时。默认60s。
proxy_read_timeout 5s;
#发送请求超时时间。默认60s。
proxy_send_timeout 5s;
proxy_pass http://backend$request_uri;
}
备注:proxy_read_timeout、proxy_send_timeout:是指连续2次成功读取/发送的时间间隔,而不是读取/发送整个响应体的超时时间。如果超过这个时间间隔,则Nginx关闭此连接。
location ~ .*.(html|htm)$ {
#配置什么情况下需要重试。{error|timeout|http_500|http_502|http_503|http_403|http_404|off}
#off表示禁用重试
#timeout慎用
proxy_next_upstream error timeout;
#重试次数。0表示不限制。
proxy_next_upstream_tries 2;
#重试超时时间。0表示不限制。
proxy_next_upstream_timeout 6s;
proxy_pass http://backend$request_uri;
}
#定义上游服务器
upstream backend{
server 192.168.142.21:8001 weight=1 max_fails=2 fail_timeout=10s;
server 192.168.142.22:8002 weight=1 max_fails=2 fail_timeout=10s;
}
fail_timeout时间内失败了max_fails次,则认为上游服务器不存活,然后移除上游服务器。
fail_timeout之后,再次加入存活服务器列表重试。
【问题】
一个请求被重复提交,原因是nginx代理后面挂着2个服务器,请求超时的时候(其实已经处理了),结果nginx发现超时,有把请求转给另外台服务器又做了次处理。
解决方案:去掉proxy_next_upstream的timeout
add_header Set-Cookie "HttpOnly";
Cookie的HttpOnly属性,指示浏览器不要在除HTTP(和HTTPS)请求之外暴露Cookie。一个有HttpOnly属性的Cookie,是不可以通过例如调用JavaScript(引用document.cookie)这种非HTTP方式来访问。因此,也不可能通过跨域脚本来偷走这种Cookie。
#隐藏nginx版本号
server_tokens off;
隐藏之前
隐藏之后