前端也要掌握的 Nginx 知识

一、前言

Nginx 是一个免费的,开源的,高性能的HTTP服务器和反向代理,以及IMAP / POP3代理服务器。 Nginx 以其高性能,稳定性,丰富的功能,简单的配置和低资源消耗而闻名。在高连接并发的情况下,Nginx是Apache服务器不错的替代品。

最近在给中台系统做系统整合,Nginx 也是微前端实现的方式之一,恰巧也有朋友问了一个 Nginx 相关的问题,敲响警钟。借此机会,对 Nginx 知识进行梳理。

二、基础篇

2.1 安装

2.1.1 Mac

安装 Nginx:

brew install nginx

启动 Nginx:

sudo nginx

nginx.conf 配置文件

/usr/local/etc/nginx/nginx.conf

在浏览器输入 http://localhost:8080/ 就能看到 nginx 默认搭建的服务器,8080 是 nginx 自带的默认网站设置的端口。

2.1.2 Linux

安装编译工具及库文件

yum -y install gcc gcc-c++ autoconf pcre pcre-devel make automake
yum -y install wget httpd-tools vim

建立目录

cd /opt/ mkdir app download logs work backup

确定nginx源

vim /etc/yum.repos.d/nginx.repo

添加如下代码

name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1

注意,上面 centos7 对应操作系统和版本号

安装

yum list | grep nginx
yum install nginx

2.2 基本命令

查看版本号

nginx -v

检查配置文件是否有语法错误

nginx -t

热加载,重新加载配置文件

nginx -s reload

快速关闭

nginx -s stop

等待工作进程处理完成后关闭

nginx -s quit

2.3 基本配置

Nginx 配置的核心是定义要处理的 URL 以及如何响应这些 URL 请求,即定义一系列的虚拟服务器(Virtual Servers)控制对来自特定域名或者 IP 的请求的处理。

每一个虚拟服务器定义一系列的 location 控制处理特定的 URI 集合。每一个 location 定义了对映射到自己的请求的处理场景,可以返回一个文件或者代理此请求。

Nginx 由不同的模块组成,这些模块由配置文件中指定的指令控制。 指令分为简单指令和块指令。

一个简单指令包含指令名称和指令参数,以空格分隔,以分号( ; )结尾。 块指令与简单指令类似,但是由大括号( {} )包围。 如果块指令大括号中包含其他指令,则称该指令为上下文(如: events , http , serverlocation )。

配置文件中的放在上下文之外的指令默认放在主配置文件中(类似继承主配置文件)。 eventshttp 放置在主配置文件中, server 放置在 http 块指令中, location 放置在 server 块指令中。

配置文件的注释以 # 开始。

2.3.1 默认配置文件

# Nginx进程,一般设置为和CPU核数一样
worker_processes  1;   
# 错误日志存放目录
error_log  /var/log/nginx/error.log warn;
# 进程pid存放位置
pid        /var/run/nginx.pid;

events {
    worker_connections  1024; # 单个后台进程的最大并发数
}


