后端

Nginx学习实践&总结

Nick · 11月30日 · 2021年 · · 本文10049字 · 阅读26分钟706

1. 简介

Nginx 是一个异步框架的 Web 服务器,也可以用作反向代理,负载平衡器 和 HTTP 缓存。

2. 安装

2.1、安装gcc环境
yum install gcc-c++
2.2、安装pcre
yum install -y pcre pcre-devel
2.3、安装zlib
yum install -y zlib zlib-devel
2.4、安装openssl
yum install -y openssl openssl-devel
2.5、安装Nginx
下载源代码包

wget http://nginx.org/download/nginx-1.18.0.tar.gz
Nginx官网下载地址:http://nginx.org/en/download.html

解压并安装 Nginx:

# tar -xvf nginx-1.18.0.tar.gz
# cd nginx-1.18.0
# ./configure
# make
# make install

2.6、修改配置文件 nginx.conf
vi /usr/local/nginx/conf/nginx.conf

3. 运行

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

如果报 [error] open() "/usr/local/nginx/logs/nginx.pid" failed (2: No such file or directory) 错误,则执行
/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf 命令后再次启动运行 Nginx。

查看 Nginx 启动状态

ps -ef | grep nginx

注意:外部访问时要关闭防火墙。

访问 IP 地址(本地或者虚拟机的ip):192.168.137.130
Nginx学习实践&总结-左眼会陪右眼哭の博客

Nginx 相关命令:

./nginx -s reload            # 重新载入配置文件
./nginx -s reopen            # 重启 Nginx
./nginx -s stop              # 停止 Nginx
./nginx -s quit              # 关闭 Nginx

4. Nginx 的典型配置:

典型配置示例

user  nginx;                        # 运行用户,默认即是nginx,可以不进行设置
worker_processes  1;                # Nginx 进程数,一般设置为和 CPU 核数一样
error_log  /var/log/nginx/error.log warn;   # Nginx 的错误日志存放目录
pid        /var/run/nginx.pid;      # Nginx 服务启动时的 pid 存放位置

events {
    use epoll;     # 使用epoll的I/O模型(如果你不知道Nginx该使用哪种轮询方法,会自动选择一个最适合你操作系统的)
    worker_connections 1024;   # 每个进程允许最大并发数
}

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 {
        listen       80;       # 配置监听的端口
        server_name  localhost;    # 配置的域名

        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;   # 同上
    }
}

server 块可以包含多个 location 块,location 指令用于匹配 uri,语法:

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

指令后面:
* = 精确匹配路径,用于不含正则表达式的 uri 前,如果匹配成功,不再进行后续的查找;

  • ^~ 用于不含正则表达式的 uri 前,表示如果该符号后面的字符是最佳匹配,采用该规则,不再进行后续的查找;
  • ~ 表示用该符号后面的正则去匹配路径,区分大小写;
  • ~* 表示用该符号后面的正则去匹配路径,不区分大小写。跟 ~ 优先级都比较低,如有多个location的正则能匹配的话,则使用正则表达式最长的那个;
    如果 uri 包含正则表达式,则必须要有 ~ 或 ~* 标志。

全局变量

Nginx 有一些常用的全局变量,你可以在配置的任何位置使用它们,如下表:
Nginx学习实践&总结-左眼会陪右眼哭の博客

5. 反向代理

Nginx 实现反向代理很简单,只需要添加:
proxy_pass http://xxxxxxxxxx;
如下配置:

server {
        listen       80;
        server_name  localhost;

        location / {
            proxy_pass http://180.101.49.11;
            index  index.html index.htm index.jsp;
        }
}

其中,http://180.101.49.11 是百度的 ip 地址,重新载入配置文件并重启后,访问 http://服务器ip地址,会自动返回百度数据。

6. 跨域问题

6.1、使用反向代理解决跨域

使用192.168.137.130服务器web请求192.168.137.131服务器接口。
如下配置一:

server {
        listen       192.168.137.130;
        server_name  localhost;

        location / {
            proxy_pass 192.168.137.131;
            index  index.html index.htm index.jsp;
        }
    }

这样就将对前一个域名 192.168.137.130 的请求全都代理到了 192.168.137.131,前端的请求都被我们用服务器代理到了后端地址下,绕过了跨域。

这里对静态文件的请求和后端服务的请求都以 192.168.137.130 开始,不易区分,所以为了实现对后端服务请求的统一转发,通常我们会约定对后端服务的请求加上 /apis/ 前缀或者其他的 path 来和对静态资源的请求加以区分,此时我们可以这样配置
如下配置二:

