nginx功能介绍及配置解析

前置

http://tengine.taobao.org/book/chapter_02.html Nginx开发从入门到精通

服务器上已经有nginx。

nginx 解决的问题

高并发

负载均衡

高可用

虚拟主机

伪静态

动静分离

高并发

具说官方测试单台nginx可达到5万的并发。

linux操作系统的配置:

file-max

系统总限制: cat /proc/sys/fs/file-max

这表明这台Linux系统最多允许同时打开(即包含所有用户打开文件数总和)65535个文件,是Linux系统级硬限制,所有用户级的打开文件数限制都不会超过这个数值。
通常这个系统级硬限制是Linux系统在启动时根据系统硬件资源状况计算出来的最佳的最大同时打开文件数限制。

当前使用句柄数:cat /proc/sys/fs/file-nr

都是临时修改:

方法1:

1
echo  6553560 > /proc/sys/fs/file-max

方法2:

1
sysctl -w "fs.file-max=34166"

方法3:

1
2
echo "fs.file-max = 6553560" >> /etc/sysctl.conf
sysctl -p

网上都是方法3可以永久修改,我重启后还不是,还是以前的。

ulimit -n

Linux系统用户进程最大打开的文件限制

但是linux的root用户比较特别,一般系统安装软件为了对软件或文件的访问权限会创建用户,测试时也使用新建的用户测试。

1
ulimit -n 
1
2
ulimit -Sn   #检查 Linux 中的软限制
ulimit -Hn #在 Linux 中检查硬限制

修改(临时生效,当前sessioin(终端)):

1
ulimit -SHn 1000

永久修改:

1
2
3
4
5
6
7
vim /etc/security/limits.conf
# 格式如下
#<domain> <type> <item> <value>
用户名 hard nofile 4096
用户名 soft nofile 1024

保存后重启

Nginx.conf的参数:

建议设置为等于CPU总核心数。

1
worker_processes  8

单个worker进程最大打开的连接数

1
2
3
events {
worker_connections 1024;
}
1
2
3
4
5
6
7
8
events
{
#参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ];
#epoll模型是Linux 2.6以上版本内核中的高性能网络I/O模型,如果跑在FreeBSD上面,就用kqueue模型。
use epoll;
#单个进程最大连接数
worker_connections 1024;
}

每个进程的最大文件打开数:

1
worker_rlimit_nofile 65535;  # 一般等于ulimit -n系统值

并发总数是 worker_processes 和 worker_connections 的乘积,即 max_clients = worker_processes * worker_connections。

七层负载均衡

配置

在192.168.158.137和192.168.158.138启动tomcat,并开放8080端口。

在http模块加入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
   upstream tomcat {
server 192.168.158.137:8080;
server 192.168.158.138:8080;
}

server {
listen 80;
server_name localhost;

location / {
proxy_pass http://tomcat;#请求转向taishan定义的服务器列表
proxy_set_header Host $host;#将请求头转发给后端服务器
proxy_set_header X-Forward-For $remote_addr;#后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
}
}

测试

访问http://192.168.158.136/test/test01

配置解析

负载均衡算法

轮询配置:

1
2
3
4
upstream tomcat {
server 192.168.158.137:8080;
server 192.168.158.138:8080;
}

访问http://192.168.158.136/test/test01,test01方法返回本机IP

权重配置:weight和访问比率成正比,用于后端服务器性能不均的情况。

1
2
3
4
upstream tomcat {
server 192.168.158.137:8080 weight=1;
server 192.168.158.138:8080 weight=3;
}

测试方法同轮询。

轮询和权重配置算法:如果后端是tomcat并且使用的是服务器session时,登录信息丢失。

iphash配置:

1
2
3
4
5
upstream tomcat {
ip_hash;
server 192.168.158.137:8080;
server 192.168.158.138:8080;
}

在java的contoller里增加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@GetMapping("login")
public Map<String, Object> login(HttpServletRequest request, @RequestParam("userId") String userId) throws SocketException {
HttpSession session = request.getSession();
session.setAttribute("userId", userId);
Map<String, Object> map = new HashMap<>();
map.put("code", 200);
map.put("msg", "成功");
return map;
}

@GetMapping("test02")
public Map<String, Object> test02(HttpServletRequest request) throws SocketException {
Map<String, Object> map = new HashMap<>();
map.put("code", 200);
map.put("msg", "成功");
map.put("data", IpUtil.getLocalIp4Address().get().toString().replaceAll("/",""));
map.put("userId", request.getSession().getAttribute("userId"));
return map;
}

