• 作者:老汪软件技巧
  • 发表时间:2024-08-26 21:02
  • 浏览量:

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

前文有提到前端项目容器化部署和静态资源部署的两种方式,核心的区别就是在于部署的这个文件服务器在谁的手里管辖。如果说是纯上传静态资源就可以完成部署工作,那么服务器的管辖大概率在运维同学手里;如果说是容器化部署,那意味着我们可以自己管理自己的文件服务器——Nginx。

本文不会细致讲解从零到一的 Nginx 配置、高级玩法,更多是通过我个人遇到的一些实战场景,跟大家一起从真实的角度了解这个我们熟悉又陌生的朋友。

前言

尽管 nginx 相比于 html、css、js 这三剑客来说,并不是前端的必修课,但是它的存在对于整个前端项目来说也不容小觑,毕竟项目经开发完上线,它就要开始发力了。

了解 nginx 可以让我们更加清楚整个 web前端 的工作链路;基于对整生态的上游下熟知从而更加快速地定位一些问题;甚至可以在面试中应对某些开放性场景中表现得游刃有余...

登录场景

企业中,一般都会有一套统一的登录、鉴权的流程。这一套路程中,中间可能存在多个节点,每个节点都各司其职,最终完成一个用户的鉴权、跳登录的这么一个流程。如果此时前端项目的 nginx 不在你手上,你大概率可以不用管这些,只要有关登录失败的问题,直接找运维查日志再找他们就好...

但是如果前端部署的 nginx 是你自己在管理,那么你大概率需要了解这其中的登录上下游关系,并且根据相应的节点做一定的跳转处理,从而完成整个登录流程的接入。以至于出现一些登录异常的问题时,你也需要自己跑到 nginx 里查看对应的日志,来定位一些问题。

如上图所示,我简单的ga了一个鉴权、跳登录的流程图。当前端页面发起一个 /user 的请求获取用户信息时,如果出现鉴权不通过需要跳转登录流程,那承载前端静态资源的 nginx 服务器就需要做一些关于登录的跳转配置了。

面试场景

关于面试场景,能拉上 nginx 来说的就太多了,我就简单例举几个能想到的例子吧。其实大厂面试官很喜欢问一些开放性的问题,就好比:页面白屏、登录失败、为什么会404、分析首屏加载慢的情况这种...相信经历过鹅厂面试的同学多多少少都遇到过,那么针对这些面试题,如果仅仅从纯前端技术的角度回答,确实显得有些苍白无力了...

为什么这么说?因为这类问题大多数都是比较全面性的问题,前端技术并不能完全覆盖其中。而且我相信面试官这样问的目的,也不仅仅是纯考察你的前端技术,我更愿意相信他们是想全面考察你对整个 web 应用的理解有多少。

比如我自己,不认识 nginx 的时候,回答白屏、登录失败、加载慢等这种开放性问题,也只能从前端角度出发。诸如什么js执行出错、网络问题、服务器挂了啊等等,反正背过的没背过的八股文都一拥而上了。但是后来,实战玩过 nginx 后,了解了众多的上下游服务等链路,或许我在回答上可以多一些维度了。比如我从这些角度进行思考:

