前端全栈之路 - 玩转 Nginx (下)

4,225 阅读8分钟

本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!

前言

在今年上半年的时候发布了一篇 Nginx 相关的博文【前端工程化 - 如何玩转 Nginx (上)】,主要介绍了 Nginx 的基本知识点例如: nginx.conf 配置以及反向代理的一些功能,本章承接之前基础内容,与大家一起学习一下 Nginx 的进阶玩法。

在 Docker 中使用

之前的示例中,我们是通过直接下载安装包来安装 Nginx,再学习了 Docker 之后,顺带介绍一下如何使用 Docker 来安装 Nginx 的步骤。

  1. 下载 Nginx 镜像

使用如下命令可以将 Nginx 镜像拉取到本地:

$ docker pull nginx         # 拉取最新版本的 Nginx 
$ docker pull nginx:version # 拉取指定的版本
  1. 创建配置文件:

与普通使用 Nginx 一样,我们也需要创建 nginx.conf 配置文件,除此之外还需要创建日志目录 log 与静态文件目录 html,当然你可以选择自己喜欢的命名规则,这个并不限制。

如果在 linux 系统中使用,建议按照 /home/nginx 的格式来创建对应的目录层级,其它系统也可以参考类似的目录结构。

  1. 运行 Nginx 容器
docker run \
-p 80:80 \
--name nginx \
-v /home/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \
-v /home/nginx/conf/conf.d:/etc/nginx/conf.d \
-v /home/nginx/log:/var/log/nginx \
-v /home/nginx/html:/usr/share/nginx/html \
-d nginx:version

直接运行上述脚本就可以正常启动 Docker 版本的 Nginx 了,后续的其他操作与普通版本的 Nginx 差别不大。

生产环境时 Docker 版本 Nginx 启动之后,可以会将静态文件直接复制到在容器中,而不是像如上直接挂载目录的模式。因为 Docker 为了抹平各个系统之间的差异,在文件存储以及转换上做了非常多的兼容,当宿主机并非是 Linux 并且数据挂载宿主机后读写又很频繁时,就能明显感受到性能上的巨大差异,这点在之前前端工程化的 All In Docker 方案中有提到过。不过实际生产中都是在 Linux 系统中使用 Docker,所以遇到这种问题的情况并不多,即使在 Linux 环境中直接挂载宿主目录,由于文件转储格式差异不大的情况下,性能损耗也保证在可接受范围内。

AB 分流

作为开发大家应该对灰度测试比较熟悉,但是对 AB分流 听上去可能有点熟悉或者也有可能日常中将两者的概念混合在一起.

AB分流 也可以被包括灰度测试中,但大部分的之所以叫灰度测试,是因为灰度的模块一般都是在上线之前,当测试验证完毕之后,就会进行全量推送,而 AB分流 是一般存在应用上线之后,且会延续一段时间。

另外灰度测试中的功能差异点不会很多,一般是线上修复问题或者一些特殊的功能需要少量用户测试,起到验证功能完整性的作用,而 AB分流 根据特殊的规则对不同的用户推送不同的版本,一般是在大量修改功能模块或者推出全新功能想要试验市场反馈进行的操作。

所以一般情况下 AB分流 会基于具体客户端具体信息,例如区域、用户信息等来设置分流规则来进行推送。

当然在大部分的场景或者团队中,如果对细节并非非常在意的情况下,这两者概念与使用可以混淆,问题不大。

根据 Cookie

前面提到了,当我们在处理 AB分流 的时候,大部分的情况都需要根据客户端具体信息来分流,需要获取客户端请求信息的部分内容再进行规则判断转发。

一般情况下,可以将这些信息放在 Http 请求中的 Header 来判断,所以 Cookie 就是一个非常通用且好用的可判断属性,当然你也可以使用其他的字段来处理逻辑,其他所有类似的场景这里也就不再举例展开了。

这个方案通用性比较好,但是也有一定的缺陷,无论是客户端还是服务端插入 Cookie,都只有当客户端访问过一次之后才会产生需要的内容,真正的分流规则只有第二次才会生效。而且单纯的 Nginx 能够获取的信息与规则并不多,在需求复杂的情况下,需要使用网关系统来配合处理,不过简单的分流场景使用以下的规则也已经足够了。

server {
    listen 80;
    server_name fe.cookieboty.com;

    #match cookie
    set $group "html";
    if ($http_cookie ~* "version=1"){ 
        set $group v1;
    }
    if ($http_cookie ~* "version=2"){
        set $group v2;
    }
    location / {            
        root   $group;
        index index.html index.htm;
    }
}

在上述配置规则中,我们在访问真实资源之前,针对该次请求中 Cookieversion 字段进行了判断,根据携带的内容转发到不同的静态文件目录。

当我们请求中的 Cookie 未携带 version 版本的时候,会直接访问默认的 html 资源:

image.png

而当请求中的 Cookie 携带了 version 版本,则访问到对应版本的静态资源:

image.png

根据 IP白名单

除了 Cookie 判断之外,还可以使用 IP白名单 来配置 AB分流,它的配置非常简单,只要简单添加一组默认的 IP 列表即可,但这种模式通常只会在内部系统中使用,毕竟外部的系统 IP 并非是固定的,变化的频率还是非常高的。