先访问http://192.168.158.136/test/login?userId=1

再访问http://192.168.158.136/test/test02 一直显示ip为一个,并且可以拿到userId。

urlhash配置:

1
2
3
4
5
upstream tomcat {
hash $request_uri;
server 192.168.158.137:8080;
server 192.168.158.138:8080;
}

复制contoler方法test01方法 分为两个/a/ 和 /b/

http://192.168.158.136/a/

http://192.168.158.136/b/ 会分配到不同的tomcat上。

响应时间配置:

这个是第三方模块提供的:https://github.com/gnosek/nginx-upstream-fair

下载zip包 nginx-upstream-fair-master.zip

1
unzip nginx-upstream-fair-master.zip

因为有编译安装时报错,所以修改代码:

1
sed -i 's/default_port/no_port/g' ngx_http_upstream_fair_module.c

nginx添加nginx-upstream-fair模块:

1
./configure --prefix=/usr/local/nginx --with-http_ssl_module --add-module=/usr/local/nginx-upstream-fair-master
1
make & make install

nginx.conf配置:

1
2
3
4
5
upstream tomcat {
fair;
server 192.168.158.137:8080;
server 192.168.158.138:8080;
}

不知道 怎么测试,试过java线程的sleep。

负载均衡的其他参数

down 表示单前的server暂时不参与负载

weight 默认为1. weight越大,负载的权重就越大。

max_fails:允许请求失败的次数默认为1.

fail_timeout: max_fails次失败后,暂停的时间。

backup: 其它所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力 会最轻。

max_conns 可以根据服务的好坏来设置最大连接数,防止挂掉,比如1000,我们可以设置800

max_fails=3 fail_timeout=30s代表在30秒内请求某一应用失败3次,认为该应用宕机,后等待30秒,这期间内不会再把新请求发送到宕机应用,而是直接发到正常的那一台,时间到后再有请求进来继续尝试连接宕机应用且仅尝试1次,如果还是失败,则继续等待30秒…以此循环,直到恢复。

1
2
3
4
upstream tomcat {
server 127.0.0.1:8050 weight=1 max_fails=1 fail_timeout=20;
server 127.0.0.1:8060 weight=1;
}

nginx的负载均衡会自动剔除节点。

默认的健康检查模块

配置一个status的location