# 请求跨域,约定代理后端服务请求path以/apis/开头
location ^~/apis/ {
    # 这里重写了请求,将正则匹配中的第一个分组的path拼接到真正的请求后面,并用break停止后续匹配
    rewrite ^/apis/(.*)$ /$1 break;
    proxy_pass 192.168.137.131;

    # 两个域名之间cookie的传递与回写
    proxy_cookie_domain 192.168.137.131 192.168.137.130;

6.2、 配置 header 解决跨域

当浏览器在访问跨源的服务器时,也可以在跨域的服务器上直接设置 Nginx,从而前端就可以无感地开发,不用把实际上访问后端的地址改成前端服务的地址,这样可适性更高。
比如前端站点是 192.168.137.130,这个地址下的前端页面请求 192.168.137.131 下的资源。两台不同服务器之间请求明显会出现跨域,我们可以在配置文件中新建一个配置,对应跨域的服务器 192.168.137.130 :

server {
  listen       80;
  server_name  192.168.137.131;

    add_header 'Access-Control-Allow-Origin' $http_origin;   # 全局变量获得当前请求origin,带cookie的请求不支持*
    add_header 'Access-Control-Allow-Credentials' 'true';    # 为 true 可带上 cookie
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';  # 允许请求方法
    add_header 'Access-Control-Allow-Headers' $http_access_control_request_headers;  # 允许请求的 header,可以为 *
    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';

  if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Max-Age' 1728000;   # OPTIONS 请求的有效期,在有效期内不用发出另一条预检请求
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;

        return 204;                  # 200 也可以
    }

    location / {
        root  /usr/share/nginx/html/be;
        index index.html;
    }
}

然后 nginx -s reload 重新加载配置。访问192.168.137.130,解决了跨域问题。

7. 开启 gzip 压缩

gzip 是一种常用的网页压缩技术,传输的网页经过 gzip 压缩之后大小通常可以变为原来的一半甚至更小(官网原话),更小的网页体积也就意味着带宽的节约和传输速度的提升,特别是对于访问量巨大大型网站来说,每一个静态资源体积的减小,都会带来可观的流量与带宽的节省。
Nginx学习实践&总结-左眼会陪右眼哭の博客
测试 Nginx的gzip压缩的网站:http://tool.chinaz.com/Gzips
Nginx学习实践&总结-左眼会陪右眼哭の博客

7.1、Nginx 配置 gzip
使用 gzip 不仅需要 Nginx 配置,浏览器端也需要配合,需要在请求消息头中包含 Accept-Encoding: gzip(IE5 之后所有的浏览器都支持了,是现代浏览器的默认设置)。一般在请求 html 和 css 等静态资源的时候,支持的浏览器在 request 请求静态资源的时候,会加上 Accept-Encoding: gzip 这个 header,表示自己支持 gzip 的压缩方式,Nginx 在拿到这个请求的时候,如果有相应配置,就会返回经过 gzip 压缩过的文件给浏览器,并在 response 相应的时候加上 content-encoding: gzip 来告诉浏览器自己采用的压缩方式(因为浏览器在传给服务器的时候一般还告诉服务器自己支持好几种压缩方式),浏览器拿到压缩的文件后,根据自己的解压方式进行解析。

为了方便管理,还是在 /etc/nginx/conf/ 文件夹中新建配置文件 gzip.conf :

# /etc/nginx/conf/gzip.conf

gzip on; # 默认off,是否开启gzip
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

# 上面两个开启基本就能跑起了,下面的愿意折腾就了解一下
gzip_static on;
gzip_proxied any;
gzip_vary on;
gzip_comp_level 6;
gzip_buffers 16 8k;
# gzip_min_length 1k;
gzip_http_version 1.1;

相关说明:
* gzip_types:要采用 gzip 压缩的 MIME 文件类型,其中 text/html 被系统强制启用;
* gzip_static:默认 off,该模块启用后,Nginx 首先检查是否存在请求静态文件的 gz 结尾的文件,如果有则直接返回该 .gz 文件内容;
* gzip_proxied:默认 off,nginx做为反向代理时启用,用于设置启用或禁用从代理服务器上收到相应内容 gzip 压缩;
* gzip_vary:用于在响应消息头中添加 Vary:Accept-Encoding,使代理服务器根据请求头中的 Accept-Encoding 识别是否启用 gzip 压缩;
* gzip_comp_level:gzip 压缩比,压缩级别是 1-9,1 压缩级别最低,9 最高,级别越高压缩率越大,压缩时间越长,建议 4-6;
* gzip_buffers:获取多少内存用于缓存压缩结果,16 8k 表示以 8k * 16 为单位获得;
* gzip_min_length:允许压缩的页面最小字节数,页面字节数从header头中的 Content-Length 中进行获取。默认值是 0,不管页面多大都压缩。建议设置成大于 1k 的字节数,小于 1k 可能会越压越大;
* gzip_http_version:默认 1.1,启用 gzip 所需的 HTTP 最低版本;