server {
    listen 80;
    server_name fe.cookieboty.com;;
    ip_list 192.168.0.1,192.168.0.2

    #match IP
    set $group "html";
    if ($remote_addr in iplist) {
        set $group v1;
    }

    location / { 
        root   $group;
        proxy_set_header Host $host;
    }
}

负载均衡

接着上述的 AB分流 继续展开,除了 CookieIP 之外,负载均衡也是可以做 AB分流,只不过普通的负载均衡是无业务属性的,所以就没在 AB分流 里面讲解而是单独拿出来展开。

负载均衡这是一个在后端服务中常见的名词,对于前端同学可能熟悉但又有些陌生,首先我们来展开一下它的概念:将客户端的请求根据一定的规则分摊到多台服务器上

  • 当其中某一台服务器挂掉后,其他服务器还可以正常提供服务,提高服务可靠与稳定性;
  • 当某一时刻请求过载的时候,可以动态扩容,避免单台服务器负载过高,提高系统的可伸缩性。

Nginx 中开启负载均衡并不复杂,快速开启的配置如下所示:

普通负载

upstream overload {
    server 127.0.0.1:8080; 
    server 127.0.0.1:8081;
    server 127.0.0.1:8082;
}
// 这是一个工程启动了三个不同的端口,不是三个不同的服务,请不要搞错了,此外我没有多台服务所以使用的是多端口来区分。

server {
    listen 80;
    server_name fe.cookieboty.com;;

    location / {
        proxy_pass http://overload;
        proxy_set_header Host $host;
    }
}

除了之前一直在使用的 server 参数之外,又多了一个新的配置参数 upstream(用于调整均衡池和调度方法),同时 proxy_pass 从直接访问 IP 变成了访问 upstream 定义的负载均衡变量。

当你每个服务器的配置相当且没有额外的要求,上述的配置就能基本满足,它会将所有的服务请求均衡的分配到各个配置好的服务器资源池中,当任一服务不可用时,会自动剔除服务,不用担心会一直命中一台有问题的服务器。

权重负载

当服务器资源并不相同比如一台高配搭配两台低配的时候,可以在 server 后面追加 weight 参数,将多数的请求转发到高配服务器上,如下所示:

upstream overload {
    server 127.0.0.1:8080 weight=1; 
    server 127.0.0.1:8081 weight=2; 
    server 127.0.0.1:8082 weight=3; 
}

如上每台服务器分配资源数据为[分配数量/总数],所以 8080 服务只分得 1/6 的请求量,而 8082 服务分配到了 3/6 的请求量。

LRU 负载

在所有请求相似且已知服务器具体配置等情况下,权重负载已经足够使用,但当请求业务请求操作、链路消耗不明确时可以使用 least_conn 参数,当添加该参数时,会将请求自动转发到活跃连接数最小的服务器上:

upstream overload {
    least_conn
    server 127.0.0.1:8080; 
    server 127.0.0.1:8081; 
    server 127.0.0.1:8082; 
}

当同时存在 least_connweight 的时候将选取活跃连接数与权重 weight 的比值最小者为下一个处理请求的服务器

ip_hash

除了上述对负载转发有影响的参数之外,还有一个 ip_hash 参数,ip_hash 是根据用户请求过来的 IP 生成对应的 hash 值:

upstream overload {
    ip_hash;
    server 127.0.0.1:8080; 
    server 127.0.0.1:8081; 
    server 127.0.0.1:8082; 
}

当负载均衡添加 ip_hash 参数之后,可以保证在当前 IP 相同的情况下,每一次请求都会命中同一台服务器,这样会避免用户上下文丢失。如果 IP 改变的话,就没办法保证每一次的请求都会命中同一台服务器。虽然这种情况在日常中也非常常见,比如切换网络、wifi 等,但是也有办法处理这种情况,不过本文就不再展开了。

upstream 除了上述参数之外,还有以下可选参数,具体使用可以参考官方文档,大家可以根据自己的需求自主添加:

参数描述
max_fails最大失败数量,当超过时主动提出资源池
fail_timeout但一个服务器被踢出后重新探测时间
backup备用服务
max_conns允许最大连接数
slow_start当节点恢复,不立即加入

写在最后

Nginx 上下两章的内容到此应该是足够一个前端开发使用了,而本章的内容对于初级前端或者常规前端来说价值并非很大。前端同学大部分对于 Nginx 的使用还是基于反向代理解决浏览器跨域这两个模块,对于更多的深入功能可以交给更专业的运维同学去解决。

这只是强调术业有专攻,毕竟前端自身也是有本职工作,但在开发链路中的知识体系越完备,未来的竞争力也会更强一些,这些知识可能当前用不上,但知道之后面对一些疑难杂症可以很快的理清思路,快速的找到问题所在。

并非所有人都有能力成为 π 型人才,但是还是可以努努力达到 T 型人才的标准

后续大家可以持续关注【前端全栈之路】专栏,会持续更新前端视角能够接触到的全链路非前端的知识体系。当然在全栈体系中,肯定会有些技术深度不够的情况,如果有遇到讲解不够清楚或者个人理解有偏差的情况,大家可以留言指正,及时修改避免误导有需要的读者。

有兴趣的同学可以加我微信号:cookieboty,一起学习前端工程化的内容,互相学习,共同进步。