1
2
3
location /status {
check_status;
}
1
2
3
4
5
6
7
upstream cluster1 {
server 192.168.158.137:8080;
server 192.168.158.138:8080;
check interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_http_send "HEAD / HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}

check 参数解析:

1
2
Syntax: check interval=milliseconds [fall=count] [rise=count] [timeout=milliseconds] [default_down=true|false] [type=tcp|http|ssl_hello|mysql|ajp] [port=check_port]
Default: 如果没有配置参数,默认值是:interval=30000 fall=5 rise=2 timeout=1000 default_down=true type=tcp

interval:向后端发送的健康检查包的间隔。

fall(fall_count): 如果连续失败次数达到fall_count,服务器就被认为是down。

rise(rise_count): 如果连续成功次数达到rise_count,服务器就被认为是up。

timeout: 后端健康请求的超时时间。

default_down: 设定初始时服务器的状态,如果是true,就说明默认是down的,如果是false,就是up的。默认值是true,也就是一开始服务器认为是不可用,要等健康检查包达到一定成功次数以后才会被认为是健康的。

type:健康检查包的类型,现在支持以下多种类型

​ tcp:简单的tcp连接,如果连接成功,就说明后端正常。

​ ssl_hello:发送一个初始的SSL hello包并接受服务器的SSL hello包。

​ http:发送HTTP请求,通过后端的回复包的状态来判断后端是否存活。mysql: 向mysql服务器连接,通过接收服务器的greeting包来判断后端是否存活。

​ ajp:向后端发送AJP协议的Cping包,通过接收Cpong包来判断后端是否存活。

port: 指定后端服务器的检查端口。可以指定不同于真实服务的后端服务器的端口,比如后端提供的是443端口的应用,你可以去检查80端口的状态来判断后端健康状况。默认是0,表示跟后端server提供真实服务的端口一样。

check_http_send:

check_http_send 配置http健康检查包发送的请求内容

为了减少传输数据量,推荐采用”HEAD”方法。当采用长连接进行健康检查时,需在该指令中添加keep-alive请求头,如:”HEAD / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n”。同时,在采用”GET”方法的情况下,请求uri的size不宜过大,确保可以在1个interval内传输完成,否则会被健康检查模块视为后端服务器或网络异常。

1
2
Syntax: check_http_send http_packet
Default: "GET / HTTP/1.0\r\n\r\n"

check_http_expect_alive:

check_http_expect_alive 指定主动健康检查时HTTP回复的成功状态:

1
2
Syntax: check_http_expect_alive [ http_2xx | http_3xx | http_4xx | http_5xx ]
Default: http_2xx | http_3xx

阿里的健康检查

nginx_upstream_check_module模块(淘宝技术团队开发)

https://github.com/yaoweibin/nginx_upstream_check_module

nginx自带健康检查的缺陷:

Nginx只有当有访问时后,才发起对后端节点探测。
如果本次请求中,节点正好出现故障,Nginx依然将请求转交给故障的节点,然后再转交给健康的节点处理。所以不会影响到这次请求的正常进行。但是会影响效率,因为多了一次转发
自带模块无法做到预警
被动健康检查

使用第三访模块nginx_upstream_check_module:

区别于nginx自带的非主动式的心跳检测,淘宝开发的tengine自带了一个提供主动式后端服务器心跳检测模块
若健康检查包类型为http,在开启健康检查功能后,nginx会根据设置的间隔向指定的后端服务器端口发送健康检查包,并根据期望的HTTP回复状态码来判断服务是否健康。
后端真实节点不可用,则请求不会转发到故障节点
故障节点恢复后,请求正常转发

高可用

nginx 可以用keepalived 实现。如果是云服务器如阿里云,是不让你装keepalived的,只能买他的(负载均衡)slb服务。

如果是nginx后的后台服务,由nginx的健康检查和负载均衡来实现。

虚拟主机

虚拟主机,就是把一台物理服务器划分成多个 “虚拟” 的服务器,这样我们的一台物理服务器就可以当做多个服务器来使用,从而可以配置多个网站。Nginx 提供虚拟主机的功能,就是为了让我们不需要安装多个 Nginx,就可以运行多个域名不同的网站。

Nginx 下,一个 server 标签就是一个虚拟主机。nginx 的虚拟主机就是通过 nginx.conf 中 server 节点指定的,想要设置多个虚拟主机,配置多个server节点即可。
通过nginx可以实现虚拟主机的配置,nginx支持三种类型的虚拟主机配置:

基于ip的虚拟主机

linux配置多个ip地址

ip: 192.168.158.135 192.168.158.136

复制nginx自带的index.html

1
2
cp index.html index135.html
cp index.html index136.html

修改html添加标识 135 136

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
listen 80;
server_name 192.168.158.135;
location / {
root html;
index index135.html index.htm;
}

}
server {
listen 80;
server_name 192.168.158.136;
location / {
root html;
index index136.html index.htm;
}

}

重启nginx

1
/usr/local/nginx/sbin/nginx -s reload

基于域名的虚拟主机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 80;
server_name www.a.com;
location / {
root html;
index index135.html index.htm;
}
}
server {
listen 80;
server_name www.b.com;
location / {
root html;
index index136.html index.htm;
}
}

基于端口的虚拟主机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 8080;
server_name 192.168.158.135;
location / {
root html;
index index135.html index.htm;
}
}
server {
listen 9090;
server_name 192.168.158.135;
location / {
root html;
index index136.html index.htm;
}
}

伪静态

使url看起来更规范,合理。
可将动态url地址伪装成静态地址提供服务。
网址换新域名后,让旧的访问跳转到新的域名上。

nginx的伪静态是用ngx_http_rewrite_module模块实现的。

配置文件的用法在官方:

http://nginx.org/en/docs/http/ngx_http_rewrite_module.html

rewrite的命令语法:

1
rewrite  正则<regex>   跳转后内容<replacement>   标记<flag>

Flag:

last 相当于Apache的[L] 标记,表示完成rewrite

break 本条规则匹配完成即终止,不再匹配后面的任何规则

redirect 返回302临时重定向,浏览器地址会显示跳转后的URL地址,爬虫不会更新url

permanent 返回301永久重定向, 浏览器地址栏会显示跳转后的URL地址,爬虫更新url

last 一般写在server和if中

break 一般使用在location中

测试:

1
2
3
4
5
server {
listen 80;
server_name 192.168.158.135;
rewrite ^/baidu/a/.*$ http://www.baidu.com?v=a last;
}

访问:http://192.168.158.135/baidu/a/ 会跳转 百度网址

配置:

1
2
3
4
location / {
rewrite ^/baidu/a$ http://www.baidu.com?v=a break;
return 403;
}

访问:http://192.168.158.136/baidu/a 会跳转 百度网址

动静分离(location如何使用)

动态请求请求tomcat

静态请求直接返回磁盘文件

在nginx里是由location标签实现的

静态请求:

1
2
3
4
5
location / {
root /usr/local/nginx/html/dist;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}

动态请求:

1
2
3
4
5
6
7
location /prod-api/{
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass 负载均衡;
}

location [ = | ~ | * | ^ ] uri { … }

location URI {} 对当前路径及子路径下的所有对象都生效;

location = URI {} 注意URL最好为具体路径。 精确匹配指定的路径,不包括子路径,因此,只对当前资源生效;

location ~ URI {} location * URI {} 模式匹配URI,此处的URI可使用正则表达式,区分字符大小写,~*不区分字符大小写;

location ^~ URI {} 禁用正则表达式

优先级:= > ^~ > |* > /|/dir/

location配置规则:

location 的执行逻辑跟 location 的编辑顺序无关。
矫正:这句话不全对,“普通 location ”的匹配规则是“最大前缀”,因此“普通 location ”的确与 location 编辑顺序无关;

但是“正则 location ”的匹配规则是“顺序匹配,且只要匹配到第一个就停止后面的匹配”;

“普通location ”与“正则 location ”之间的匹配顺序是?先匹配普通 location ,再“考虑”匹配正则 location 。

注意这里的“考虑”是“可能”的意思,也就是说匹配完“普通 location ”后,有的时候需要继续匹配“正则 location ”,有的时候则不需要继续匹配“正则 location ”。两种情况下,不需要继续匹配正则 location :

​ 1.当普通 location 前面指定了“ ^~ ”,特别告诉 Nginx 本条普通 location 一旦匹配上,则不需要继续正则匹配;

​ 2.当普通location 恰好严格匹配上,不是最大前缀匹配,则不再继续匹配正则

nginx常用正则

字符 说明
^ 匹配输入字符串的起始位置
$ 匹配输入字符串的结束位置
* 匹配前面的字符零次或多次
+ 匹配前面的字符一次或多次
? 匹配前面的字符零次或一次
. 匹配除“\n”之外的任何单个字符
\ 将后面接着字符标记为一个特殊字符或一个原义字符或一个向后引用
\d 匹配纯数字
{n,} 重复n次或更多次
{n } 重复n次
[c] 匹配单个字符c
[a-z] 匹配a-z小写字母的任意一个
[a-zA-Z] 匹配a-z小写字母或A-Z大写字母的任意一个

如何看nginx官网

http://nginx.org/

点击documentation

Introduction 下是一些案例,比如如何做一个负载均衡,配置https等

Modules reference 这是个很重要的部分

Core functionalit有error_log、worker_processes、worker_connections等配置参数的说明

ngx_http_core_module中有location、server的配置

然后用到哪个模块可以去哪个模块看如何使用

IP访问控制

使用的是ngx_http_access_module

1
2
3
语法:	allow address | CIDR | unix: | all;
默认: —
可以写在哪: http, server, location, limit_except
1
2
3
语法:	deny address | CIDR | unix: | all;
默认: —
可以写在哪: http, server, location, limit_except
1
2
3
4
5
6
7
8
9
10
11
12
location / {
# 拒绝单个IP
deny 192.168.1.1;
# 允许ip段
allow 192.168.1.0/24;
# 允许ip段
allow 10.1.1.0/16;
# 允许ipv6段
allow 2001:0db8::/32;
# 拒绝其余所有ip
deny all;
}

用户认证访问

可以使用ngx_http_auth_basic_module ngx_http_auth_jwt_module ngx_http_auth_request_module

这里演示basic:

1
2
3
Syntax:	auth_basic string | off;
Default: auth_basic off;
Context: http, server, location, limit_except
1
2
3
Syntax:	auth_basic_user_file file;
Default: —
Context: http, server, location, limit_except
1
2
3
4
location / {
auth_basic "closed site";
auth_basic_user_file htpasswd;
}

新建htpasswd 文件放到conf目录

