工程实践:Nginx了解与入门

Nginx是一个Http服务器,常常被用在高并发,反向代理和负载均衡的场景下。这块内容一直没时间研究,刚好周末刷一下,恶补一下知识。

我用Docker启动了Nginx容器,以下内容均在容器内实现。

在启动了容器后,首先使用nginx -v查看版本。并用service nginx status来检查运行状态:

1
2
3
4
# nginx -v
nginx version: nginx/1.26.0
# service nginx status
nginx is running.

如果已经正确运行,这时候访问localhost,就能看到Welcome to nginx的页面。这意味着人们已经可以通过网址来访问我们的Web服务器了。

Nginx配置文件

Nginx的配置文件在Linux系统下位于/etc/nginx,名为nginx.conf。我们进去看一下,这是默认的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

#user nobody;
worker_processes 1;

#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

#pid logs/nginx.pid;


events {
worker_connections 1024;
}


http {
include mime.types;
default_type application/octet-stream;

#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';

#access_log logs/access.log main;

sendfile on;
#tcp_nopush on;

#keepalive_timeout 0;
keepalive_timeout 65;

#gzip on;

server {
listen 80;
server_name localhost;

#charset koi8-r;

#access_log logs/host.access.log main;

location / {
root html;
index index.html index.htm;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}

看着云里雾里,为了学习配置文件,我们备份一下,再写个新的配置。

1
2
mv nginx.conf nginx.conf.bak
touch nginx.conf

通常修改配置文件后,需要使用nginx -t来进行检查,查看是否能正确执行。不过现在是空的配置文件,会报错。

报错信息提示:no "events" section in configurationevents字段代表nginx如何处理连接。

我们可以修改一下内容:

1
events {}

这时候nginx -t不报错了。

检查完文件后,就需要重新加载,指令是nginx -s reload。启动完之后,发现之前的localhost无法访问了,意料之中。

好吧,并不是意料之中。我在Windows上依然访问到了localhost。很神奇,哪怕我把nginx容器关闭,依然能访问。这说明Windows上有其他进程调用了nginx,最后发现是Docker Desktop。应该是WSL里有配置nginx

解决了这个小插曲,后面我就不用Docker启动了。我在官网下了nginx的压缩包,解压后在路径下执行命令行:start nginx,即可启动服务。

Web服务器

同样备份清空,现在无法访问了。这就是http字段需要配置的内容:我们需要在这个字段内配置server,也就是http服务。例如如果服务器需要监听80端口,就这么写:

1
2
3
4
5
6
7
8
9
10
events {

}

http {
server {
listen 80;
server_name localhost; # IP地址或域名
}
}

完成这一步,reload一下发现又可以访问localhost了,因为这次我们准确提供了IP和端口,用户可以直接访问了。Perfecto!

这里其实还有一点问题。配置文件里是需要配置资源的,正常来说未配置资源时,会报错403 Forbidden。这就是nginx未找到资源。而它在未设置字段时默认读取html路径下的index.html。因此,当我修改了它的名字时,页面成功报错。

server字段,我们不一定需要设置资源地址,还可以返回状态码和对应字段。例如:

1
2
3
4
5
6
7
8
9
10
11
12
events {

}

http {
server {
listen 80;
server_name localhost; # IP地址或域名

return 200 'egg old wet.\n';
}
}

这时候访问localhost,就会返回这句话。也可以通过curl localhost从命令行访问到这句话。

配置文件中还可以设置资源根目录,我们进行如下修改,此时nginx就会从指定路径访问默认的index.html,而如果路径下没有这个文件,则会报错:

1
2
3
4
5
6
7
8
9
10
11
12
events {

}

http {
server {
listen 80;
server_name localhost; # IP地址或域名

root C:/nginx-1.26.0/html;
}
}

如果要指定文件,则需要给index字段进行赋值。这里我新建了一个自己页面,路径为C:\nginx-1.26.0\html,页面名为test.html。那么,配置文件这边需要改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
events {

}

http {
server {
listen 80;
server_name localhost; # IP地址或域名

root C:/nginx-1.26.0/html;
index test.html;
}
}

这时访问localhost,就可以正确显示了。

有的时候,需要在页面文件内引入各种其他文件。在配置文件的相同路径下,有一个mime.types的文件,这是对于各种文件类型的解析。我们需要对它进行引入,才能让web服务器正确解析文件。因此,我们需要在配置文件中加上include字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
events {

}

http {
include mime.types; # 由于是同路径,按照相对路径导入
server {
listen 80;
server_name localhost; # IP地址或域名

root C:/nginx-1.26.0/html;
index test.html;
}
}

这里的配置是全局配置,所以被放在了单个的server块外部。

除了这个配置,nginx自身的配置在conf.d中的default.conf中定义,如果需要修改就要在那里进行改动,例如一开始默认的index.html就在那里设置。

同样的,我们也可以把上面写的这些配置也通过一个文件进行导入。我们在conf.d路径下新建default.conf,并把内容复制其中:

1
2
3
4
5
6
7
server {
listen 80;
server_name localhost; # IP地址或域名

root C:/nginx-1.26.0/html;
index test.html;
}

然后我们回到nginx.conf文件,导入这个配置:

1
2
3
4
5
6
7
8
events {

}

http {
include mime.types; # 由于是同路径,按照相对路径导入
include C:/nginx-1.26.0/conf.d/*.conf # 导入路径下所有conf文件
}

reload一下,就实现了将配置文件分开执行的效果,有点像编程语言的导入库。这让config文件更加简洁。

现在,我想在default.conf中设定在访问localhost的时候,能够直接访问根目录。那么我需要在default.conf如下设置:

1
2
3
4
5
6
7
8
server {
listen 80;
server_name localhost; # IP地址或域名

location / {
root C:/nginx-1.26.0/html;
}
}

用这种方式可以更方便的定义路径。

现在,我想在访问localhost/app的时候能够访问到这个路径下的index文件,因此我们需要在location字段加上app路径,即:

1
2
3
4
5
6
7
8
server {
listen 80;
server_name localhost; # IP地址或域名

location /app {
root C:/nginx-1.26.0/html/app; # 对应的还需要新建一个app的文件夹来存放资源
}
}

这里需要注意,location后面的字段需要在root里找到一样的字段,否则nginx会找不到。现在,通过访问localhost/app/localhost/app/index.html都可以访问到index页面。

当我们输入location /app时,它就会在root路径寻找app为前缀的文件或URI(例如文件夹:localhost/apple/)。如果找不到就会报错。

为了安全隐患,防止用户可以访问到路径下其他文件,我们可以指定location

1
2
3
4
5
6
7
8
server {
listen 80;
server_name localhost; # IP地址或域名

location = /app/index.html {
root C:/nginx-1.26.0/html/app; # 对应的还需要新建一个app的文件夹来存放资源
}
}

这时,URI和文件路径必须完全匹配才能访问,即只有访问localhost/app/index.html才能访问资源。

此外,还可以使用正则来进行模糊匹配,例如我们的路径下有视频文件0-9.avi共九个文件,我们只想开放6-9这三个文件,就需要在location中设置正则表达式,只有访问指定名字的文件,才能访问到路径下的视频文件。

1
2
3
4
5
6
7
8
server {
listen 80;
server_name localhost; # IP地址或域名

location ~ /app/video[6-9].avi {
root C:/nginx-1.26.0/html/app; # 对应的还需要新建一个app的文件夹来存放资源
}
}

有时候,我们想要隐藏真实地址,来避免用户直接访问资源,这时可以用rewrite的字段来进行重定向。

1
2
3
4
5
6
7
8
server {
listen 80;
server_name localhost; # IP地址或域名

root C:/nginx-1.26.0/html/app;

rewrite /temp /app/index.html; # temp是临时设定的路径
}

此时访问localhost/temp即可访问到资源,而且用户也不会察觉到文件在服务器的真实路径。

反向代理

反向代理区别于正向代理,是为服务器上的资源和服务代理出去,访客通过访问服务器代理的多台子服务器,就能访问到网站,这样做能够进行负载均衡,减少主服务器的负担。

什么是正向代理,简单来说就是VPN,我们通过访问一个代理服务器,这个服务器代表我们去访问我们无法直连的服务器。正反代理代表的角色正好相反。

下面就用nginx来实现一下反向代理。假如我们创建了两个服务app/app1,分别监听3000和3001端口。此时我们需要修改配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 80;
server_name localhost; # IP地址或域名

root C:/nginx-1.26.0/html/app;
error_page 404 /404.html;
index index.html;

location /app1 {
proxy_pass http://localhost:3000; # 对3000端口的服务进行反向代理
}

location /app2 {
proxy_pass http://localhost:3001;
}
}

这样,访问指定的路径,就可以访问到对应端口运行的项目的资源。例如,访问localhost/app1就能访问到3000端口。实战中,可以通过修改server_name来设置不同后端。例如改为zerolovesea.com

负载均衡

使用nginx来设置负载均衡,需要在http块内设置upstream块,即上游服务器。我们修改配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
upstream backend-servers {
server localhost:3000; # 负载均衡的两个服务器
server localhost:3001; # 负载均衡的两个服务器
}

server {
listen 80;
server_name localhost; # IP地址或域名

root C:/nginx-1.26.0/html/app;
error_page 404 /404.html;
index index.html;

location / {
proxy_pass http://backend-servers; # 将流量引导指定服务器
}
}

这样访问localhost时,通过不断刷新,可以看到我们在3000和3001的服务中来回变化,这时因为服务器默认使用轮询的方式来进行负载均衡。我们可以使用weight字段来分配不同服务的权重占比,例如:

1
2
3
4
upstream backend-servers {
server localhost:3000 weight=2;# 负载均衡的两个服务器
server localhost:3001 weight=6; # 负载均衡的两个服务器
}

2024/4/27 于苏州