来源丨经授权转自 小白debug(ID:xiaobaidebug) 作者丨靓仔白
你是一个程序员,你在电脑上编辑了一段文本,将它保存为 txt 文件 。将它拖到浏览器打开,就能看到文件里的内容。
但这看起来太过单调,为了让画面更丰富,我们定个规则,在文本边上加个两个h1符号,文本就以 标题 形式展示。
加入ul和li就能变成 列表 ,加入img还能让 url文本 直接变成对应的 图片 。
这些带尖括号的特殊符号,我们叫它 标签 。只要浏览器识别到这些标签,就展示对应的样式。
为了将这个自带标签的文本跟 txt 纯文本区分开来,我们给了它新的后缀名, html 。
浏览器只要识别到文件是 html,就会解析里面的标签,这样我们就有了标题、输入框等各种丰富的内容了。这其实就是我们平时在浏览器中看到的 网页 。
但不同的是,这个 html 文件是浏览器从我们 本地 电脑文件中打开的。
本地浏览器打开html 而我们平时访问的网页,则是从 某台远端服务器 ,将文件传到我们电脑的浏览器后打开的。
从远端服务器得到html打开 那么问题就来了,我们是 怎么获得这个远端服务器上的 html 文件的 ?
没有什么是加一层中间层不能解决的,如果有,那就再加一层 ,这次我们要加的中间层是 Nginx .
nginx中间层 假设我们完全不了解 nginx,来看下它是怎么设计出来的。
HTTP 服务器是什么? 想要让本地的浏览器,获取到放在远端服务器上的 html 文件。
那很简单,我们可以在远端服务器启动一个 进程 ,这个进程对外提供 http 服务 ,说白了就是提供了个 url 。
用户在浏览器中输入这个 url, 回车,浏览器就会向 这个进程发起 http 请求 ,
进程收到浏览器的请求后,就将 html 文件发给浏览器,浏览器完成解析和展示,完美。
而像这种根据浏览器请求,返回 html 文件的服务进程,其实就叫 http 服务器 。
有了它,前端开发老哥写的各种 html 文件就能部署到远端服务器上, 对外提供网页服务 了。
http服务器是什么 反向代理是什么? 但一个完整产品往往不止有 前端页面 ,还有 后端服务 ,比如某宝,前端商城页面需要从后端服务那获取最新的商品数据。
前后端分离 假设现在前端页面已经被加载到浏览器中,浏览器会按页面里写好的代码逻辑,向后端商品服务发起请求,获取数据,流量小的时候没什么问题,流量变大后,后端服务器扛不住的话,就需要增加商品服务的个数,服务变多后,每个都有对应的 ip 和端口,浏览器就不知道该访问哪个服务了。
所以我们还需要 在这几个后端服务前面加一个进程 ,对外提供一个 url 域名,请求来了,由这个进程均匀转发给背后的几个服务,让每个服务都能处理上请求,也就实现了所谓的 负载均衡 。
像这种, 屏蔽掉背后具体有哪些服务器的代理方式,就是我们常说的反向代理 。
反向代理 有了反向代理,我们对外就可以只提供一个url域名,背后根据需要, 随时 扩缩容服务 。
这个反向代理的功能,正好可以加到前面放 html 文件的进程上。
那现在这个进程就很灵性了,既可以为 前端 html 文件提供 http 服务器的功能,当 html 文件被加载到浏览器,并向后端发起请求的时候,这个进程还能为后端服务器提供反向代理的功能。
http服务器+反向代理 模块化网关能力 既然是中间层,所有网络流量都要经过进程,那它高低也算个 网关 了。
网关 于是我们就可以顺理成章的在它上面加入一些 通用网关能力 ,比如加个 日志 ,记录每次调用的结果,方便后续排查问题,又比如加个对输入输出的内容进行 压缩 的功能,减小网络带宽消耗,又或者是对某个 IP 进行 限流或封禁 ,甚至还可以 修改输入输出 的内容。能实现的功能实在太多,想象空间很大,于是将这部分功能设计为 开放接口 ,让用户通过 自定义模块 来实现特定功能。
这还不够,现在这个网关只支持http,我们其实还能扩展下,让它支持tcp,udp,http2和websocket,你能想到的我都要支持,我本来不支持的,自会有人通过自定义模块帮我支持。
支持多种通用能力和协议 配置能力 前面提到那么多种能力,用户肯定不会全用上,所以需要有个地方让人选择用哪些能力,于是我们可以加个配置文件,也就是 nginx.conf ,用户想用什么能力,就在配置文件上声明清楚就行,非常方便。
nginx.conf配置
单线程 现在这个网关进程的主要任务就是跟上下游建立网络连接,顺便内部做下处理。多个客户端请求通过网络进入到一个进程,如果用多线程并发处理,那就需要考虑并发问题,同时影响性能。怎么办呢?
多线程 很简单!外部不管有多少有个网络连接,网关进程收到客户端请求后,都统一塞到 一个线程 上,在一个线程上处理客户端请求,什么 并发问题 和 线程切换开销 ,完全不存在!
单线程 多 worker 进程 但单个进程要单线程处理那么多流量,哪怕再快,压力也不小,万一这里面有美羊羊发的流量,你觊觎那么久?怎么忍心让她久等?沸羊羊你说话!
怎么办呢?既然多线程不行,那我们就上 多进程 。
于是可以将单个进程改成多个进程,我们管它们叫 worker 进程 。进程之间互相独立,一个 worker 跪了不影响另外一个 worker 进程。
多worker进程 让多个 worker 进程同时监听一个 ip 地址+端口。只要一有流量进来,操作系统就会随机给到其中一个进程处理。将 进程数量 设置为跟操作系统 cpu核数 一致,那每个进程都能得到一个核,开足马力猛猛干。
worker数与核数一致 看到这里,问题就来了,为什么多个进程同时监听一个端口不会出现 端口冲突 (port is already in use),评论区告诉我答案。
共享内存 但多 worker 进程的情况下,同一个客户端的多个请求会随机打到某个 worker ,对于限流这种需要计数的场景,就会被分散到多个 worker 上单独计数,那还怎么限流,所以还需要给这些 worker 进程 分配一个 共享内存 区域,方便多个进程之间共用同一份数据做逻辑,确保系统数据一致性。
共享内存 proxy cache 作为网关,它在收到前端网页请求后,会转发给后端,并将后端处理结果中转给前端。如果它能 将响应结果缓存 起来,这样下次收到 同样的请求 ,直接将缓存里的数据返回给前端,从而 减少响应时间和网络负载 。
那这个数据是放在共享内存里吗?内存贵,不合适,我们可以维护些磁盘文件,用于在前端请求后端的过程中,暂存后端响应的结果,后面再有相同请求,就可以将磁盘里的数据返回。
这又是经典的 空间换时间 ,用廉价的磁盘空间换取网络传输和cpu计算耗时。对于后端响应较慢或重复请求较多的场景,读写磁盘总归比直接将请求打到后端来得快。这些 用于缓存响应数据的磁盘文件 ,就是 所谓的 proxy cache 。
proxy cache 加入 master 进程 但这还不够,现在每个 worker 都会分走一部分流量,如果功能更新,所有 worker 同时一起重启,上面的网络连接就会全部断掉。更好的方式是创建 worker 和关闭 worker 挨个陆续执行,这样前端网页连接断开后还能去连另外一个worker,保证任意时间一直有worker在工作。也就是所谓的 滚动升级 。因此还需要一个新的进程协调各个 worker 谁先谁后,这个
协调进程 ,就是所谓的 master 进程 。让master读取前面提到的 nginx.conf 配置,统一管理多个worker。
master进程 nginx 是什么 好啦,到这里,当初那个简陋的单进程网关服务,就变成了一个支持动态配置多种通用网关能力和多种网络协议,单 master 多 worker 架构、多个worker进程之间共享内存和proxy cache,对外提供一个IP+端口,支持 http 服务器和反向代理的高性能网关服务。
它就是所谓的 nginx 。
nginx是什么 它不仅支持日志、限流等各种通用能力、还支持自定义网关能力,只要你写好配置,就能让它给你当牛做马。性能上 5w qps 非常轻松,应付你那只有几十 qps 的服务更是绰绰有余了。
现在大家通了吗?好啦,如果你觉得这个视频对你有帮助,记得点赞并转发给你那不成器的兄弟,文字版的笔记见评论区。
最后遗留一个问题,想必大家也发现了,聊到现在它其实也只是某台服务器上的多个进程,一旦服务器跪了,nginx 也就跪了,存在 单点问题 。
nginx单点问题 那 怎么解决 nginx 的单点问题 呢? nginx 有集群模式 吗?评论区告诉我答案。
最后的最后再遗留一个问题,你听说过大数据吗?你知道大家是怎么解决大数据问题的吗?下期聊聊这个话题,如果你感兴趣,记得关注。我们下期见!