htpasswd里内容格式为:

1
2
3
4
# comment
name1:password1
name2:password2:comment
name3:password3

密码是有要求的:看官网文档

1
2
3
yum -y install openssl
# 执行完后会返回加密后的密码
openssl passwd 123456

访问状态监控

使用的ngx_http_stub_status_module

1
2
3
Syntax:	stub_status;
Default: —
Context: server, location

示例:

1
2
3
location = /basic_status {
stub_status;
}

需要重新编译安装:

1
2
3
4
5
6
7
# 查看以前的配置
/usr/local/nginx/sbin/nginx -V
cd 到nginx原码目录
./configure 加以前的配置 --with-http_stub_status_module
make & make install
# 关闭nginx服务
# 启动nginx服务

访问:

http://192.168.158.135/basic_status

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Active connections
当前活动客户端连接数,包括Waiting连接数。
accepts
接受的客户端连接总数。
handled
处理的连接总数。accepts 通常,除非已达到某些资源限制(例如, worker_connections限制) ,否则该参数值相同。
requests
客户端请求的总数。
Reading
nginx 正在读取请求标头的当前连接数。
Writing
nginx 将响应写回客户端的当前连接数。
Waiting
当前等待请求的空闲客户端连接数。

gzip压缩

使用的ngx_http_gzip_module

具体详细的说明看官网。

试例:

1
2
gzip on;
gzip_types *;

准备一个大的html,大于20kb,因为gzip_min_length 默认为20kb.

在network禁用缓存,浏览器访问,发现请求的返回size为30kb,这是未gzip压缩的情况。

编辑配置文件,并重新加载配置。

再访问,再查看,发现只有1kb了,返回的response headers中变为Content-Encoding: gzip。

https配置

获取ssl证书

1.可以去阿里云的数字证书管理服务 申请免费或收费的证书。

2.linux系统生成ssl证书(浏览器不认)

生成私钥:

1
openssl genrsa -out privkey.pem 2048

2048 长度是2048

-out:生成的文件名

当前目录下会生成privkey.pem的文件

生成ca证书:

1
openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095

-key:使用的私钥文件
-out:生成的证书文件
-days:有效期,单位天

Country ISO国家代码(两位字母) CN
State or Provine Name 所在省份 beijing
Locality Name 所在城市 beijing
Organization Name 公司名称 xxx
Organization Unit Name 部门名称 TEST
Common Name 申请证书域名 localhost
Email Address 电子邮箱(可以不输入)
A challenge password 加密证书请求密码(可以不输入) 123456

当前目录下会生成cacert.pem的文件

nignx配置

Nginx的https是由–with-http_ssl_module模块提供的

在nginx 的程序目录中添加目录存放证书

1
2
3
cd /usr/local/nginx/conf
mkdir cert
#把证书放进这个目录

nginx配置

https监听配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
listen 443 ssl;
#配置HTTPS的默认访问端口为443。
#如果未在此处配置HTTPS的默认访问端口,可能会造成Nginx无法启动。
#如果您使用Nginx 1.15.0及以上版本,请使用listen 443 ssl代替listen 443和ssl on。
server_name yourdomain; #需要将yourdomain替换成证书绑定的域名。
ssl_certificate cert/cert-file-name.pem; #需要将cert-file-name.pem替换成已上传的证书文件的名称。
ssl_certificate_key cert/cert-file-name.key; #需要将cert-file-name.key替换成已上传的证书私钥文件的名称。
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
#表示使用的加密套件的类型。
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; #表示使用的TLS协议的类型。
ssl_prefer_server_ciphers on;
location / {
root html; #Web网站程序存放目录。
index index.html index.htm;
}
}

转发配置:

1
2
3
4
5
6
7
8
erver {
listen 80;
server_name yourdomain; #需要将yourdomain替换成证书绑定的域名。
rewrite ^(.*)$ https://$host$1; #将所有HTTP请求通过rewrite指令重定向到HTTPS。
location / {
index index.html index.htm;
}
}

启动nginx:

1
/usr/local/nginx/sbin/nginx

开放443端口

这时就可以访问了,如果是自签证书,浏览器不认,需要仍访问网站,可以看到页面。

也可以浏览器添加信任证书。


nginx功能介绍及配置解析
http://hanqichuan.com/2022/05/10/nginx/nginx功能介绍及配置解析/
作者
韩启川
发布于
2022年5月10日
许可协议