nginx 进程问题。真实遇到 openresty 中存在死循环 lua 脚本导致 nginx 进程跑满 cpu,此时浏览器访问所有页面都无法加载(纯崩溃状态。配置错误导致404。我们都知道当访问不存在的资源时就会获得 404 的状态码,但其实,并不是所有的 404 状态码都是资源不存在而导致的,可能是有坑人的配置(下文会详细讲。nginx 重试配置。当遇到请求慢的情况,也有可能是在 nginx 配置了 proxy_next_upstream ,当某个请求上游出错、超时等情况出现时,会尝试转发到下一个上游服务器。这也会导致请求慢的问题,毕竟多次请求的耗时是叠加的,对于用户端来说,请求总时间就长了。登录失败——url 长度太长。用户携带了大量的 params 跳转登录,当业务服务器处理完成返回跳转原登录url时,如果 header 超出 nginx 配置的缓冲区大小也会出现登录错误。如:upstream sent too big header while reading response header from upstream 这种的 nginx 错误。

总之,当结合 nginx 的场景再去回答上述开放性面试题时,我相信整个回答的完整度、知识覆盖面等都会比仅仅从前端出发的角度要好,或许这也会为你的面试加上一丢丢的分数。所以,了解、学习 nginx 对于前端同学来说,还是有一定必要性的,不管是应对工作中的一些业务场景,或者是面试。

实战相关如何用 nginx 部署

前面讲容器化部署的时候,我并没有展开来说,但其实这部分并不复杂,我这里简单提一下。首先我跟大家演示一下,真实前端项目部署上线后,是怎么运行起来的。开发时本地有 vite、webpack 给我们预制好的 dev server,那么当我们将项目打包成 dist 后,应该怎么搞呢?

我这里通过 vite create 搞了个初始化的项目,本地启动后如下图所示。(为了好区分,我特地加了一句:Nginx Test)

基于这个项目,运行打包命令后,我们便会得到一个 dist 包。产物中可以找到我自己加入得那一句 Nginx Test:

这时候,我就把对应的这个 dist 包,拉到我本地装了 nginx 的一个目录中。并且把 nginx 的配置做了下改动:

紧接着我通过 nginx -g "daemon off;" 命令运行 nginx。-g "daemon off;"这个参数大家不用关注也行,它只是我用于让 nginx 前台运行,方便随时停掉的而已(不然有守护进程,停掉比较麻烦。这时候,当我们访问 localhost 应该就能看到我们的 web 界面了(默认 80 端口:

那么,我们在容器化部署时,只需要遵循这样的配置方式,基于 nginx 的基础镜像,就可以打出一个前端项目的镜像包了。当我们基于该镜像启动容器时,运行 nginx 的启动命令,如无意外的情况我们的项目就成功部署好了。

简单看看 Dockerfile 的写法(详细大家可以去官方的nginx镜像仓库看看:

FROM nginx
COPY nginx.conf /etc/nginx/conf.d/
COPY ./dist/ /usr/share/nginx/html/
CMD ["/bin/bash", "-c", "nginx -g 'daemon off;'"]

上述的每一行你都可以在官方的镜像仓库中找到。东西不多,我带着大家一行行看下:

复制我们项目中的 nginx.conf 配置到一个位置;复制 dist 包到一个位置;nginx 前台启动命令

这样,前端项目的容器化部署就搞定了。不过这一切操作,都不会包含上一篇文章提到的重启容器后静态资源丢失的处理,如需处理重启容器资源丢失问题,请参考上一篇文章的讲解。

真真假假 404

这个熟悉页面一定是众多前端的噩梦!看到它就意味着有问题!不过不知道你有没有想过这么一个问题:怎么出现这个界面的呢?这个界面是由谁写的呢?或者更高级一点的,如 openresty,50x 报错的时候会返回如下的界面:

其实很多线上项目中,通常 web服务器 会因为某些异常状态而返回对应的一些页面(如上述 nginx 的404,openresty 的 50x。这些大部分都不是我们自己开发的(除非自己有需求开发并且配置到 nginx 上了),所以当我第一次在自己域名的项目中遇到一些奇怪的页面,还以为服务器被黑了呢...

那么回到 404 这个问题,我们都知道访问不存在的资源就会 404,那么如果项目是基于默认的 nginx 启动运行的,404 的时候就会返回上面那个大家都比较熟悉的页面。如果我对相应的配置改动一下,如:

error_page  404              /nb.html;
location = /nb.html {
    root   html;
}

当我们再次访问不存在资源时,页面就如我们配置那样返回了:

上述大概给大家介绍了一下通过 nginx 部署的项目,404 时候的一个表现。虽然我上面皮了一下,给 nginx 的 404 错误配置了一个为 nb.html 的页面作为返回,但线上一般是不会这么玩的是吧?所以我们每次在浏览器接收到的 404.html 的返回结果,就一定是遇到 404资源不存在的问题吗?

如果你现在问我这个问题时,我会回答是,但不全是。为何这么说,我通过这个案例来说明。

前文我将 dist 包拉到 nginx 的 html 的目录时,大家可能也注意到了一个叫 50x.html 的文件是吧:

双击打开这个 html 时,他是长这样的:

这个页面大伙也有点熟悉对吧,如果我们 nginx 上接收到 50x 的报错,按照默认的配置来说便会返回这么一个页面。比如此时的我通过链接访问来模拟上游服务器返回 502 的错误码时前端接收到最终页面返回的情况。

接下来,如果我把这个 50x.html 的文件删了呢?大家可以猜想到结局吗?(这里我已经恢复了404改nb的配置)

没错,实时如你所见,页面显示 404 了。其实你看到这里,也许你会觉得这不是扯淡吗?无稽之谈。但我想说的是,这确实是我实战中遇到的问题。因为我们团队内部对 nginx 基础镜像自己又包了点东西,打了一个自己的 nginx 镜像。但这个镜像中,50x.html 的位置放错了,所以每次上游服务器返回任何 50x 报错的时候,最终到页面上的都是404。

这个配置错误,或者说 50x.html 的位置错误,一定程度上影响了一开始我们排查问题时的思考方向。一直怀疑资源问题的我,直到我查看 error.log 时发现其实对应的请求状态是超时,或者 502时,再联合查看 access.log 后,发现居然是 50x.html 的文件不存在...

所以,如果现在你问我前端接收到 404 返回时,是不是一定是资源不存在的问题时,我会回答:是,又不全是。可能因为 nginx 配置问题,导致全部上游的服务器报错问题都会是 404 的返回结果。

dns缓存

如果你曾遇到过某个项目每隔一段时间打开后出现 404,但是重启服务又解决了;如果你的项目也是部署在 aws,并且域名解析到其 ingress 层;那么恭喜你,也许你成功踩到了 nginx 的 dns 缓存的坑了。

nginx 默认行为在启动时会解析所有在配置文件中定义的域名。这些解析结果会被缓存,并在整个 Nginx 运行期间使用。基于这一点,如果某个服务的 ip 在 nginx 运行期间的发生了变动,那么就会出现 404 的问题。因为当 nginx 请求上游服务时,依然访问的旧的 ip 地址...

如上图所示,服务器1 的 ip 改变后,再通过该 nginx 向上游发送请求时,就不能正确返回了。此时我们能做的只有重启 nginx(重启时会重新基于配置做 dns 解析),让 nginx 正确的”找到“这个上游服务器。

这其实也是我在实战中真实遇到的场景,并且这问题不太好定位解决。其一就是它的偶发性,aws 的 ingress 的 ip 并不是每时每刻都在变,所以当你想准确定位这个问题时,你却发现这其实很难复现问题。当每次遇到问题,紧急重启完 nginx 问题后问题就解决了,但是当你去追溯问题时你又发现复现不出来。于是你只能找到你的上游服务的开发者一起探讨...

其实你说坑也确实坑,自己菜也确实菜,毕竟从未遇到过,所以一开始解决起来还是有点耗时和棘手。如果说对 nginx 有更深的认知,或者对云厂商了解过多,或许这样的问题排查就不会这么棘手了。所以还是要不断在实战中积累经验,每踩过的一个坑,都不会白踩。

简单总结,如果现在再遇到面试有人问我 404 的原因,或许我不仅仅能回到资源不存在、nginx 配置问题、还能再补充多一个这样的场景:nginx dns 的缓存问题,当遇到上游服务 ip 变化时,依然会有 404 的问题。

总结

其实,历经容器化部署后,更加体会到 nginx 这类 web服务器 对于前端发展的重要性。当我写这篇文章的时候,不断回想起之前面试各大厂时遇到的一些开放性问题,如果结合 nginx 的维度来进行回答,将会更全面。并且在这个时候,自己也意识到为什么当时面试官会问这样的问题。

本文我仅仅例举了两个实战中碰到的问题,发散开来其实可以对应到种种开放性面试题。并且我还没有对所有遇到的问题做一个总结回顾,所以我是有理由相信 nginx 的知识对于整个前端的从业生涯来说是比较重要的。它可以使我们从更加全面的视野来看待前端,看待问题。

如果当前团队中采用的容器化部署前端项目的方式,并且你有机会能接触到 nginx 的配置,那我觉得你一定要亲自上手玩玩。菜如笔者,不断玩不断学习,也算是在不断积累经验的过程也能有一点点收获,并且可以通过实战的经验反哺到一些开放性面试题中,我觉得对自己的综合实力来说是有提升的。所以你也一定可以有所收获~