http {
    include       /etc/nginx/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"';

    # nginx访问日志存放位置
    access_log  /var/log/nginx/access.log  main;

    # 开启高效传输模式
    sendfile        on;
    # tcp_nopush     on;    # 减少网络报文段的数量

    keepalive_timeout  65;  # 保持连接的时间,也叫超时时间

    # gzip  on;  # 开启gzip压缩

    # 引入其他的配置文件
    include servers/*;
}

2.3.2 虚拟主机配置

# 虚拟主机
server {
     listen       8080;  # 配置监听端口
     server_name  localhost;  # 浏览器访问域名

     charset utf-8;
     access_log  logs/localhost.access.log  access;

     # 路由
     location / {
         root   www; # 访问根目录
         index  index.html index.htm; # 入口文件
     }

     error_page  404              /404.html;   # 配置404页面

     # redirect server error pages to the static page /50x.html
     #
     error_page   500 502 503 504  /50x.html;   # 错误状态码的显示页面,配置后需要重启

     location = /50x.html {
         root   /usr/share/nginx/html;
     }
}

2.3.3 内置变量

下面是 ngin x一些配置中常用的内置全局变量,即可以在配置的任何位置使用它们。

变量名 作用
$host 请求信息中的 Host ,如果请求中没有 Host 行,则等于设置的服务器名
$request_method 客户端请求类型,如 GETPOST
$remote_addr 客户端的 IP 地址
$args 请求中的参数
$content_length 请求头中的 Content-length 字段
$http_user_agent 客户端 agent 信息
$http_cookie 客户端 cookie 信息
$remote_port 客户端的端口
$server_protocol 请求使用的协议,如 HTTP/1.0HTTP/1.1
$server_addr 服务器地址
$server_name 服务器名称
$server_port 服务器的端口号

2.3.4 Listen 指令使用方式

listen address[:port] |port | unix:path
Nginx 服务的 Listen 监听可以设置 ip:portport 或者套接字

除此之外还有补充参数,如下(更详细的内容查看文档):

参数 作用
default_server 指定此虚拟主机为默认主机(监听在同一个端口中);如果没有,则第一个虚拟主机为默认主机。
ssl 指定此端口上接受的连接在ssl模式下工作。
http2 配置端口接受http2的连接;
spdy 端口接受spdy的连接。
proxy_protocol 允许端口接受的连接使用代理服务器协议。
setfib 设置相关路由表
fastopen TCP快速打开,提高两端点间连接的打开速度。不建议使用。
backlog 设置连接队列的最大长度。
rcvbuf 套接字接收缓冲区大小
sndbuf 套接字发送缓冲区大小
accept_filter 过滤器
deferred 延迟
bind 绑定一个 address:port 对。
ipv6only on / off
reuseport 为每个工作进程创建一个单独的监听套接字
so_keepalive on|off|[keepidle]:[keepintvl]:[keepcnt]

三、实践篇

3.1 正向代理

正向代理,是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。

正向代理是为客户端服务的,客户端可以根据正向代理访问到它本身无法访问到的服务器资源。

正向代理对客户端是透明的,对服务端是非透明的,即服务端并不知道自己收到的是来自代理的访问还是来自真实客户端的访问。

3.2 反向代理

反向代理(Reverse Proxy)方式是指以代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

反向代理是为服务端服务的,反向代理可以帮助服务器接收来自客户端的请求,帮助服务器做请求转发,负载均衡等。

反向代理对服务端是透明的,对客户端是非透明的,即我们并不知道自己访问的是代理服务器,而服务器知道反向代理在为他服务。

3.3 资源缓存

3.4 https服务

首先需要签署第三方可信任的 SSL 证书,可以使用 Let's Encrypt 或者通过云服务商创建免费的 SSL 证书。

第二步为服务配置 SSL 证书,在需要配置 https 的服务中监听 443 端口,定义 SSL 证书文件和私钥文件,基本配置如下所示(更多参数配置查看文档):

server {
   #ssl参数
   listen              443 ssl http2;
   server_name         example.com;
   #证书文件
   ssl_certificate    /usr/local/nginx/conf/ssl/example.com.crt;
   #私钥文件
   ssl_certificate_key /usr/local/nginx/conf/ssl/example.com.key;
}

3.5 负载均衡

上面提到正向代理和反向代理,负载均衡就是反向代理的应用。

当一个应用单位时间内访问量激增,服务器的带宽及性能受到影响,影响大到自身承受能力时,服务器就会宕机奔溃,为了防止这种现象发生,以及实现更好的用户体验,我们可以通过配置Nginx负载均衡的方式来分担服务器压力。

当有一台服务器宕机时,负载均衡器就分配其他的服务器给用户,极大的增加的网站的稳定性。当用户访问web时候,首先访问到的是负载均衡器,再通过负载均衡器将请求转发给后台服务器。

3.5.1 负载均衡的几种常见策略

轮询策略(默认)

将所有客户端请求轮询分配给服务端。这种策略是可以正常工作的,但是如果其中某一台服务器压力太大,出现延迟,会影响所有分配在这台服务器下的用户。

// nginx.config
upstream balanceServer {
    server 192.168.0.1;
    server 192.168.0.2;
}

权重策略

指定不同ip的权重,权重与访问比成正相关,权重越高,访问越大,适用于不同性能的机器

// nginx.config
upstream balanceServer {
    server 192.168.0.1 weight=2;
    server 192.168.0.2 weight=8;
}

最小连接数策略 least_conn

把请求分配给连接数较少的后端服务器,它可以平衡每个队列的长度,并避免向压力大的服务器添加更多的请求。

upstream balanceServer {
    least_conn;
    server 10.1.22.33:12345;
    server 10.1.22.34:12345;
    server 10.1.22.35:12345;
}

客户端ip绑定 ip_hash

来自同一个ip的请求永远只分配一台服务器,有效解决了动态网页存在的session共享问题。

upstream balanceServer {
    ip_hash;
    server 10.1.22.33:12345;
    server 10.1.22.34:12345;
    server 10.1.22.35:12345;
}

最快响应时间策略 fair(第三方)

会将请求优先分配给相应最快的服务器,这种方式需要依赖到第三方插件 nginx-upstream-fair

upstream balanceServer {
    fair;
    server 10.1.22.33:12345;
    server 10.1.22.34:12345;
    server 10.1.22.35:12345;
}

url_hash(第三方)

按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。

upstream balanceServer {
    hash $request_uri;
    server 192.168.244.1:8080;
    server 192.168.244.2:8080;
    server 192.168.244.3:8080;
    server 192.168.244.4:8080;
}

3.5.2 健康检查

Nginx 自带 ngx_http_upstream_module (健康检测模块)本质上服务器心跳的检查,通过定期轮询向集群里的服务器发送健康检查请求,来检查集群中是否有服务器处于异常状态。

如果检测出其中某台服务器异常,那么在通过客户端请求nginx反向代理进来的都不会被发送到该服务器上(直至下次轮训健康检查正常)

例子:

upstream balanceServer{
    server 192.168.0.1  max_fails=1 fail_timeout=40s;
    server 192.168.0.2  max_fails=1 fail_timeout=40s;
}

server {
    listen 80;
    server_name localhost; 
    location / {
      proxy_pass http://balanceServer;
    }
}

涉及两个配置:

fail_timeout : 设定服务器被认为不可用的时间段以及统计失败尝试次数的时间段,默认为10s

max_fails : 设定Nginx与服务器通信的尝试失败的次数,默认为:1次

3.6 静态资源服务器

location ~* \.(png|gif|jpg|jpeg)$ {
    root    /root/static/;
    autoindex on;
    access_log  off;
    expires     10h; # 设置过期时间为10小时
}

匹配以 png|gif|jpg|jpeg 为结尾的请求,并将请求转发到本地路径, root 中指定的路径即 nginx 本地路径。同时也可以进行一些缓存的设置。

3.7 适配PC与移动环境

当用户从移动端打开PC端 baidu.com 的场景时,将自动跳转指移动端 m.baidu.com ,本质上是Nginx可以通过内置变量 $http_user_agent ,获取到请求客户端的 userAgent ,从而知道当前用户当前终端是移动端还是PC,进而重定向到H5站还是PC站。

server {
 location / {
        /# 移动、pc设备agent获取
        if ($http_user_agent ~* '(Android|webOS|iPhone)') {
            set $mobile_request '1';
        }
        if ($mobile_request = '1') {
            rewrite ^.+ http://m.baidu.com;
        }
    }
}

3.8 配置gzip

Gzip是网页的一种网页压缩技术,经过gzip压缩后,页面大小可以变为原来的30%甚至更小。更小的网页会让用户浏览的体验更好,速度更快。gzip网页压缩的实现需要浏览器和服务器的支持。

server{
    gzip on;  # 于开启或 关闭gzip模块
    gzip_buffers 32 4K; # 设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流。
    gzip_comp_level 6;  # 压缩级别,1-10,数字越大压缩的越好,压缩级别越高压缩率越大,压缩时间越长。
    gzip_min_length 100;  # 设置允许压缩的页面最小字节数,页面字节数从相应消息头的Content-length中进行获取。
    gzip_types application/javascript text/css text/xml;
    gzip_disable "MSIE [1-6]\.";  # IE6对Gzip不友好,对Gzip(可以通过该指令对一些特定的User-Agent不使用压缩功能)
    gzip_proxied on: # 用于设置启用或禁用从代理服务器上收到相应内容gzip压缩。
    gzip_http_version  1.1;   # 识别HTTP协议版本,其值可以是 1.1 或 1.0
    gzip_proxied : off; # 用于设置启用或禁用从代理服务器上收到相应内容gzip压缩。
    gzip_vary on;  # 用于在响应消息头中添加Vary:Accept-Encoding,使代理服务器根据请求头中的Accept-Encoding识别是否启用gzip压缩。
}

3.9 解决跨域问题

server {
    listen       80;
    server_name  aaa.xxxx.com;
    location / {
        proxy_pass  bbb.xxxx.com;
    }
}

3.10 请求过滤

根据状态码过滤

error_page 500 501 502 503 504 506 /50x.html;
    location = /50x.html {
        #将跟路径改编为存放html的路径。
        root /root/static/html;
    }

根据URL名称过滤,精准匹配URL,不匹配的URL全部重定向到主页

location / {
    rewrite  ^.*$ /index.html  redirect;
}

根据请求类型过滤

if ( $request_method !~ ^(GET|POST|HEAD)$ ) {
    return 403;
}

3.11 防盗链

可以防止资源被其他网站调用

location ~* \.(gif|jpg|png)$ {
    # 只允许 192.168.0.1 请求资源
    valid_referers none blocked 192.168.0.1;
    if ($invalid_referer) {
       rewrite ^/ http://$host/logo.png;
    }
}

3.12 访问权限控制

可以配置 nginx 白名单,规定哪些ip可以访问服务器。

简单配置

location / {
        allow  192.168.0.1;  # 允许该ip访问
        deny   all;  # 禁止所有
    }

建立白名单

vim /etc/nginx/white_ip.conf
 ...
192.168.0.1 1;
 ...

修改 nginx 配置

geo $remote_addr $ip_whitelist{
    default 0;
    include ip.conf;
}
# geo 指令主要是可以根据指定变量的值映射出一个新变量。 如果不指定变量,默认为$remote_addr

使用白名单

server {
    location / {
        if ( $ip_whitelist = 0 ){
            return 403; //不在白名单返回 403
        }
        index index.html;
        root /tmp;
    }
}