概述
- 我们需要对 Nginx 要有体系化的一个认识
- 对 Nginx 自身来说,它是作为一个中间件的,只要是中间件,它必然会涉及到前端和后端
- 对于 Nginx 来说,它是需要协调整个前后端的一个组件
- 那对于中间件来,我们要理解整个外部系统前端和后端是如何进行交互的
- 我们一个用户请求过来以后,是如何被前端处理
- 到后端以后,如何被我们的应用程序处理
- 处理完之后再返回给我们用户的
- 还有就是 Nginx 的部署和调优
- 第一个层次是使用 Nginx 进行一个简单的配置去把它用起来
- 第二个层次是需要深度优化
- 掌握 Nginx 能够帮助我们去迅速进行故障定位
- 包括协调前后端开发
- 分析整个系统一个性能瓶颈
- 给开发提供一些支持,都是非常有必要的
Nginx 概念定义
- Nginx(engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务
- Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点开发的
- 第一个公开版本0.1.0发布于2004年10月4日, 其将源代码以类BSD许可证的形式发布
- 因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名
- 2011年6月1日,nginx1.0.4发布
- Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器
- 在BSD-like协议下发行, 其特点是占有内存少,并发能力强
- 事实上nginx的并发能力确实在同类型的网页服务器中表现较好
- 中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等
Nginx 历史背景
1 )Nginx 为何如此流行
- 在二十世纪初,互联网数据其实呈现一个快速增长,也就爆发式的增长
- 其实随着互联网数据的增长,体现在两个方面
- 第一个就是全球化,全球化之后,人与人之间,包括这个物联网,还有介入设备的数量的增多
- 导致我们现在互联网上的数据增长是特别快,就对硬件提出了更高的要求
- 在最早之前,我们的CPU设计是只有单核的,随着CPU的开始变成多核形式
- 第二个就是最早的一个应用程序服务器软件是Apache, 它处理请求比较低效
- 它是诞生在九十年代的一个产物来处理我们的web请求
- 对于 Apache 它天生设计就是设计在一个单核CPU之上的
- 它通常是用一个进程来处理一个请求
- 随着这种互联网数据的一个快速的增长,硬件设备也是有单CPU和多核的CPU
- 它一个进程只能处理一个请求,也就意味着如果我们想要处理更高的并发连接生产上
- 那我们就需要创建更多的一个进程,对于这样一个进程来说
- 比如说你现在有十万的并发链接,肯定是不能创建十万个进程来处理这样一个请求的
- 更重要的是一个进程处理多个请求就意味着一个进程在这个请求没有处理完之前
- 是不能够处理下一个请求的,比如说我们现在有多核CPU
- 继续用Apache来处理我们这个应用程序的时候
- 那也就意味着你的一个进程在同一时刻内只能处理一个请求
- 其实我们现在的CPU的设计不是用来处理单个应用程序的
- 而是服务于很多个应用程序的
- 那也就意味着我们的这应用程序之间需要不停的进行进程的一个切换。
- 进程切换就是有很大的一个这个开销
- 第一个就是全球化,全球化之后,人与人之间,包括这个物联网,还有介入设备的数量的增多
- 所以说,Nginx 之所以能够流行起来,就是因为诞生在这样两个背景之下
2 )Nginx 相较于 Apache 的优势
-
Apache
- 一个进程处理一个请求
- 阻塞式的
-
Nginx
- 一个进程处理多个请求
- 非阻塞式的
-
对于 Nginx 来说为什么能够适应高并发这样一种场景
-
因为是它诞生的比Apache要晚,所以它天生设计的时候,就是考虑到了高并发的这样一种场景
-
所以它其实是用一个进程来处理多个请求,并且它的事件处理模型是异步非阻塞的
-
也就是说,我一个进程在处理一个请求的时候,比如这个连接在某些条件不满足的时候
-
我可以先把这个连接记录一下状态,放到这里,先不处理
-
之后,下一个链接可以直接过来进行处理,等到我们前面这样一个连接,满足完之后
-
它会通过一系列机制来唤醒我们这个进程,对我们进行处理
-
当然在这个过程当中,也就意味着我们的编程复杂度是要提升的
-
因为一个进程处理一个请求,是阻塞式的,我不处理完,我就在等一直等
-
但是对我们的一个进程处理多个请求,并且是非阻塞式的时候
-
也就意味着当我们处理完一个进程,当一个连接请求过来以后,我们来处理
-
当不满足的时候,我们需要把现在这个进程的处理先停下来
-
但是停下来之后,我们要去增加很多的开销去记录我们这样一个进程的一个状态
-
比如说他现在运行到哪一步了,数据是运行到哪一块儿都要用很多后面的开销来记录下来
-
但是这个开销相较于我们这个阿帕奇这种处理请求来说,要进行很多的这个上下目切换。
-
并且天生的这种阻塞式的事件模型来说还是要强大很多
Nginx 的重要性
- 应用广泛,其实不论是大厂还是小厂,它几乎都是必用的一个web组件
- 只要说你的业务系统是使用外部来进行开发的
- 因此你想要把整个系统部署起来,几乎都可以使用 Nginx 来进行部署
- Nginx 是非常高性能的一个组件
- 前后端运维必知必会的这样一个基础技能之一
- 性能优化,进阶高级工程师
- 分析系统的性能瓶颈,并且对整个系统进行优化
Nginx 核心使用场景
1 )高性能的静态 web 服务器
2 )反向代理
![](https://i-blog.csdnimg.cn/direct/4cdd06fd2b3d47ecbb2ef811d54c01fb.png)
3 )API 服务
Http 请求全流程剖析
![](https://i-blog.csdnimg.cn/direct/66be7d7d97984814b6fa6ef988c4eb12.png)
- 当你在浏览器中输入一个网站的一个URL, 我们看下对应服务器发生了哪些事情
- 首先比如说你在一个客户端(电脑),输入一个网站的地址
- 回车之后,就是发送了一个HTTP的请求,可能你的前端面对的是一个Nginx
- 当然还有很多其他的这种web服务器, 这个WebServer自身来说, 只能处理静态资源
- 对于动态请求来说,它需要继续将这个请求交给我们后端的应用程序服务器
- 所以我们的应用程序服务器自身才是具备的处理动态资源的一个能力
- 当我们应用程序服务器接收到我们的Nginx转发给动态资源的一个解析请求之后
- 我们的应用程序服务器可能还要在自身进行一个业务逻辑的计算
- 比如说你现在后端的写的是一个订单系统,就需要去实时的计算
- 可能计算现在商品的一个数据下多少件,或者商品的一个价格是不是有变更等
- 总之来说,这样一些处理都是需要依赖于我们这个应用程序服务器
- 也就是我们很多开发人员所写的这个业务逻辑程序需要部署上去之后才能够生效
- 对于我们这个应用程序服务器自身来说,可能还需要跟后端数据库服务器(DB)来进行交互
- 我们的订单系统的数据肯定是要存在数据库服务器中
- 我们的应用程序服务器可能需要去数据库服务器再发起一个请求查询
- 服务器把结果返给他,他在一步一步的返回给我们的Nginx
- 我们的Nginx将静态资源和动态资源一起封装完之后再回给我们的客户端
- 其实这整个流程就一个粗略的Http请求的一个全流程的过程
- Nginx 自身只能处理静态资源,对于动态资源来说,它其实还是需要反向代理给我们后端的应用程序服务器
- 由我们的应用程序服务器处理完之后,将结果返回给我们的Nginx
- 由我们的Nginx将静态资源和返回的动态资源进行包装之后,返回给我们的客户端
- 由我们的客户端在浏览器中对结果进行一个渲染展示
Nginx 处理请求的过程
![](https://i-blog.csdnimg.cn/direct/268522532af3484fafdd57ff9a9042b7.png)
- 根据这一个图,这个红色的箭头标志代表客户端发过来的一个请求
- 通常也是一个Http请求,对Http请求来说,有三大块
- 第一个是一个静态资源服务,第二个是一个API服务,第三个是后端有一个反向代理
- 对于第一个静态资源服务器
- 我们都知道静态资源服务器, 是放在我们本地文件系统。
- 通常来说,我们现在一个网站中浏览的所谓的图片,CSS样式表,flash动画等等
- 各种这样一些静态资源都是放在我们本地文件系统
- 也就是在我们这个linux系统上,通常会有一个目录来放我们这样一些资源文件
- 对于反向代理
- 我们的动态资源,是通过反向代理来处理的
- 也就是 Nginx 通过反向代理能够代理给我们后端应用服务器
- 应用服务器,也就是我们的很多的开发所写的代码,就是要部署在这样一个服务器上
- 通常我们还要有一个数据库的服务器,我们的应用程序务器需要去和我们的数据服务器进行交互
- 对动态资源,请求到达我们的应用程序服务器以后
- 应用程序服务器就要进行一对对业务逻辑的计算之后,才能返回
- 到这以后,它的性能就会有一个急剧的下降
- 这里反向代理,还有两个能力
- 一个是缓存加速能力,一个是负载均衡能力
- 1 )所谓缓存加速能力
- 应用程序服务器从数据库服务器获取到的一个结果
- 有时候我们可以去先给它缓存起来
- 下一次请求再过来的时候,我就不需要再去应用程序服务器获取结果,再去数据库找结果
- 如果有缓存的话,结果直接就返回了,Nginx 自身也能够提供你这样的一些缓存服务
- 2 )所谓负载均衡
- 比如说,现在应用程序服务器不是一台,现在用户规模更大了
- 从五万的并发链接可能到了二十万了,一台应用服务器不够了
- 我可能需要过三台,需要通过负载均衡的形式
- 通过一定的算法去负载均均衡衡给我们负载均用到后端的三台应用程序服务器
- 所以在这儿,所以负载均衡是反向代理中的一个子功能
- 关于性能这方面
- 比如说静态资源服务器可能处理五万个请求
- 到应用程序服务器只能能理五千个甚至五百个都有可能
- 而数据库服务器性能就更低了,有可能只能在一个时刻只能处于一百个请求
- 所以说,为了缓解他们之间每个系统之间这种速度之间的差异,性能之间的差异
- 我们需要部署一个缓存服务器,这个需要根据你系统的一个规模来配置
- 所谓的缓存服务器是用来在性能不同的应务服务器之间均衡的一个组件(中间件)
- 关于API服务
- 其实在Nginx诞生初期的时候,这个API是没有的
- 随着 Nginx 不断的开发,有了 OpenResty 之后提供了一个能力
- OpenResty 其实就是一个 Nginx 和 Lua 脚本 的一个结合
- 集合了 大量 Lua 脚本 和 一些第三方的核心模块,共同形成了这样一个API服务
- 有API服务之后,用户请求可能到这儿以后,通过API服务可自己去开发出来一个高性能的web服务器
- 可以取代应用程序服务器
Nginx 优势
- 高并发,高性能
- 扩展性好,模块化设计,解耦
- 比如:阿里的 tengine,OpenResty
- 异步非阻塞的事件驱动模型
- 高可靠性
- BSD许可,允许二次开发
高并发利器 Nginx 相关体系结构
-
Nginx 简介
- 核心应用场景
- 流行高性能的原因
- 主要特点
- 安装部署第一个nginx
-
进程结构及热部署
- 进程结构
- 信号量管理机制
- 配置文件重载
- 热部署原理
- 热部署完整操作流程
- 模块化设计机制
- 编译安装的配置参数
- 配置文件结构
- 实战:基于多网卡的虚拟主机实现
- 实战:基于端口的虚拟主机实现
- 实战:基于域名的虚拟主机实现
-
HTTP核心指令用法
- 配置文件main段参数详解
- 配置文件events段参数详解
- server_name指令用法
- server_name指令用法优先级
- root和alias的区别
- location的基础用法
- location指令中匹配规则的优先级
- URL结尾有无反斜线的影响
- stub_status模块用法
-
HTTP核心模块用法
- 对connection做限制的limig_conn模块
- 对request处理速率做限制的limit_req模块
- 限制特定IP或网段访问的access模块
- 限制特定用户访问的auth_basic模块
- 基于HTTP响应状态码做权限控制的auth_request模块
- rewrite模块用法
- HTTP核心模块用法
- return和rewrite指令执行顺序
- autoindex模块用法
- TCP连接相关变量
- 发送HTTP请求变量
- 处理HTTP请求变量
- 响应请求变量
- 系统内置变量
-
生产场景之反向代理
- 反向代理基础原理
- 动静分离
- 使用Nginx作为反向代理时支持的协议
- upstream模块用法详解
- 配置一个可用的上游应用服务器
- 配置nginx反向代理实例
- proxy_pass指令用法常见误区
- 代理场景下Nginx接收用户请求包体的处理方式细节
- 代理场景下Nginx如何更改发往上游的用户请求细节
-
生产场景之负载均衡
- 负载均衡基础
- 配置实现Nginx对上游服务负载均衡
- 负载均衡算法-哈希算法
- 负载均衡算法-ip_hash算法
- 负载均衡算法-最少连接数算法
- 负载均衡场景下Nginx针对上游服务器返回异常时的容错机制-上
- 负载均衡场景下Nginx针对上游服务器返回异常时的容错机制-中
- 负载均衡场景下Nginx针对上游服务器返回异常时的容错机制-下
-
生产场景之缓存和HTTPS
- 缓存基础
- 缓存相关指令用法
- 缓存用法配置示例
- 配置Nginx不缓存上游服务特定内容
- 缓存失效降低上游压力机制一合并源请求
- 缓存失效降低上游压力机制一启用陈旧缓存
- 缓存清除策略
- https原理
- 对称加密和非对称加密
- 搭建私有CA
- 签署证书
- 配置Nginx支持https服务
-
架构基础
- 深入理解同步和异步、阻塞和非阻塞
- epoll的原理及优劣
- 何为连接池
- Nginx如何利用连接池处理网络请求
- 子进程协同的核心:共享内存
- slab内存管理器
- CPU亲缘性
- 实践中如何配置CPU亲缘性
- 内核的sendfile机制
-
性能优化
- 性能优化理论基础
- CPU多核心之间的负载均衡
- 网络层优化-TCP三次握手的参数优化
- 网络层优化-TCP连接建立的参数优化
- 网络层优化-TCP滑动窗口参数优化
- 网络层优化-TCP缓冲区参数优化
- 网络层优化-TCP慢启动及拥塞控制优化
- 网络层优化-减少关闭TCP连接的time_wait数量
- 优化磁盘IO
- 降低磁盘读写频率
知识点逐级递进
1 )体系,内容系统的完整性
- 基础配置、进程结构、热升级
- 核心模块用法
- 生产场景实践
- 性能优化
2 )代码实践出真知
- 基于理论的熟悉
- 到配置的示例
- 再到具体使用场景
3 )覆盖生产环境用法
- 动静分离
- 如何将前端和后端拆分
- 后端只用专注于这个业务逻辑,前端就用来处理这些静态资源
- 进一步提升系统并发能力
- Web Server
- 前端快速搭建开发测试环境, 将自己的代码部署到 Nginx
- 这里,Nginx 作为一个纯粹的一个静态的 web server 来处理的
- 反向代理
- 有动态的程序(java、python)的话,你肯定要涉及到反向代理的
- Https
- https 是比较基础的一个服务
- 现在的业务系统几乎都要开 https 服务全链路去加密
- 保障我们的业务安全
- 缓存服务器
- 因为 Nginx 是提供了强大的缓存能力的
- 很好的利用缓存,能够极大的去提升我们整个系统的吞吐量
- 现在的所有系统中,我们几乎都会用到缓存
- 如何对静态资态进行缓存
- 如何缓存上游应用服务的动态数据
- 来提升系统并发性能
- 七层负载均衡
- 如果单台服务器不够的话,就需要多台服务器
- 涉及如何对不同的服务器进行一个负载均衡
4 )深入技术本质
![](https://i-blog.csdnimg.cn/direct/aaf1eb21d56449b2b109111b5e4d0f29.png)
- 比如说我们现在整个服务器,那我们可以分为用户态和内核态
- 其实所谓用户态,大家可以理解为这样一些进程
- 比如说,80 是 nginx进程, 22 是 ssh 服务进程, 9092 是 kafka 的进程
- 可以理解在用户态中运行的都是这样一些具体的应用程序
- 所谓内核态
- 比如说,像TCP/IP协议上就是在运行的一个内核的
- 包括我们磁盘上的数据,我们想要获取磁盘上的数据
- 虽然说我们的应用程序是去获取磁盘上数据
- 但其实我们应用程序是不可以直接去内核中拿数据的
- 举个例子
- 比如说我们一个用户请求到达我们服务器之后,在服务器内部是如何处理这样一个请求的
- 比如说我们现在一个用户请求到达到我们的网卡
- 网卡会拆包拆到物理层的一个包,拿到一个具体的MAC地址
- 发现MAC地址是自己的话,就继续拆,继续拆完这个物理包之后
- 会拿到一个网络层的一个数据报文,网络层的报文之后,发现这个IP地址是自己的
- 我们都知道,在网络层,关注的是原IP地址和目的IP地址
- 它发现在自己的IP地址之后,会继续拆这个包
- 拆完这个包之后,发现到传输层的一个目的端口是80端口
- 至此,它会找到应用程序用户态中的某一个应用程序进程
- 比如说,它发现目的端口是80端口,这个数据报文是要继续交给我们用户态空间的某一个应用程序进程
- 也就是我们的 nginx 进程, 这个时候它会把这个报文发到 Nginx 上
- 也就是说,在传输层网络层和我们的数据链路层TCP/IP协议站的七层协议中的除了应用层之外的三层协议都是由我们的内核来进行处理的
- 只有到应用层的时候才交给我们用户态的应用程序来进行处理
- 用户态的 Nginx 拿到之后,比如说想要获取某一个图片的时候
- 这个时候它是不能够直接去跟磁盘打交道的,只有我们的内核可以和磁盘打交道
- 所以,他需要发起一个请求,就获取某一个图片数据缓存,肯定是放在我们磁盘文件系统中的某一个图片
- 那这个时候,其实整个获取的过程是由我们的内核在获取的
- 我们内核获取完之后,会把这个图片放到我们内核中的一个缓冲buffer中
- 放到缓冲buffer之后,我们内核会继续把它放到我们的一个用户空间的一个buffer中
- 至此, 我们的 Nginx 进程才可以去用户态的缓冲区中,拿这个图片进行一个处理
- 基于这个例子,我们整个系统想要进行优化的时候,是需要了解整个过程的
- 比如说我们是不是可以优化中间的某一个过程
5 )总之
- Nginx 在整个业务系统中是非常重要的一个外部组件
- 了解 Nginx 能够帮助我们在具体的工作中不断的突破成长瓶颈
- 尤其是我们作为一个外部开发,不管你是前端还是后端,想要在你的职位中有深入的提升
- 肯定不能专注于自己的那一块,你一定要去关注你整个项目的一个全流程
- 也就是你要关注整个系统的一个性能瓶颈
- Nginx 作为中间件,它需要协调整个前后端