nginx(自写)
浏览器只要识别到文件是HTML,就会解析里面的标签,这样就有了标题,输入框等各种丰富的内容了,这其实就是我们在浏览器中看到的网页,但不同的是,这个HTML文件是浏览器从我们本地电脑文件中打开的,而我们平时访问的网页,则是从某台远端服务器,将文件传到我们电脑的浏览器后打开的。那么我们是怎么获得这个远端服务器上的HTML文件的,没有什么是不能加一层中间层解决的,如果有就是再加一层,这次加的是nginx
HTTP服务器是什么:
想要让本地浏览器获取到放在远端服务器上的HTML文件,那可以在远端服务器上启动一个进程(这个进程其实就是HTTP服务器),这个进程对外提供HTTP服务,就是提供了一个URL,用户在浏览器中输入这个URL,浏览器就会向这个进程发起HTTP请求,进程收到浏览器请求后,就将HTML文件发给浏览器,浏览器完成解析和展示。有了它(HTTP服务器),前端写的各种HTML文件就能部署到远端服务器上,对外提供网页服务
反向代理:
一个完整产品不仅有前端网页还有后端服务。比如某宝,前端商城页面需要从后端服务那儿获取最新的商品数据,假设前端页面已经被加载到浏览器中,浏览器会按页面里写好的代码逻辑向后端商品服务发起请求获取数据,流量小的时候没什么问题,流量过大后端服务器扛不住的话,就需要增加商品服务的个数,服务端变多后,每个都有对应的ip和端口,浏览器就不知道该访问哪个服务了,所以还需要在这几个后端服务前面加一个进程,这个进程对外提供一个URL域名,请求来了后,由这个进程均匀转发给背后的几个服务,让每个服务都能处理上请求,也就是实现了负载均衡,向这种屏蔽掉背后具体有哪些服务器的代理方式,就是我们常说的反向代理。有了反向代理,我们就可以对外只提供一个URL域名,背后根据需要随时扩缩容服务
反向代理的功能,正好可以加到前面放HTML文件的进程上,那现在这个进程既可以为前端HTML文件提供HTTP服务器的功能,当HTML文件被加载到浏览器并向后端发起请求的时候,这个进程还能为后端服务器提供反向代理的功能
模块化网关能力
既然是中间层,所有网络流量都要经过进程,那就算个网关,于是就可以在它上面加一些通用网关的能力,比如加个日志记录每次调用的结果,方便后续排查问题,又比如加个对输入输出的内容进行压缩的功能,减小网络带来的带宽消耗又或者是对某个Ip进行限流或封禁,甚至话可以修改输入输出的内容。这里能实现的功能太多了,于是可以将这部分功能设计为开放接口,让用户通过自定义模块来实现特定功能。这还不够,现在这个网关只能支持HTTP,我们其实还能扩展下让他支持tcp,udp,http2和websocket.可以通过自定义模块,支持这些。nginx 可以再第四层也可以在第七层。一个软件(如Nginx)可以同时在多个网络层上工作,而并非被限制在某一层。
物理层 & 数据链路层:这就像是城市的公路系统和卡车。它负责把数据包(信件)从一地 physically 运到另一地。Nginx不直接处理这一层,它依赖于操作系统(比如Linux)的网卡驱动来处理。
网络层(IP):这就像是邮政编码系统。每个IP地址就像一个邮政编码,它决定了数据包的最终目的地是哪个城市、哪个区域。Nginx肯定需要理解IP,因为它要知道一个请求是发给谁的。
传输层(TCP/UDP):这就像是邮局的标准化信封和运输流程。
TCP 就像挂号信或快递。它要求签收确认(ACK),保证信件不丢失、不损坏、按顺序送达。建立连接就像快递员上门取件和最终签收的完整流程。
UDP 就像投递普通明信片。你扔进邮筒,希望它能到,但邮局不保证它一定到,也不保证按你投递的顺序到达。
Nginx作为一个强大的“分拣中心”,最基本的功能就是处理这些“TCP信封”。它监听服务器的某个“信箱”(端口,比如80、443),接收所有寄到这个信箱的TCP挂号信。
应用层(HTTP, HTTP/2, WebSocket, gRPC...):这就像是信封里的具体内容和处理这些内容的规则。
HTTP 信里装的是用特定格式(HTTP语法)写成的请求,比如“请给我
/index.html
这个文件”。WebSocket 信里一开始也是HTTP格式(握手信),但握手成功后,双方就约定好以后可以用任何自定义格式写信了,适合频繁通信(如聊天室)。
gRPC 信里装的是用Protocol Buffers格式序列化的二进制数据,效率极高。
现在,我们来解答你的疑问:“为什么Nginx这个网关可以支持所有这些?”
Nginx的工作模式是分层处理:
第一步:先处理“传输层”(接管TCP连接)
当Nginx启动时,它会告诉操作系统:“请把所有发送到80端口(HTTP) 和443端口(HTTPS) 的TCP连接(挂号信)都交给我来处理”。
此时,Nginx还不知道也不关心这个TCP连接里具体装的是什么应用层协议。它只是建立了这个可靠的传输通道。
第二步:再解析“应用层”(拆开信封看内容)
TCP连接建立后,Nginx开始读取这个连接里发送过来的原始数据。这时,它才会尝试解析这些数据的内容,来判断这到底是一个什么样的请求:
模块化设计的关键就在这里!
Nginx有一个
ngx_http_module
模块。这个模块会检查数据的前几个字节。如果发现数据是GET / HTTP/1.1
这样的字符串,它立刻就知道:“哦,这是一个HTTP请求!” 然后就会调用HTTP处理模块来接管后续所有工作(解析Header、找对应的HTML文件、负载均衡等)。同样,Nginx有一个
ngx_stream_core_module
模块。如果你配置它去监听9000端口,并告诉它这个端口用来做数据库负载均衡,那么所有发送到9000端口的TCP连接,都会被这个模块接管。它根本不会去解析数据内容是不是HTTP,而是直接根据你的配置,将这个原始的TCP连接转发给后端的某个MySQL服务器。这就是四层负载均衡(L4 Load Balancing),工作在传输层。对于WebSocket,Nginx的HTTP模块在解析初始数据时,会发现这是一个带有
Upgrade: websocket
头的HTTP请求。它会识别出这是一个升级连接协议请求,然后允许这个TCP连接在完成HTTP握手后,持续存在并被用于WebSocket通信。Nginx此时就变成了一个WebSocket代理,但它底层操作的依然是那个最初的TCP连接。
总结一下:
功能 | 主要工作的层 | Nginx如何实现 |
---|---|---|
HTTP服务器/反向代理 | 应用层 (L7) | ngx_http_module 解析HTTP协议内容 |
TCP/UDP负载均衡 | 传输层 (L4) | ngx_stream_core_module 不解析内容,直接转发TCP/UDP流 |
SSL/TLS终止 | 会话层 (L5/L6) | ngx_http_ssl_module 或 ngx_stream_ssl_module 在建立TCP连接后,进行加密解密 |
所以,你的疑问非常到位。Nginx的强大之处就在于它的模块化架构和分层处理逻辑:
它首先作为一个TCP/UDP连接管理器(传输层代理)工作。
然后,通过加载不同的模块,它能够窥视这些连接中的应用层数据,并根据其内容决定如何路由、处理、修改它们,从而变身为一个应用层网关。
正是这种设计,使得“加一层Nginx”能够解决如此多不同层面的问题,从静态文件服务(HTTP)、到API路由(HTTP)、再到数据库负载均衡(TCP),无一不可。
“可以通过自定义模块,支持这些”这句话的含义就是:你可以为Nginx编写一个模块,告诉它当收到某个端口的TCP连接时,如何解析里面的自定义协议数据,从而实现你自己的网关逻辑。
配置能力
前面提到那么多种能力,用户肯定不会全用上,所以需要有个地方让人选择用哪些能力,于是我们可以加个配置文件,也就是nginx.conf,用户想用什么能力就在配置文件上说明清除就行
单线程
现在这个网关进程的主要任务就是跟上下游建立网络连接,顺便内部做下处理,多个客户端请求通过网路进入到一个进程,如果用多线程并发处理,那就需要考虑并发问题,影响性能。这样,不管外部有多少个网路连接,网关进程接收到客户端请求后,都统一塞到一个线程上,在一个线程上处理客户端请求,这样并发问题和线程切换开销完全不存在。
多worker进程
但单个进程要单线程处理那么多流量。既然多线程不行(并发问题和线程切换开销),那就改为多个进程,多worker进程,进程之间互相独立。一个worker进程跪了,不影响另外一个worker进程。让多个worker进程同时监听一个IP地址加端口。一有流量进来,操作系统就会随机给到其中一个进程处理。将进程数量设置为跟操作系统cpu核数一致,那每个进程都能得到一个核。
为什么多个进程同时监听一个端口,不会出现端口冲突?
端口由Master首先抢占,再由内核优化分配给Worker进程,避免了端口冲突,实现了多进程高效处理单端口的连接
内存共享
但多worker进程的情况下,同一个客户端的多个请求会随机打到某个worker。对于限流这种需要计数的场景,就会被分散到多个worker上,单独计数,那还怎么限流。所以还需要给这些worker进程分配一个共享内存区域,方便多个进程之间公用同一份数据做逻辑,确保系统数据一致性
Proxy Cache
作为网关,它在收到前端网页请求后会转发给后端,并将后端处理结果中转给前端。如果它能将响应结果缓存起来,这样下次收到同样的请求,直接将缓存里的数据返回给前端,从而可以减少响应时间和网络负载。这个数据不能放在共享内存里,内存贵不合适,我们可以维护一些磁盘文件用于在前端请求后端的过程中,暂存后端响应的结果。这就是经典的空间换时间,用廉价的磁盘空间换取网络传输和cpu计算耗时,对于后端响应较慢,或重复请求较多的场景,
Master进程
现在每个worker进程会分走一部分流量,如果功能更新,所有worker同时一起重启,上面的网络连接就会全部断掉。更好的是,创建worker和关闭worker挨个陆续执行,这样前端网络连接断开后还能去连另外一个worker,保证任意时间一直又worker工作,也就是所谓的滚动升级,因此还需要一个新的进程协调各个worker谁先谁后,这个新进程就是一个master进程,让master进程读取前面提到的nginx.conf配置,统一管理多个worker
Nginx是什么
到这里,当初那个简陋的单进程网关服务就变成了一个支持动态配置,多种通用网关能力和多种网络协议,单master多worker架构,多个worker进程之间共享内存和Proxy Cache,对外提供一个ip加端口,支持http服务器和反向代理的高性能网关服务,它就是nginx,不仅支持日志,限流等通用能力,还支持自定义网关能力,只要写好配置即可,性能上5万QPS非常轻松