8. 配置负载均衡

负载均衡主要思想是把负载均匀合理地分发到多个服务器上,实现压力分流的目的。
主要配置如下:

http {
  upstream myserver {
    # ip_hash;  # ip_hash 方式
    # fair;   # fair 方式
    server 127.0.0.1:8081;  # 负载均衡目的服务地址backup;
    server 127.0.0.1:8080;
    server 127.0.0.1:8082 weight=10;  # weight 方式,不写默认为 1
    server 127.0.0.1:8081 backup; 备份服务器
  }

  server {
    location / {
        proxy_pass http://myserver;
      proxy_connect_timeout 10;
    }
  }
}

Nginx 提供了好几种分配方式,默认为轮询,就是轮流来。有以下几种分配方式:
* 轮询,默认方式,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务挂了,能自动剔除;
* weight,权重分配,指定轮询几率,权重越高,在被访问的概率越大,用于后端服务器性能不均的情况;
* ip_hash,每个请求按访问 IP 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决动态网页 session 共享问题。负载均衡每次请求都会重新定位到服务器集群中的某一个,那么已经登录某一个服务器的用户再重新定位到另一个服务器,其登录信息将会丢失,这样显然是不妥的;
* fair(第三方),按后端服务器的响应时间分配,响应时间短的优先分配,依赖第三方插件 nginx-upstream-fair,需要先安装;

备份服务器:只要在希望成为后备的服务器ip后面多添加一个backup参数,这台服务器就会成为备份服务器。
在平时不使用,nginx不会给它转发任何请求。只有当其他节点全部无法连接的时候,nginx才会启用这个节点。一旦有可用的节点恢复服务,该节点则不再使用,又进入后备状态。

9. 适配 PC 或移动设备

根据用户设备不同返回不同样式的站点,以前经常使用的是纯前端的自适应布局,但无论是复杂性和易用性上面还是不如分开编写的好,比如我们常见的淘宝、京东......这些大型网站就都没有采用自适应,而是用分开制作的方式,根据用户请求的 user-agent 来判断是返回 PC 还是 H5 站点。

简单的配置如下:

server {
  listen 80;
    server_name 192.168.137.130;

    location / {
        root  /usr/share/nginx/html/pc;
    if ($http_user_agent ~* '(Android|webOS|iPhone|iPod|BlackBerry)') {
      root /usr/share/nginx/html/mobile;
    }
        index index.html;
    }
}

10. 配置 HTTPS

配置HTTPS其实很简单,可以使用购买的某某云域名,一般都会有免费申请的服务器证书,安装直接看所在云的操作指南即可。
下载证书的压缩文件,里面有个 nginx 文件夹,把 xxx.crt 和 xxx.key 文件拷贝到服务器目录,再配置下:

server {
  listen 443 ssl http2 default_server;   # SSL 访问端口号为 443
  server_name qkongtao.cn;         # 填写绑定证书的域名

  ssl_certificate /etc/nginx/https/*********.crt;   # 证书文件地址
  ssl_certificate_key /etc/nginx/*********.key;      # 私钥文件地址
  ssl_session_timeout 10m;

  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;      #请按照以下协议配置
  ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
  ssl_prefer_server_ciphers on;

  location / {
    root         /usr/share/nginx/html;
    index        index.html index.htm;
  }
}

写完 nginx -t -q 校验一下,没问题就 nginx -s reload,现在去访问就能访问 HTTPS 版的网站了。
一般还可以加上几个增强安全性的命令:

add_header X-Frame-Options DENY;           # 减少点击劫持
add_header X-Content-Type-Options nosniff; # 禁止服务器自动解析资源类型
add_header X-Xss-Protection 1;             # 防XSS攻击

由于自己的网站使用太多的二级域名,很多链接和资源都来源于二级域名。自己也层尝试过很多次配置HTTPS,但是二级域名或者某链接总是出现各种问题,折腾了很久,后来还是放弃了。。。

11. 常用小技巧

1)、静态服务

server {
  listen       8000;
  server_name  static.qkongtao.cn;
  charset utf-8;    # 防止中文文件名乱码

  location /download {
    alias             /usr/share/nginx/html/static;  # 静态资源目录

    autoindex               on;    # 开启静态资源列目录
    autoindex_exact_size    on;   # on(默认)显示文件的确切大小,单位是byte;off显示文件大概大小,单位KB、MB、GB
    autoindex_localtime     off;   # off(默认)时显示的文件时间为GMT时间;on显示的文件时间为服务器时间
  }
}

2)、图片防盗链

server {
  listen       80;
  server_name  qkongtao.cn;

  # 图片防盗链
  location ~* \.(gif|jpg|jpeg|png|bmp|swf)$ {
    valid_referers none blocked 192.168.137.130;  # 只允许本机 IP 外链引用
    if ($invalid_referer){
      return 403;
    }
  }
}

3)、请求过滤

# 非指定请求全返回 403
if ( $request_method !~ ^(GET|POST|HEAD)$ ) {
  return 403;
}

location / {
  # IP访问限制(只允许IP是 192.168.0.2 机器访问)
  allow 192.168.0.2;
  deny all;

  root   html;
  index  index.html index.htm;
}

4)、配置图片、字体等静态文件缓存

由于图片、字体、音频、视频等静态文件在打包的时候通常会增加了 hash,所以缓存可以设置的长一点,先设置强制缓存,再设置协商缓存;如果存在没有 hash 值的静态文件,建议不设置强制缓存,仅通过协商缓存判断是否需要使用缓存。

# 图片缓存时间设置
location ~ .*\.(css|js|jpg|png|gif|swf|woff|woff2|eot|svg|ttf|otf|mp3|m4a|aac|txt)$ {
    expires 10d;
}

# 如果不希望缓存
expires -1;

5)、单页面项目 history 路由配置

server {
  listen       80;
  server_name  fe.qkongtao.cn;

  location / {
    root       /usr/share/nginx/html/dist;  # vue 打包后的文件夹
    index      index.html index.htm;
    try_files  $uri $uri/ /index.html @rewrites;

    expires -1;                          # 首页一般没有强制缓存
    add_header Cache-Control no-cache;
  }

  # 接口转发,如果需要的话
  #location ~ ^/api {
  #  proxy_pass http://be.qkongtao.cn;
  #}

  location @rewrites {
    rewrite ^(.+)$ /index.html break;
  }
}

6)、HTTP 请求转发到 HTTPS

配置完 HTTPS 后,浏览器还是可以访问 HTTP 的地址http://qkongtao.cn/ 的,可以做一个 301 重定向跳转,把对应域名的 HTTP 请求重定向到 HTTPS 上
下面给出三种重定向的情况:

server {
    listen      80;
    server_name www.qkongtao.cn;

    # 单域名重定向
    if ($host = 'www.qkongtao.cn'){
        return 301 https://www.qkongtao.cn$request_uri;
    }
    # 全局非 https 协议时重定向
    if ($scheme != 'https') {
        return 301 https://$server_name$request_uri;
    }

    # 或者全部重定向
    return 301 https://$server_name$request_uri;

    # 以上配置选择自己需要的即可,不用全部加
}

7)、泛域名路径分离

这是一个非常实用的技能,经常有时候我们可能需要配置一些二级或者三级域名,希望通过 Nginx 自动指向对应目录,通过正则表达式和nginx的规则,比如:
test1.doc.qkongtao.cn 自动指向 /usr/share/nginx/html/doc/test1服务器地址;
test2.doc.qkongtao.cn 自动指向 /usr/share/nginx/html/doc/test2服务器地址;

server {
    listen       80;
    server_name  ~^([\w-]+)\.doc\.qkongtao\.cn$;
    root /usr/share/nginx/html/doc/$1;
}

8)、泛域名转发

比如我希望任何的二、三级域名或者我未配置二、三级的域名都可以访问,并且最终都会回到主域名上,nginx就可以根据路由解析不同的规则,比如:
test1.serv.qkongtao.cn/api?name=a 自动转发到 qkongtao.cn/test1/api?name=a ;
test2.serv.qkongtao.cn/api?name=a 自动转发到 qkongtao.cn/test2/api?name=a ;

server {
    listen       80;
    server_name ~^([\w-]+)\.serv\.qkongtao\.cn$;
    location / {
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        Host $http_host;
        proxy_set_header        X-NginX-Proxy true;
        proxy_pass              http://qkongtao.cn/$1$request_uri;
    }
}

11. 小结

本次Nginx的学习是整理了以前用到,但零零闪闪的知识点。Nginx对于后端来说是一个很重要的服务器工具,但是如果没有系统的学习,可以会用,但是会不知所以然的感觉,这次自己花了点时间,看了很多网上的博文和教程,再根据自己的实践和工作经历,总结了一下属于自己的Nginx的知识点。Nginx其实还有很多高级的内容,但是还是需要在工作或者实践中用到才会慢慢的熟悉和总结,没有笔记还是会很容易忘记的。

0 条回应
在线人数:1人 来访统计
隐藏