图片来自 Pexels
Nginx 是开源、高性能、高可靠的 Web 和反向代理服务器,而且支持热部署,几乎可以做到 7*24 小时不间断运行,即使运行几个月也不需要重新启动,还能在不间断服务的情况下对软件版本进行热更新。
性能是 Nginx 最重要的考量,其占用内存少、并发能力强、能支持高达 5w 个并发连接数,最重要的是, Nginx 是免费的并可以商业化,配置使用也比较简单。
特点如下:
Nginx 的最重要的几个使用场景:
对于前端来说 Node.js 并不陌生,Nginx 和 Node.js 的很多理念类似,HTTP 服务器、事件驱动、异步非阻塞等,且 Nginx 的大部分功能使用 Node.js 也可以实现,但 Nginx 和 Node.js 并不冲突,都有自己擅长的领域。
Nginx 擅长于底层服务器端资源的处理(静态资源处理转发、反向代理,负载均衡等),Node.js 更擅长上层具体业务逻辑的处理,两者可以完美组合。
用一张图表示:
本文演示的是 Linux CentOS 7.x 的操作系统上安装 Nginx,至于在其他操作系统上进行安装可以网上自行搜索,都非常简单的。
使用 yum 安装 Nginx:
- yum install nginx -y
安装完成后,通过 rpm -ql nginx 命令查看 Nginx 的安装信息:
- # Nginx 配置文件
- /etc/nginx/nginx.conf # Nginx 主配置文件
- /etc/nginx/nginx.conf.default
- # 可执行程序文件
- /usr/bin/nginx-upgrade
- /usr/sbin/nginx
- # Nginx 库文件
- /usr/lib/systemd/system/nginx.service # 用于配置系统守护进程
- /usr/lib64/nginx/modules # Nginx 模块目录
- # 帮助文档
- /usr/share/doc/nginx-1.16.1
- /usr/share/doc/nginx-1.16.1/CHANGES
- /usr/share/doc/nginx-1.16.1/README
- /usr/share/doc/nginx-1.16.1/README.dynamic
- /usr/share/doc/nginx-1.16.1/UPGRADE-NOTES-1.6-to-1.10
- # 静态资源目录
- /usr/share/nginx/html/404.html
- /usr/share/nginx/html/50x.html
- /usr/share/nginx/html/index.html
- # 存放 Nginx 日志文件
- /var/log/nginx
主要关注的文件夹有两个:
systemctl 系统命令:
- # 开机配置
- systemctl enable nginx # 开机自动启动
- systemctl disable nginx # 关闭开机自动启动
- # 启动 Nginx
- systemctl start nginx # 启动Nginx成功后,可以直接访问主机IP,此时会展示Nginx默认页面
- # 停止 Nginx
- systemctl stop nginx
- # 重启 Nginx
- systemctl restart nginx
- # 重新加载 Nginx
- systemctl reload nginx
- # 查看 Nginx 运行状态
- systemctl status nginx
- # 查看 Nginx 进程
- ps -ef | grep nginx
- # 杀死 Nginx 进程
- kill -9 pid # 根据上面查看到的 Nginx 进程号,杀死 Nginx 进程,-9 表示强制结束进程
Nginx 应用程序命令:
- nginx -s reload # 向主进程发送信号,重新加载配置文件,热重启
- nginx -s reopen # 重启 Nginx
- nginx -s stop # 快速关闭
- nginx -s quit # 等待工作进程处理完成后关闭
- nginx -T # 查看当前 Nginx 最终的配置
- nginx -t # 检查配置是否有问题
①配置文件结构
Nginx 的典型配置示例:
代码如下:
- # main 段配置信息
- user nginx; # 运行用户,默认即是 Nginx,可以不进行设置
- worker_processes auto; # Nginx 进程数,一般设置为和 CPU 核数一样
- error_log /var/log/nginx/error.log warn; # Nginx 的错误日志存放目录
- pid /var/run/nginx.pid; # Nginx 服务启动时的 pid 存放位置
- # events 段配置信息
- events {
- use epoll; # 使用 epoll 的 I/O 模型(如果你不知道 Nginx 该使用哪种轮询方法,会自动选择一个最适合你操作系统的)
- worker_connections 1024; # 每个进程允许最大并发数
- }
- # http 段配置信息
- # 配置使用最频繁的部分,代理、缓存、日志定义等绝大多数功能和第三方模块的配置都在这里设置
- http {
- # 设置日志模式
- 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 /var/log/nginx/access.log main; # Nginx 访问日志存放位置
- sendfile on; # 开启高效传输模式
- tcp_nopush on; # 减少网络报文段的数量
- tcp_nodelay on;
- keepalive_timeout 65; # 保持连接的时间,也叫超时时间,单位秒
- types_hash_max_size 2048;
- include /etc/nginx/mime.types; # 文件扩展名与类型映射表
- default_type application/octet-stream; # 默认文件类型
- include /etc/nginx/conf.d/*.conf; # 加载子配置项
- # server段配置信息
- server {
- listen 80; # 配置监听的端口
- server_name localhost; # 配置的域名
- # location段配置信息
- location / {
- root /usr/share/nginx/html; # 网站根目录
- index index.html index.htm; # 默认首页文件
- deny 172.168.22.11; # 禁止访问的 ip 地址,可以为 all
- allow 172.168.33.44;# 允许访问的 ip 地址,可以为 all
- }
- error_page 500 502 503 504 /50x.html; # 默认 50x 对应的访问页面
- error_page 400 404 error.html; # 同上
- }
- }
用一张图清晰的展示它的层级结构:
②配置文件 main 段核心参数
user:指定运行 Nginx 的 woker 子进程的属主和属组,其中组可以不指定。
- user USERNAME [GROUP]
- user nginx lion; # 用户是 Nginx;组是 lion
pid:指定运行 Nginx master 主进程的 pid 文件存放路径。
- pid /opt/nginx/logs/nginx.pid # master主进程的的 pid 存放在 nginx.pid 的文件
worker_rlimit_nofile_number:指定 worker 子进程可以打开的最大文件句柄数。
- worker_rlimit_nofile 20480; # 可以理解成每个 worker 子进程的最大连接数量。
worker_rlimit_core:指定 worker 子进程异常终止后的 core 文件,用于记录分析问题。
- worker_rlimit_core 50M; # 存放大小限制
- working_directory /opt/nginx/tmp; # 存放目录
worker_processes_number:指定 Nginx 启动的 worker 子进程数量。
- worker_processes 4; # 指定具体子进程数量
- worker_processes auto; # 与当前 CPU 物理核心数一致
worker_cpu_affinity:将每个 worker 子进程与我们的 CPU 物理核心绑定。
- worker_cpu_affinity 0001 0010 0100 1000; # 4个物理核心,4个 worker 子进程
将每个 worker 子进程与特定 CPU 物理核心绑定,优势在于,避免同一个 worker 子进程在不同的 CPU 核心上切换,缓存失效,降低性能。但其并不能真正的避免进程切换。
worker_priority:指定 worker 子进程的 nice 值,以调整运行 Nginx 的优先级,通常设定为负值,以优先调用 Nginx 。
- worker_priority -10; # 120-10=110,110 就是最终的优先级
Linux 默认进程的优先级值是 120,值越小越优先;nice 定范围为 -20 到 +19 。
[备注] 应用的默认优先级值是 120 加上 nice 值等于它最终的值,这个值越小,优先级越高。
worker_shutdown_timeout:指定 worker 子进程优雅退出时的超时时间。
- worker_shutdown_timeout 5s;
timer_resolution:worker 子进程内部使用的计时器精度,调整时间间隔越大,系统调用越少,有利于性能提升;反之,系统调用越多,性能下降。
- timer_resolution 100ms;
在 Linux 系统中,用户需要获取计时器时需要向操作系统内核发送请求,有请求就必然会有开销,因此这个间隔越大开销就越小。
daemon:指定 Nginx 的运行方式,前台还是后台,前台用于调试,后台用于生产。
- daemon off; # 默认是 on,后台运行模式
③配置文件 events 段核心参数
use:Nginx 使用何种事件驱动模型。
- use method; # 不推荐配置它,让 Nginx 自己选择
- method 可选值为:select、poll、kqueue、epoll、/dev/poll、eventport
worker_connections:worker 子进程能够处理的最大并发连接数。
- worker_connections 1024 # 每个子进程的最大连接数为 1024
accept_mutex:是否打开负载均衡互斥锁。
- accept_mutex on # 默认是 off 关闭的,这里推荐打开
server_name 指令:指定虚拟主机域名。
- server_name name1 name2 name3
- # 示例:
- server_name www.nginx.com;
域名匹配的四种写法:
匹配优先级:精确匹配 > 左侧通配符匹配 > 右侧通配符匹配 > 正则表达式匹配
server_name 配置实例:
配置本地 DNS 解析 vim /etc/hosts (macOS 系统):
- # 添加如下内容,其中 121.42.11.34 是阿里云服务器 IP 地址
- 121.42.11.34 www.nginx-test.com
- 121.42.11.34 mail.nginx-test.com
- 121.42.11.34 www.nginx-test.org
- 121.42.11.34 doc.nginx-test.com
- 121.42.11.34 www.nginx-test.cn
- 121.42.11.34 fe.nginx-test.club
[注意] 这里使用的是虚拟域名进行测试,因此需要配置本地 DNS 解析,如果使用阿里云上购买的域名,则需要在阿里云上设置好域名解析。
配置阿里云 Nginx,vim/etc/nginx/nginx.conf:
- # 这里只列举了 http 端中的 sever 端配置
- # 左匹配
- server {
- listen 80;
- server_name *.nginx-test.com;
- root /usr/share/nginx/html/nginx-test/left-match/;
- location / {
- index index.html;
- }
- }
- # 正则匹配
- server {
- listen 80;
- server_name ~^.*\.nginx-test\..*$;
- root /usr/share/nginx/html/nginx-test/reg-match/;
- location / {
- index index.html;
- }
- }
- # 右匹配
- server {
- listen 80;
- server_name www.nginx-test.*;
- root /usr/share/nginx/html/nginx-test/right-match/;
- location / {
- index index.html;
- }
- }
- # 完全匹配
- server {
- listen 80;
- server_name www.nginx-test.com;
- root /usr/share/nginx/html/nginx-test/all-match/;
- location / {
- index index.html;
- }
- }
访问分析:
④root
指定静态资源目录位置,它可以写在 http、server、location 等配置中。
- root path
- 例如:
- location /image {
- root /opt/nginx/static;
- }
- 当用户访问 www.test.com/image/1.png 时,实际在服务器找的路径是 /opt/nginx/static/image/1.png
[注意] root 会将定义路径与 URI 叠加,alias 则只取定义路径。
⑤alias
它也是指定静态资源目录位置,它只能写在 location 中。
- location /image {
- alias /opt/nginx/static/image/;
- }
- 当用户访问 www.test.com/image/1.png 时,实际在服务器找的路径是 /opt/nginx/static/image/1.png
[注意] 使用 alias 末尾一定要添加 / ,并且它只能位于 location 中。
⑥location
配置路径:
- location [ = | ~ | ~* | ^~ ] uri {
- ...
- }
匹配规则:
匹配优先级:= > ^~ > ~ > ~* > 不带任何字符。
实例:
- server {
- listen 80;
- server_name www.nginx-test.com;
- # 只有当访问 www.nginx-test.com/match_all/ 时才会匹配到/usr/share/nginx/html/match_all/index.html
- location = /match_all/ {
- root /usr/share/nginx/html
- index index.html
- }
- # 当访问 www.nginx-test.com/1.jpg 等路径时会去 /usr/share/nginx/images/1.jpg 找对应的资源
- location ~ \.(jpeg|jpg|png|svg)$ {
- root /usr/share/nginx/images;
- }
- # 当访问 www.nginx-test.com/bbs/ 时会匹配上 /usr/share/nginx/html/bbs/index.html
- location ^~ /bbs/ {
- root /usr/share/nginx/html;
- index index.html index.htm;
- }
- }
location 中的反斜线:
- location /test {
- ...
- }
- location /test/ {
- ...
- }
return:停止处理请求,直接返回响应码或重定向到其他 URL ;执行 return 指令后,location 中后续指令将不会被执行。
- return code [text];
- return code URL;
- return URL;
- 例如:
- location / {
- return 404; # 直接返回状态码
- }
- location / {
- return 404 "pages not found"; # 返回状态码 + 一段文本
- }
- location / {
- return 302 /bbs ; # 返回状态码 + 重定向地址
- }
- location / {
- return https://www.baidu.com ; # 返回重定向地址
- }
rewrite:根据指定正则表达式匹配规则,重写 URL。
- 语法:rewrite 正则表达式 要替换的内容 [flag];
- 上下文:server、location、if
- 示例:rewirte /images/(.*\.jpg)$ /pic/$1; # $1是前面括号(.*\.jpg)的反向引用
flag 可选值的含义:
- server{
- listen 80;
- server_name fe.lion.club; # 要在本地 hosts 文件进行配置
- root html;
- location /search {
- rewrite ^/(.*) https://www.baidu.com redirect;
- }
- location /images {
- rewrite /images/(.*) /pics/$1;
- }
- location /pics {
- rewrite /pics/(.*) /photos/$1;
- }
- location /photos {
- }
- }
按照这个配置我们来分析:
⑦if 指令
- 语法:if (condition) {...}
- 上下文:server、location
- 示例:
- if($http_user_agent ~ Chrome){
- rewrite /(.*)/browser/$1 break;
- }
condition 判断条件:
实例:
- server {
- listen 8080;
- server_name localhost;
- root html;
- location / {
- if ( $uri = "/images/" ){
- rewrite (.*) /pics/ break;
- }
- }
- }
当访问 localhost:8080/images/ 时,会进入 if 判断里面执行 rewrite 命令。
⑧autoindex
用户请求以 / 结尾时,列出目录结构,可以用于快速搭建静态资源下载网站。autoindex.conf 配置信息:
- server {
- listen 80;
- server_name fe.lion-test.club;
- location /download/ {
- root /opt/source;
- autoindex on; # 打开 autoindex,,可选参数有 on | off
- autoindex_exact_size on; # 修改为 off,以 KB、MB、GB 显示文件大小,默认为 on,以 bytes 显示出⽂件的确切⼤⼩
- autoindex_format html; # 以 html 的方式进行格式化,可选参数有 html | json | xml
- autoindex_localtime off; # 显示的⽂件时间为⽂件的服务器时间。默认为 off,显示的⽂件时间为GMT时间
- }
- }
当访问 fe.lion.com/download/ 时,会把服务器 /opt/source/download/ 路径下的文件展示出来,如下图所示:
⑨变量
Nginx 提供给使用者的变量非常多,但是终究是一个完整的请求过程所产生数据, Nginx 将这些数据以变量的形式提供给使用者。
下面列举些项目中常用的变量:
实例演示 var.conf:
- server{
- listen 8081;
- server_name var.lion-test.club;
- root /usr/share/nginx/html;
- location / {
- return 200 "
- remote_addr: $remote_addr
- remote_port: $remote_port
- server_addr: $server_addr
- server_port: $server_port
- server_protocol: $server_protocol
- binary_remote_addr: $binary_remote_addr
- connection: $connection
- uri: $uri
- request_uri: $request_uri
- scheme: $scheme
- request_method: $request_method
- request_length: $request_length
- args: $args
- arg_pid: $arg_pid
- is_args: $is_args
- query_string: $query_string
- host: $host
- http_user_agent: $http_user_agent
- http_referer: $http_referer
- http_via: $http_via
- request_time: $request_time
- https: $https
- request_filename: $request_filename
- document_root: $document_root
- ";
- }
- }
当我们访问 http://var.lion-test.club:8081/test?pid=121414&cid=sadasd 时,由于 Nginx 中写了 return 方法,因此 chrome 浏览器会默认为我们下载一个文件,下面展示的就是下载的文件内容:
- remote_addr: 27.16.220.84
- remote_port: 56838
- server_addr: 172.17.0.2
- server_port: 8081
- server_protocol: HTTP/1.1
- binary_remote_addr: 茉
- connection: 126
- uri: /test/
- request_uri: /test/?pid=121414&cid=sadasd
- scheme: http
- request_method: GET
- request_length: 518
- args: pid=121414&cid=sadasd
- arg_pid: 121414
- is_args: ?
- query_string: pid=121414&cid=sadasd
- host: var.lion-test.club
- http_user_agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36
- http_referer:
- http_via:
- request_time: 0.000
- https:
- request_filename: /usr/share/nginx/html/test/
- document_root: /usr/share/nginx/html
Nginx 的配置还有非常多,以上只是罗列了一些常用的配置,在实际项目中还是要学会查阅文档。
代理是在服务器和客户端之间假设的一层服务器,代理将接收客户端的请求并将它转发给服务器,然后将服务端的响应转发给客户端。
不管是正向代理还是反向代理,实现的都是上面的功能。
①正向代理
正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。
正向代理是为我们服务的,即为客户端服务的,客户端可以根据正向代理访问到它本身无法访问到的服务器资源。
正向代理对我们是透明的,对服务端是非透明的,即服务端并不知道自己收到的是来自代理的访问还是来自真实客户端的访问。
②反向代理
反向代理(Reverse Proxy)方式是指以代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
反向代理是为服务端服务的,反向代理可以帮助服务器接收来自客户端的请求,帮助服务器做请求转发,负载均衡等。
反向代理对服务端是透明的,对我们是非透明的,即我们并不知道自己访问的是代理服务器,而服务器知道反向代理在为他服务。
反向代理的优势:
那么“动静分离”是什么?负载均衡又是什么?
③动静分离
动静分离是指在 web 服务器架构中,将静态页面与动态页面或者静态内容接口和动态内容接口分开不同系统访问的架构设计方法,进而提示整个服务的访问性和可维护性。
一般来说,都需要将动态资源和静态资源分开,由于 Nginx 的高并发和静态资源缓存等特性,经常将静态资源部署在 Nginx 上。如果请求的是静态资源,直接到静态资源目录获取资源,如果是动态资源的请求,则利用反向代理的原理,把请求转发给对应后台应用去处理,从而实现动静分离。
使用前后端分离后,可以很大程度提升静态资源的访问速度,即使动态服务不可用,静态资源的访问也不会受到影响。
④负载均衡
一般情况下,客户端发送多个请求到服务器,服务器处理请求,其中一部分可能要操作一些资源比如数据库、静态资源等,服务器处理完毕后,再将结果返回给客户端。
这种模式对于早期的系统来说,功能要求不复杂,且并发请求相对较少的情况下还能胜任,成本也低。随着信息数量不断增长,访问量和数据量飞速增长,以及系统业务复杂度持续增加,这种做法已无法满足要求,并发量特别大时,服务器容易崩。
很明显这是由于服务器性能的瓶颈造成的问题,除了堆机器之外,最重要的做法就是负载均衡。
请求爆发式增长的情况下,单个机器性能再强劲也无法满足要求了,这个时候集群的概念产生了,单个服务器解决不了的问题,可以使用多个服务器,然后将请求分发到各个服务器上,将负载分发到不同的服务器,这就是负载均衡,核心是「分摊压力」。Nginx 实现负载均衡,一般来说指的是将请求转发给服务器集群。
举个具体的例子,晚高峰乘坐地铁的时候,入站口经常会有地铁工作人员大喇叭“请走 B 口, B 口人少车空……”,这个工作人员的作用就是负载均衡。
Nginx 实现负载均衡的策略:
在配置反向代理和负载均衡等等功能之前,有两个核心模块是我们必须要掌握的,这两个模块应该说是 Nginx 应用配置中的核心,它们分别是:upstream、proxy_pass。
①upstream
- 语法:upstream name {
- ...
- }
- 上下文:http
- 示例:
- upstream back_end_server{
- server 192.168.100.33:8081
- }
用于定义上游服务器(指的就是后台提供的应用服务器)的相关信息。
在 upstream 内可使用的指令:
server:定义上游服务器地址。
- 语法:server address [parameters]
- 上下文:upstream
parameters 可选值:
keepalive:限制每个 worker 子进程与上游服务器空闲长连接的最大数量。
- keepalive connections;
- 上下文:upstream
- 示例:keepalive 16;
keepalive_requests:单个长连接可以处理的最多 HTTP 请求个数。
- 语法:keepalive_requests number;
- 默认值:keepalive_requests 100;
- 上下文:upstream
keepalive_timeout:空闲长连接的最长保持时间。
- 语法:keepalive_timeout time;
- 默认值:keepalive_timeout 60s;
- 上下文:upstream
配置实例:
- upstream back_end{
- server 127.0.0.1:8081 weight=3 max_conns=1000 fail_timeout=10s max_fails=2;
- keepalive 32;
- keepalive_requests 50;
- keepalive_timeout 30s;
- }
②proxy_pass
用于配置代理服务器:
- 语法:proxy_pass URL;
- 上下文:location、if、limit_except
- 示例:
- proxy_pass http://127.0.0.1:8081
- proxy_pass http://127.0.0.1:8081/proxy
URL 参数原则:
接下来让我们来看看两种常见的 URL 用法:
这两种用法的区别就是带 / 和不带 / ,在配置代理时它们的区别可大了:
不带 / 的用法:
- location /bbs/{
- proxy_pass http://127.0.0.1:8080;
- }
分析:
带 / 的用法:
- location /bbs/{
- proxy_pass http://127.0.0.1:8080/;
- }
分析:
并没有拼接上 /bbs,这点和 root与 alias 之间的区别是保持一致的。
③配置反向代理
这里为了演示更加接近实际,作者准备了两台云服务器,它们的公网 IP 分别是:121.42.11.34 与 121.5.180.193。
我们把 121.42.11.34 服务器作为上游服务器,做如下配置:
- # /etc/nginx/conf.d/proxy.conf
- server{
- listen 8080;
- server_name localhost;
- location /proxy/ {
- root /usr/share/nginx/html/proxy;
- index index.html;
- }
- }
- # /usr/share/nginx/html/proxy/index.html
- <h1> 121.42.11.34 proxy html </h1>
配置完成后重启 Nginx 服务器 nginx -s reload。
- # /etc/nginx/conf.d/proxy.conf
- upstream back_end {
- server 121.42.11.34:8080 weight=2 max_conns=1000 fail_timeout=10s max_fails=3;
- keepalive 32;
- keepalive_requests 80;
- keepalive_timeout 20s;
- }
- server {
- listen 80;
- server_name proxy.lion.club;
- location /proxy {
- proxy_pass http://back_end/proxy;
- }
- }
本地机器要访问 proxy.lion.club 域名,因此需要配置本地 hosts,通过命令:vim /etc/hosts 进入配置文件,添加如下内容:
- 121.5.180.193 proxy.lion.club
分析:
当访问 proxy.lion.club/proxy 时通过 upstream 的配置找到 121.42.11.34:8080。
因此访问地址变为 http://121.42.11.34:8080/proxy。
连接到 121.42.11.34 服务器,找到 8080 端口提供的 server。
通过 server 找到 /usr/share/nginx/html/proxy/index.html 资源,最终展示出来。
④配置负载均衡
配置负载均衡主要是要使用 upstream 指令。
我们把 121.42.11.34 服务器作为上游服务器,做如下配置(/etc/nginx/conf.d/balance.conf):
- server{
- listen 8020;
- location / {
- return 200 'return 8020 \n';
- }
- }
- server{
- listen 8030;
- location / {
- return 200 'return 8030 \n';
- }
- }
- server{
- listen 8040;
- location / {
- return 200 'return 8040 \n';
- }
- }
配置完成后:
把 121.5.180.193 服务器作为代理服务器,做如下配置(/etc/nginx/conf.d/balance.conf):
- upstream demo_server {
- server 121.42.11.34:8020;
- server 121.42.11.34:8030;
- server 121.42.11.34:8040;
- }
- server {
- listen 80;
- server_name balance.lion.club;
- location /balance/ {
- proxy_pass http://demo_server;
- }
- }
配置完成后重启 Nginx 服务器。并且在需要访问的客户端配置好 ip 和域名的映射关系。
- # /etc/hosts
- 121.5.180.193 balance.lion.club
在客户端机器执行 curl http://balance.lion.club/balance/ 命令:
不难看出,负载均衡的配置已经生效了,每次给我们分发的上游服务器都不一样。就是通过简单的轮询策略进行上游服务器分发。
接下来,我们再来了解下 Nginx 的其他分发策略。
hash 算法:通过制定关键字作为 hash key,基于 hash 算法映射到特定的上游服务器中。关键字可以包含有变量、字符串。
- upstream demo_server {
- hash $request_uri;
- server 121.42.11.34:8020;
- server 121.42.11.34:8030;
- server 121.42.11.34:8040;
- }
- server {
- listen 80;
- server_name balance.lion.club;
- location /balance/ {
- proxy_pass http://demo_server;
- }
- }
hash $request_uri 表示使用 request_uri 变量作为 hash 的 key 值,只要访问的 URI 保持不变,就会一直分发给同一台服务器。
ip_hash:根据客户端的请求 ip 进行判断,只要 ip 地址不变就永远分配到同一台主机。它可以有效解决后台服务器 session 保持的问题。
- upstream demo_server {
- ip_hash;
- server 121.42.11.34:8020;
- server 121.42.11.34:8030;
- server 121.42.11.34:8040;
- }
- server {
- listen 80;
- server_name balance.lion.club;
- location /balance/ {
- proxy_pass http://demo_server;
- }
- }
最少连接数算法:各个 worker 子进程通过读取共享内存的数据,来获取后端服务器的信息。来挑选一台当前已建立连接数最少的服务器进行分配请求。
- 语法:least_conn;
- 上下文:upstream;
- 示例:
- upstream demo_server {
- zone test 10M; # zone可以设置共享内存空间的名字和大小
- least_conn;
- server 121.42.11.34:8020;
- server 121.42.11.34:8030;
- server 121.42.11.34:8040;
- }
- server {
- listen 80;
- server_name balance.lion.club;
- location /balance/ {
- proxy_pass http://demo_server;
- }
- }
最后你会发现,负载均衡的配置其实一点都不复杂。
⑤配置缓存
缓存可以非常有效的提升性能,因此不论是客户端(浏览器),还是代理服务器(Nginx),乃至上游服务器都多少会涉及到缓存。可见缓存在每个环节都是非常重要的。下面让我们来学习 Nginx 中如何设置缓存策略。
proxy_cache:存储一些之前被访问过、而且可能将要被再次访问的资源,使用户可以直接从代理服务器获得,从而减少上游服务器的压力,加快整个访问速度。
- 语法:proxy_cache zone | off ; # zone 是共享内存的名称
- 默认值:proxy_cache off;
- 上下文:http、server、location
proxy_cache_path:设置缓存文件的存放路径。
- 语法:proxy_cache_path path [level=levels] ...可选参数省略,下面会详细列举
- 默认值:proxy_cache_path off
- 上下文:http
参数含义:
proxy_cache_key:设置缓存文件的 key 。
- 语法:proxy_cache_key
- 默认值:proxy_cache_key $scheme$proxy_host$request_uri;
- 上下文:http、server、location
proxy_cache_valid:配置什么状态码可以被缓存,以及缓存时长。
- 语法:proxy_cache_valid [code...] time;
- 上下文:http、server、location
- 配置示例:proxy_cache_valid 200 304 2m;; # 说明对于状态为 200 和 304 的缓存文件的缓存时间是 2 分钟
proxy_no_cache:定义相应保存到缓存的条件,如果字符串参数的至少一个值不为空且不等于“ 0”,则将不保存该响应到缓存。
- 语法:proxy_no_cache string;
- 上下文:http、server、location
- 示例:proxy_no_cache $http_pragma $http_authorization;
proxy_cache_bypass:定义条件,在该条件下将不会从缓存中获取响应。
- 语法:proxy_cache_bypass string;
- 上下文:http、server、location
- 示例:proxy_cache_bypass $http_pragma $http_authorization;
upstream_cache_status 变量:它存储了缓存是否命中的信息,会设置在响应头信息中,在调试中非常有用。
- MISS:未命中缓存
- HIT:命中缓存
- EXPIRED:缓存过期
- STALE:命中了陈旧缓存
- REVALIDDATED:Nginx 验证陈旧缓存依然有效
- UPDATING:内容陈旧,但正在更新
- BYPASS:X响应从原始服务器获取
配置实例:我们把 121.42.11.34 服务器作为上游服务器,做如下配置(/etc/nginx/conf.d/cache.conf):
- server {
- listen 1010;
- root /usr/share/nginx/html/1010;
- location / {
- index index.html;
- }
- }
- server {
- listen 1020;
- root /usr/share/nginx/html/1020;
- location / {
- index index.html;
- }
- }
把 121.5.180.193 服务器作为代理服务器,做如下配置(/etc/nginx/conf.d/cache.conf):
- proxy_cache_path /etc/nginx/cache_temp levels=2:2 keys_zone=cache_zone:30m max_size=2g inactive=60m use_temp_path=off;
- upstream cache_server{
- server 121.42.11.34:1010;
- server 121.42.11.34:1020;
- }
- server {
- listen 80;
- server_name cache.lion.club;
- location / {
- proxy_cache cache_zone; # 设置缓存内存,上面配置中已经定义好的
- proxy_cache_valid 200 5m; # 缓存状态为 200 的请求,缓存时长为 5 分钟
- proxy_cache_key $request_uri; # 缓存文件的 key 为请求的URI
- add_header Nginx-Cache-Status $upstream_cache_status # 把缓存状态设置为头部信息,响应给客户端
- proxy_pass http://cache_server; # 代理转发
- }
- }
缓存就是这样配置,我们可以在 /etc/nginx/cache_temp 路径下找到相应的缓存文件。
对于一些实时性要求非常高的页面或数据来说,就不应该去设置缓存,下面来看看如何配置不缓存的内容。
- ...
- server {
- listen 80;
- server_name cache.lion.club;
- # URI 中后缀为 .txt 或 .text 的设置变量值为 "no cache"
- if ($request_uri ~ \.(txt|text)$) {
- set $cache_name "no cache"
- }
- location / {
- proxy_no_cache $cache_name; # 判断该变量是否有值,如果有值则不进行缓存,如果没有值则进行缓存
- proxy_cache cache_zone; # 设置缓存内存
- proxy_cache_valid 200 5m; # 缓存状态为 200的请求,缓存时长为 5 分钟
- proxy_cache_key $request_uri; # 缓存文件的 key 为请求的 URI
- add_header Nginx-Cache-Status $upstream_cache_status # 把缓存状态设置为头部信息,响应给客户端
- proxy_pass http://cache_server; # 代理转发
- }
- }
⑥HTTPS
在学习如何配置 HTTPS 之前,我们先来简单回顾下 HTTPS 的工作流程是怎么样的?它是如何进行加密保证安全的?
HTTPS 工作流程:
这就是 HTTPS 的基本运作原理,使用对称加密和非对称机密配合使用,保证传输内容的安全性。
关于 HTTPS 更多知识,可以查看作者的另外一篇文章:
- https://juejin.cn/post/6844904148601667598#heading-37
配置证书:下载证书的压缩文件,里面有个 Nginx 文件夹,把 xxx.crt 和 xxx.key 文件拷贝到服务器目录,再进行如下配置:
- server {
- listen 443 ssl http2 default_server; # SSL 访问端口号为 443
- server_name lion.club; # 填写绑定证书的域名(我这里是随便写的)
- ssl_certificate /etc/nginx/https/lion.club_bundle.crt; # 证书地址
- ssl_certificate_key /etc/nginx/https/lion.club.key; # 私钥地址
- ssl_session_timeout 10m;
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # 支持ssl协议版本,默认为后三个,主流版本是[TLSv1.2]
- location / {
- root /usr/share/nginx/html;
- index index.html index.htm;
- }
- }
如此配置后就能正常访问 HTTPS 版的网站了。
⑦配置跨域 CORS
先简单回顾下跨域究竟是怎么回事。
跨域的定义:同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。通常不允许不同源间的读操作。
同源的定义:如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。
下面给出了与 URL http://store.company.com/dir/page.html 的源进行对比的示例:
- http://store.company.com/dir2/other.html 同源
- https://store.company.com/secure.html 不同源,协议不同
- http://store.company.com:81/dir/etc.html 不同源,端口不同
- http://news.company.com/dir/other.html 不同源,主机不同
不同源会有如下限制:
Nginx 解决跨域的原理,例如:
现在我在 fe.server.com 对 dev.server.com 发起请求一定会出现跨域。
现在我们只需要启动一个 Nginx 服务器,将 server_name 设置为 fe.server.com 然后设置相应的 location 以拦截前端需要跨域的请求,最后将请求代理回 dev.server.com。如下面的配置:
- server {
- listen 80;
- server_name fe.server.com;
- location / {
- proxy_pass dev.server.com;
- }
- }
这样可以完美绕过浏览器的同源策略:fe.server.com 访问 Nginx 的 fe.server.com 属于同源访问,而 Nginx 对服务端转发的请求不会触发浏览器的同源策略。
⑧配置开启 gzip 压缩
GZIP 是规定的三种标准 HTTP 压缩格式之一。目前绝大多数的网站都在使用 GZIP 传输 HTML、CSS、JavaScript 等资源文件。
对于文本文件,GZiP 的效果非常明显,开启后传输所需流量大约会降至 1/4~1/3 。
并不是每个浏览器都支持 gzip 的,如何知道客户端是否支持 gzip 呢,请求头中的 Accept-Encoding 来标识对压缩的支持。
启用 gzip 同时需要客户端和服务端的支持,如果客户端支持 gzip 的解析,那么只要服务端能够返回 gzip 的文件就可以启用 gzip 了,我们可以通过 Nginx 的配置来让服务端支持 gzip。下面的 respone 中 content-encoding:gzip,指服务端开启了 gzip 的压缩方式。
在 /etc/nginx/conf.d/ 文件夹中新建配置文件 gzip.conf:
- # # 默认 off,是否开启 gzip
- gzip on;
- # 要采用 gzip 压缩的 MIME 文件类型,其中 text/html 被系统强制启用;
- gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
- # ---- 以上两个参数开启就可以支持 Gzip 压缩了 ---- #
- # 默认 off,该模块启用后,Nginx 首先检查是否存在请求静态文件的 gz 结尾的文件,如果有则直接返回该 .gz 文件内容;
- gzip_static on;
- # 默认 off,Nginx 做为反向代理时启用,用于设置启用或禁用从代理服务器上收到相应内容 gzip 压缩;
- gzip_proxied any;
- # 用于在响应消息头中添加 Vary:Accept-Encoding,使代理服务器根据请求头中的 Accept-Encoding 识别是否启用 gzip 压缩;
- gzip_vary on;
- # gzip 压缩比,压缩级别是 1-9,1 压缩级别最低,9 最高,级别越高压缩率越大,压缩时间越长,建议 4-6;
- gzip_comp_level 6;
- # 获取多少内存用于缓存压缩结果,16 8k 表示以 8k*16 为单位获得;
- gzip_buffers 16 8k;
- # 允许压缩的页面最小字节数,页面字节数从 header 头中的 Content-Length 中进行获取。默认值是 0,不管页面多大都压缩。建议设置成大于 1k 的字节数,小于 1k 可能会越压越大;
- # gzip_min_length 1k;
- # 默认 1.1,启用 gzip 所需的 HTTP 最低版本;
- gzip_http_version 1.1;
其实也可以通过前端构建工具例如 webpack、rollup 等在打生产包时就做好 Gzip 压缩,然后放到 Nginx 服务器中,这样可以减少服务器的开销,加快访问速度。
关于 Nginx 的实际应用就学习到这里,相信通过掌握了 Nginx 核心配置以及实战配置,之后再遇到什么需求,我们也能轻松应对。接下来,让我们再深入一点学习下 Nginx 的架构。
①进程结构
多进程结构 Nginx 的进程模型图:
多进程中的 Nginx 进程架构如下图所示,会有一个父进程(Master Process),它会有很多子进程(Child Processes):
②配置文件重载原理
reload 重载配置文件的流程:
③Nginx 模块化管理机制
Nginx 的内部结构是由核心部分和一系列的功能模块所组成。这样划分是为了使得每个模块的功能相对简单,便于开发,同时也便于对系统进行功能扩展。Nginx 的模块是互相独立的,低耦合高内聚。
相信通过本文的学习,你应该会对 Nginx 有一个更加全面的认识。
作者:Lion
编辑:陶家龙
出处:juejin.cn/post/6942607113118023710
超融合基础设施(HCI)集群和机架服务器都为数据中心的部署提供了便利。仍然,找出...
磁盘的分区 装过系统的人都知道必须要分区后才能安装系统,形象点的说比如window...
微服务类型的应用程序依靠快速,可靠的网络基础结构来快速可靠地做出响应,并且...
存储厂商NGD Systems公司副总裁Scott Shadley在一次专题访谈中,探讨了SNIA计算...
相比共享服务器,许多企业更喜欢订购私有服务器进行托管以搭建自己的数据存储方...
前一篇文章针对韩国独立服务器配置如何选择进行了详细的说明,很多用户在读完之...
OCP是一个由Facebook于2011年发起的开放计算社区组织,致力于数据中心创新技术的...
虚拟内存 尽管基址寄存器和变址寄存器用来创建地址空间的抽象,但是这有一个其他...
cn2线路的香港服务器如何?这里小编先给大家解释一下cn2线路是什么?CN2线路是中...
作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,...