csdn的图片显示有问题,为了更好的阅读体验,请移步nginx-blog
一、初始Nginx
1.1 nginx应用场景
![image.png](https://img-blog.csdnimg.cn/img_convert/5a930a077c0e7f4a8d17fe69e4350e9c.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=586&id=LL1zE&margin=[object Object]&name=image.png&originHeight=1172&originWidth=2112&originalType=binary&ratio=1&rotation=0&showTitle=false&size=928999&status=done&style=none&taskId=uddb9f030-3d60-4932-8c4c-cced0a440f1&title=&width=1056)
1.2 Nginx优点
![A82E994B-DDE1-4C48-A3B1-4CC72150E101.png](https://img-blog.csdnimg.cn/img_convert/8fcb61262648556243573ef2cebdcfcc.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=511&id=u59c349bd&margin=[object Object]&name=A82E994B-DDE1-4C48-A3B1-4CC72150E101.png&originHeight=1022&originWidth=1938&originalType=binary&ratio=1&rotation=0&showTitle=false&size=973178&status=done&style=none&taskId=udfc4d868-cc8d-4074-9e67-588b458f05f&title=&width=969)
1.3 Nginx组成
![image.png](https://img-blog.csdnimg.cn/img_convert/988d8f4f89b21b29a6f917d1ec6385b2.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=413&id=u4baf27df&margin=[object Object]&name=image.png&originHeight=826&originWidth=1516&originalType=binary&ratio=1&rotation=0&showTitle=false&size=690551&status=done&style=none&taskId=u093b0ccd-1ef0-476e-8b8f-a703c56616d&title=&width=758)
1.4 Nginx配置语法
![image.png](https://img-blog.csdnimg.cn/img_convert/9ecab8688370426e90478d09afdef67e.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=415&id=u546695db&margin=[object Object]&name=image.png&originHeight=830&originWidth=1480&originalType=binary&ratio=1&rotation=0&showTitle=false&size=815741&status=done&style=none&taskId=u2c247769-0483-4af3-a4c5-377f3d3f97e&title=&width=740)
![image.png](https://img-blog.csdnimg.cn/img_convert/2d721ec44ea250782afc3890899d74a8.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=438&id=ueaec73dd&margin=[object Object]&name=image.png&originHeight=876&originWidth=1380&originalType=binary&ratio=1&rotation=0&showTitle=false&size=328505&status=done&style=none&taskId=u790be97a-4b0b-4db9-a113-d50a1b57b6c&title=&width=690)![image.png](https://img-blog.csdnimg.cn/img_convert/ba61ca6096aa5c88c817e024645b4705.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=434&id=uc3b7e24a&margin=[object Object]&name=image.png&originHeight=868&originWidth=1548&originalType=binary&ratio=1&rotation=0&showTitle=false&size=473105&status=done&style=none&taskId=u7ab99a62-6fc6-40aa-bcf5-ac954f839af&title=&width=774)
![image.png](https://img-blog.csdnimg.cn/img_convert/09875d39f3d851c309b29b91eb6b05c3.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=426&id=ufad33ad3&margin=[object Object]&name=image.png&originHeight=852&originWidth=1460&originalType=binary&ratio=1&rotation=0&showTitle=false&size=423949&status=done&style=none&taskId=ue31bfd4f-7edb-48e8-bee3-31d0eadd078&title=&width=730)
1.5 Nginx 命令行
![image.png](https://img-blog.csdnimg.cn/img_convert/09e3250a79debfec4cd89096ae285712.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=432&id=ufa43ac01&margin=[object Object]&name=image.png&originHeight=864&originWidth=1456&originalType=binary&ratio=1&rotation=0&showTitle=false&size=646525&status=done&style=none&taskId=ua093ced4-fbab-4ecb-b65f-e2afac5a6ab&title=&width=728)
1.6 日志
web日志分析工具:goaccess
1.7 SSl安全协议
![image.png](https://img-blog.csdnimg.cn/img_convert/765e71d44e2b05963f0da598491c8bec.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=433&id=u167fc52b&margin=[object Object]&name=image.png&originHeight=866&originWidth=1512&originalType=binary&ratio=1&rotation=0&showTitle=false&size=771135&status=done&style=none&taskId=ueb77d876-4757-4a44-929f-609bbe79b3b&title=&width=756)
![image.png](https://img-blog.csdnimg.cn/img_convert/9194fd240c0abcc046ff29bc20fba349.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=428&id=uc42cac93&margin=[object Object]&name=image.png&originHeight=856&originWidth=1486&originalType=binary&ratio=1&rotation=0&showTitle=false&size=651764&status=done&style=none&taskId=u66318642-6518-4f37-881c-daa3d2ee47b&title=&width=743)
1.7.1 对称加密
![image.png](https://img-blog.csdnimg.cn/img_convert/7c1b704a6ed8c85e613c15786dacc07a.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=418&id=u555e3556&margin=[object Object]&name=image.png&originHeight=836&originWidth=1326&originalType=binary&ratio=1&rotation=0&showTitle=false&size=194153&status=done&style=none&taskId=u171d1452-7cc7-4148-819a-3072fd184a3&title=&width=663)
基于异或算法,明文可以转为密文,密文也可以转为明文,而且性能好,只需要一次便历过程。
1.7.2 非对称加密
![image.png](https://img-blog.csdnimg.cn/img_convert/fd3fbb5fbb960452435c45b3cc29224d.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=355&id=u8daa4a38&margin=[object Object]&name=image.png&originHeight=710&originWidth=1590&originalType=binary&ratio=1&rotation=0&showTitle=false&size=341051&status=done&style=none&taskId=u9747b1f9-8adb-4fff-904d-31432c7860a&title=&width=795)
自己发出去的文本用私钥加密,接收方使用公钥加密;反之,接收方回复消息使用公钥加密,自己使用私钥解密。
![image.png](https://img-blog.csdnimg.cn/img_convert/39d03fffcf3a084561b1c6156a12bcb1.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=450&id=u88a77c62&margin=[object Object]&name=image.png&originHeight=900&originWidth=1534&originalType=binary&ratio=1&rotation=0&showTitle=false&size=529082&status=done&style=none&taskId=ue0ecf4c4-4058-4ead-8437-50c04108c19&title=&width=767)
在Nginx上可以设置为:
ssl_verify_client on;#以便 OCSP 验证工作
ssl_ocsp on;#启用客户端证书链的 OCSP 验证
resolver 192.0.2.1;#解析器应指定为解析 OCSP 响应器主机名
![image.png](https://img-blog.csdnimg.cn/img_convert/69e078c9d8fdb1af94f76bdd54a26491.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=424&id=u08d2c9e7&margin=[object Object]&name=image.png&originHeight=848&originWidth=1510&originalType=binary&ratio=1&rotation=0&showTitle=false&size=644753&status=done&style=none&taskId=u3637df4c-3da2-4c23-8cc1-93edeb8830b&title=&width=755)
浏览器获取到证书后如何生效,需要验证证书链:
![image.png](https://img-blog.csdnimg.cn/img_convert/2c002bc589a99d0a9f8e65007aa060de.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=403&id=u7bd89cd0&margin=[object Object]&name=image.png&originHeight=806&originWidth=1596&originalType=binary&ratio=1&rotation=0&showTitle=false&size=706121&status=done&style=none&taskId=u25b277c3-4a01-4f3a-aa71-ab14d0bed78&title=&width=798)
说明:
站点证书由三部分构成(根证书、二级证书、主证书),操作系统的根证书很难修改,大部分浏览器(除firebox)使用的是操作系统的证书库。所以浏览器在验证证书是否有效时,除了验证nginx发过来的两个证书(二级证书和主站点证书)是否过期外,还要要在根证书是否有效且被认证。
1.7.3 TLS的通信过程
![image.png](https://img-blog.csdnimg.cn/img_convert/f339ad9338f8dc16768cc927f20e99cd.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=445&id=ufb99ad14&margin=[object Object]&name=image.png&originHeight=890&originWidth=1614&originalType=binary&ratio=1&rotation=0&showTitle=false&size=717638&status=done&style=none&taskId=u72971e82-41b9-420d-b364-d9cf4dc558a&title=&width=807)
第一步:浏览器向服务器发送clinet hello消息,告诉服务器我支持哪些加密算法;
第二步:服务器把最偏向的加密算法发送给客户端,发送server hello消息,告诉服务器最终选择哪个安全套件;
第三步:服务器向客户端发送证书链;
第四步:客户端验证服务器相关证书;
第五步:服务器发送server hello done,且在第五步前向客户的发送加密算法的公共参数;
第六步:浏览器根据公共参数生成自己的私钥,再把公钥发送给服务器;
第七步:服务器生成自己的一对公钥和私钥,用自己的私钥和客户端发来的私钥,生成双方加密的密钥。
第八步:浏览器根据服务器发来的公钥和自己生成的私钥也会生成双方加密的密钥,通过非对称加密,二者生成的密钥是相同的。
第九步:服务器使用生成的密钥加密发送的消息,传给浏览器。
Nginx对加密算法的优化:
对于小文件,Nginx需要优化非对称加密算法,适当弱化密码强度;
对于大文件,需要考虑优化对称加密算法(AES)。
1.7.4 使用免费SSL证书把Http网站改造为Https网站
[root]:yum install python2-certbot-nginx
[root]:certbot --nginx --nginx-server-root=conf目录 -d 需要安装证书的server name
1.8 基于openResty使用lua实现简单服务
1.8.1 下载
openresty.org–>下载–>源码发布–下载–>解压
1.8.2 分析目录结构
![image.png](https://img-blog.csdnimg.cn/img_convert/8e64c04ec1fa6875e0165ab5bc5a2f3d.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=216&id=u1ff86b65&margin=[object Object]&name=image.png&originHeight=432&originWidth=1130&originalType=binary&ratio=1&rotation=0&showTitle=false&size=497671&status=done&style=none&taskId=u7369271d-1f04-43a0-9dcf-43f18a49c33&title=&width=565)
1.8.3 编译
编译一个基本的openresty .configure
make&make install
1.8.4 添加lua代码
![image.png](https://img-blog.csdnimg.cn/img_convert/8ff7fd74b248eb71038c46e939aab14f.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=295&id=u2936c073&margin=[object Object]&name=image.png&originHeight=590&originWidth=1524&originalType=binary&ratio=1&rotation=0&showTitle=false&size=230878&status=done&style=none&taskId=uba1fba36-a7c0-4dda-9ee6-9a7020d9543&title=&width=762)
1.8.5 运行
二、Nginx架构基础
![image.png](https://img-blog.csdnimg.cn/img_convert/92991b0bb602363177d3d0211bc1591f.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=438&id=ud746df97&margin=[object Object]&name=image.png&originHeight=876&originWidth=1600&originalType=binary&ratio=1&rotation=0&showTitle=false&size=816530&status=done&style=none&taskId=ua40d20a8-ec6a-4ac8-b50b-dac5dec87ec&title=&width=800)
2.1 Nginx的进程结构
![image.png](https://img-blog.csdnimg.cn/img_convert/132a2632c10ec0bc69bcf6e24f431b67.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=427&id=u487ab640&margin=[object Object]&name=image.png&originHeight=854&originWidth=1412&originalType=binary&ratio=1&rotation=0&showTitle=false&size=560212&status=done&style=none&taskId=u3bf290f2-ccfd-458a-94da-318e85a25e4&title=&width=706)
Nginx包含master进程和子进程,子进程又分为两大类,Cache相关进程和worker进程,子进程间的通信是通过共享内存来解决的。
master进程:用来管理worker进程,负责监控worker进程正常工作,是否需要重新加载配置文件等。
缓存:是多个worker进程共享的,同时也会被cache manager(缓存的关联)和cache loader(缓存的载入)进程使用。cache manager和cache loader进程使用是用于后端发来的动态请求做缓存的来使用的。
为了保证Nginx的高可用性,nginx被设计为多进程的模式,因为多线程不同线程会共享一块内存空间,线程间相互影响,如果一个第三方模块导致地址越界等问题会使得整个Nginx全部挂掉。
2.2 使用信号管理Nginx父子进程
![image.png](https://img-blog.csdnimg.cn/img_convert/76a9a9f2f5fadfbd23b014247897b9cf.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=420&id=u1019b4de&margin=[object Object]&name=image.png&originHeight=840&originWidth=1520&originalType=binary&ratio=1&rotation=0&showTitle=false&size=556853&status=done&style=none&taskId=u1a9a6968-3180-4cb3-9b6f-c758e22b7be&title=&width=760)
2.3 reload流程
![image.png](https://img-blog.csdnimg.cn/img_convert/13350fb3a5e7a3daa389954c93f8d6d7.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=480&id=ua44596f6&margin=[object Object]&name=image.png&originHeight=960&originWidth=1794&originalType=binary&ratio=1&rotation=0&showTitle=false&size=965935&status=done&style=none&taskId=u8e0ac1bc-328a-46e5-b34d-a6332fcd8a0&title=&width=897)
![image.png](https://img-blog.csdnimg.cn/img_convert/47fc12acc8a8782671c24e68ac3d7614.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=494&id=ue50573a9&margin=[object Object]&name=image.png&originHeight=988&originWidth=1800&originalType=binary&ratio=1&rotation=0&showTitle=false&size=579088&status=done&style=none&taskId=ua1f257c9-6f0c-4c10-886d-298b0cd6a84&title=&width=900)
2.4 热升级流程
![image.png](https://img-blog.csdnimg.cn/img_convert/bfa2d518a1a0c01c52530a5a8cec6b41.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=393&id=u5e6dc846&margin=[object Object]&name=image.png&originHeight=786&originWidth=1450&originalType=binary&ratio=1&rotation=0&showTitle=false&size=703236&status=done&style=none&taskId=u2436f5c7-22ff-44a9-b7c7-dff30a2e78d&title=&width=725)
![image.png](https://img-blog.csdnimg.cn/img_convert/43b1b71f4c35dc3d73724868c7e7b5ec.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=399&id=u06a3e15a&margin=[object Object]&name=image.png&originHeight=798&originWidth=1512&originalType=binary&ratio=1&rotation=0&showTitle=false&size=442930&status=done&style=none&taskId=u6576c8e6-74a3-4490-b5e9-24029966232&title=&width=756)
2.5 优化的关闭worker进程
优化的关闭只针对Http请求,对于websocket,TCP,UDP,Nginx无法得知worker是否在处理请求。
![image.png](https://img-blog.csdnimg.cn/img_convert/066e2fb4943863f6fa2bf51a410b9b7c.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=396&id=u267339ed&margin=[object Object]&name=image.png&originHeight=792&originWidth=1426&originalType=binary&ratio=1&rotation=0&showTitle=false&size=504452&status=done&style=none&taskId=ubc2ac892-79a0-4e7d-ba7b-80c350f4624&title=&width=713)
2.6 网络收发和Nginx事件间的对应关系
Nginx是一个事件驱动的框架,事件是指网络事件,一个网络连接对应两个事件(读事件和写事件)
2.6.1 网络传输
![image.png](https://img-blog.csdnimg.cn/img_convert/9280954645b4f9265cbc81fbcac07fcb.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=674&id=u0565fcd0&margin=[object Object]&name=image.png&originHeight=1348&originWidth=2472&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1078525&status=done&style=none&taskId=u633d97ba-b09e-4edb-94b3-01ebc78ba71&title=&width=1236)
2.6.2 TCP流和报文
![image.png](https://img-blog.csdnimg.cn/img_convert/d29b4aafc136d5d2757bb3ba373b6e59.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=672&id=u5ede5806&margin=[object Object]&name=image.png&originHeight=1344&originWidth=2434&originalType=binary&ratio=1&rotation=0&showTitle=false&size=971254&status=done&style=none&taskId=u834386c2-d0e9-410e-85a1-edc29a53265&title=&width=1217)
![image.png](https://img-blog.csdnimg.cn/img_convert/08c76618ae8f5ccfd5ec683262cfd5dd.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=657&id=ue85904c3&margin=[object Object]&name=image.png&originHeight=1314&originWidth=2394&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1049871&status=done&style=none&taskId=uaea3a798-39f8-4825-9bcb-400c221f76b&title=&width=1197)
2.7 Nginx事件循环
![image.png](https://img-blog.csdnimg.cn/img_convert/1d96a2afb5dd2a97bdcc69b15f9b7d94.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=659&id=u1e9fce32&margin=[object Object]&name=image.png&originHeight=1318&originWidth=2266&originalType=binary&ratio=1&rotation=0&showTitle=false&size=570999&status=done&style=none&taskId=u6cc79520-a8d8-41a7-9be6-5c635283aa3&title=&width=1133)
2.7.1 epool优劣已经原理
上图nginx等待服务器内核的事件队列使用epool来处理的。
![image.png](https://img-blog.csdnimg.cn/img_convert/9474b53f85db98bf69f54525559ffae8.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=677&id=uf37f6e9b&margin=[object Object]&name=image.png&originHeight=1354&originWidth=2398&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1073766&status=done&style=none&taskId=udcf0d437-d241-47e1-b3bc-2995e6b7fb1&title=&width=1199)
2.8 Nginx的请求切换
![image.png](https://img-blog.csdnimg.cn/img_convert/0de6f4b958800851f83c494f6ac2ad96.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=673&id=u30e09ba8&margin=[object Object]&name=image.png&originHeight=1346&originWidth=2418&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1149441&status=done&style=none&taskId=u6a575f63-c9a8-4d8e-87d2-dc1be5c0ebc&title=&width=1209)
nginx是用户态直接切换的,除非操作系统分给worker的时间分片到期了,否则一直工作,所以将worker的优先级调为-19,可以使得操作系统给worker分配更多的时间分片,提高Nginx性能。
2.9 同步和异步、阻塞和非阻塞的区别
阻塞和非阻塞是线程在访问某个资源时,数据是否准备就绪的一种处理方式。
阻塞方法:操作系统或者底层C库提供的方法或者是一个系统调用,这个方法可能是我的进程进入sleep状态(当前条件不满足,操作系统把我的进程切换到另外一个进程)。
非阻塞方法:我们调用该方法永远不会在我们时间分片未用完时,切换到另外一个进程。
同步和异步是用户态的调用方式而言。
同步:调用方法时,需要等待返回结果。
异步:调用方法后,无需等待返回结果,被调用方法处理完成后后主动通知给调用方。(支付回调)
2.9.1 阻塞调用:
![image.png](https://img-blog.csdnimg.cn/img_convert/5ce9869660cf411fea86265e2e5259a0.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=635&id=mmbW1&margin=[object Object]&name=image.png&originHeight=1270&originWidth=2308&originalType=binary&ratio=1&rotation=0&showTitle=false&size=756496&status=done&style=none&taskId=ufef84591-1516-49f2-bdb2-77410e81f32&title=&width=1154)
2.9.2 非阻塞调用:
![image.png](https://img-blog.csdnimg.cn/img_convert/c4ac4d3b176088f38c291a011f86417c.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=668&id=ue53dda1d&margin=[object Object]&name=image.png&originHeight=1336&originWidth=2320&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1003491&status=done&style=none&taskId=uc340540a-9101-4d2d-b070-8579213e891&title=&width=1160)
2.9.3 非阻塞调用下的同步和异步:
![image.png](https://img-blog.csdnimg.cn/img_convert/9b0d811d24cdf1061d68c45fd0cdd533.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=670&id=u34e4073a&margin=[object Object]&name=image.png&originHeight=1340&originWidth=2498&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1394172&status=done&style=none&taskId=u5d50ab4e-b982-4007-8151-ab0a6cb8a63&title=&width=1249)
openResty使得我们可以通过写同步的的方法,实际上以异步的来执行。
2.10 Nginx模块
![image.png](https://img-blog.csdnimg.cn/img_convert/e28a283a8cdeee392c2e3164fe55f244.png#clientId=u4080493f-2439-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=682&id=u8e0d1bec&margin=[object Object]&name=image.png&originHeight=1364&originWidth=2400&originalType=binary&ratio=1&rotation=0&showTitle=false&size=951445&status=done&style=none&taskId=u1e94db26-886a-48c5-9d00-3bb89d8d66e&title=&width=1200)
2.10.1 Nginx模块分类
![image.png](https://img-blog.csdnimg.cn/img_convert/da7ede8968243725eef8515bce8704eb.png#clientId=ue2063783-0659-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=504&id=uceec0e31&margin=[object Object]&name=image.png&originHeight=1008&originWidth=2000&originalType=binary&ratio=1&rotation=0&showTitle=false&size=765616&status=done&style=none&taskId=u78964cdd-88b3-461b-bb13-9d9927ab610&title=&width=1000)
2.11 Nginx连接池
说明:
每一个连接对应2个事件(读事件和写事件),如上图是通过数组序号来配合使用的。消耗的内存如下:
一个connection(232)+2个event(96*2) = 424字节。设置的连接数越多,消耗的内存就越大。
2.12 Nginx内存池
2.12.1 connection_pool_size:
- Default:connection_pool_size:256|512
内存池配置512字节,并不意味着只能分配512字节,当内存超过预分配内存大小时,是可以继续分配的。提前分配内存空间,减小分配的资源消耗。
2.12.2 request_pool_size:
- Default:request_pool_size:4k
请求池大小,默认4K。
2.12.3 为什么请求内存池大于远远大于连接内存池?
因为连接需要存储的上下文信息很少,只需要帮助后面的请求读取最初一部分字节就行;对于请求而言,需要保存大量上下文信息,比如:url和header。其对性能的影响比较小,极端场景下,url特别长,可以修改配置,增大请求内存池预分配的空间大小;通常情况下,url和header都很小,可以考虑降低请求内存池的预分配空间大小,最大化Nginx的并发量。
内存池对减小内存碎片和第三方模块开发是很有意义的。
2.13 Nginx进程间通信方式
2.13.1 哪些官方模块使用了共享内存呢?
- Nginx_http_lua_api是OpenResty的核心模块:
在如上代码中,同时使用了rbtree和链表:
lua_shared_dict dogs 10m;//使用红黑树来保存每一个key-value,每一个节点是它的key,节点值就是value。
//当内存大于10m时,使用lru方式淘汰,最先设置了key-value就会被淘汰,这就说明每个key-value连在了一起形成了一张链表。
2.13.2 Slab内存分配管理
如何把一整块内存切割成小块分配给每个红黑树节点使用的?Slab内存管理
Slab会把共享内存切割很多页面(4K),每个页面被切分为不同的slot(不同的slot分配内存空间不同128|256|512,乘2方式向上增长)。
Bestfit:比如30字节的内存,会被分配到32字节的slot。
2.13.3 查看slot使用状态
2.13.4 在OpenResty上使用tengine的slab_stat模块查看共享内存分配情况
编译安装(在编译安装OpenResty时,使用add-module把tengine的slab_stat模块安装进来):
案例:
2.14 Nginx数据容器
nginx的数据容器包括:数组、链表、队列、哈希表、红黑树、基数树
Nginx数组(nginx_array_T):
多块连续内存,每块连续内存中可以存放许多元素。
链表:
nginx_list_T
队列:
nginx_queue_T
哈希表
nginx_hash_elt_T
哈希表每个元素占用连续的内存。value是指针,指向用户数据,len长度,name就是hash的key。
Hash表应用于静态不变的内容。
Max size:控制了最大的hash表Bucket的个数,并不是实际最大hash表bucket的个数。
Bucket size:每个bucket的大小,向上对齐(cache line)。比如64位操作系统,操作系统每次读取64个字节,但bucket size配置60,这个时候实际是Nginx会设置为64,与操作系统对齐。,bucket size配置尽量不要超过64字节,以免占用内存过高。
红黑树
红黑树本身是一个二叉树,包含左节点和右节点。其次是一个查找二叉树,左节点比右节点小。可能会退化成一个链表,如右图。
- 红黑树优点:
- 使用红黑树的模块:
2.15 动态模块
使用案例:
(1)查看哪些模块支持动态模块:./configure —help | more
(2)把一个模块动态编译到nginx
(3)打开动态模块配置
load_module module/nginx_http_image_filter_module.so
image_filter resize 15 10;
2.16 配置指令
2.16.1 什么是指令的context?
该条配置指令能处于的配置块。
上图表明:log_format能在http模块上配置;access_log能在http,server…等模块配置。
一个配置存在多个配置块是是可以合并的。值指令可以合并;动作类指令不可以合并。判断依据:看该条配置的生效阶段。
值指令向上覆盖:
http模块指令合并规则:
三、详解HTTP模块
3.1 Listen指令
3.2 接收请求的事件模块
3.3 正则表达式:
3.4 如何找到处理请求的server指令块
3.4.1 server_name
- 指令说明:
- server_name多域名:
例如:
server{
server_name [aa.com](http://aa.com) [bb.com](http://bb.com); #其中aa.com是主域名
server_name_in_redirect off;
redirect 302 /redirect;
}
如上配置:如果访问bb.com,会直接跳转到bb.com/redirect
如果配置:
server{
server_name [aa.com](http://aa.com) [bb.com](http://bb.com); #其中aa.com是主域名
server_name_in_redirect on;
redirect 302 /redirect;
}
如上配置:如果访问bb.com,会302到aa.com/redirect
- server_name匹配顺序:
3.5 HTTP请求11个阶段
HTTP请求11个阶段的执行顺序:
上图说明:
(1)每一个阶段会有多个模块得到执行。
(2)limit_req和limit_conn都在preaccess阶段得到执行,但是如果limit_req阻止了本次请求,就会直接返回,limit_conn就得不到执行。
(3)access阶段的access执行通过后,后续auth_basic和auth_request两个阶段不会执行,直接跳到precontent阶段,同理content阶段也是如此。
3.5.1 postread阶段
(1)realip模块
把realip模块编译进Nginx模块?
三个指令:
set_real_ip_from:设置可信赖的ip(如本机IP或者机器某台机器IP),找到用户真实ip
real_ip_header:ip值从哪个参数中取(X-Real_Ip |X-Forwarded-For)
real_ip_recursive:环回地址,取 X-Forwarded-For最后一个地址如果和客户端地址一样就pass掉,取上一个地址。
(2)如何拿到真实的用户IP地址?
说明:当网络请求过程中,存在很多反向代理服务器时,Nginx会把用户真实的ip写在X-Real_IP中,而每经过一层反向代理,会吧上一层IP追加在X-Forwarded-For中。
拿到真实用户IP后如何使用?
3.5.2 rewrite阶段
(1)rewrite模块return指令
error_page:收到某个特定的返回码时,重定向某个url或者给用户返回特定内容。
return示例:
server与location块下的return指令关系?
server块的return先于location块的return执行。
return与error_page指令的关系?
访问不存在的资源,执行error_page指令;如果执行了return指令,error_page得不到执行。
(2) rewrite指令
案例:
问题1:rewrite指令优先级高于return
问题2:依次返回“test3”、“test3”、”third!”
问题3:不携带break,就往下执行到return 200 ‘second’模块
问题:依次返回301、302、302、301
rewrite日志记录:rewrite_log:on就可以开启了
(3)if指令
3.5.3 find_config阶段
(1)location指令
匹配规则:
匹配结果:
/Test1:5,6;
/Test1/:1,3,5;
/Test1/Test2:2,4,5
/Test1/Test2/:4,5
/test1/Test2:2
匹配顺序
同上问题,返回什么?
/Test1:exact match!
/Test1/:stop regular expressions match!
/Test1/Test2:lonest regular expressions match!
/Test1/Test2/:lonest regular expressions match!
/test1/Test2:lonest regular expressions match!
3.5.4 preaccess阶段
(1)limit_conn指令
key一般为用户的IP地址(remote_addr变量)。
$binary_remote_addr 关键字,这里是限制同一客户端ip地址;
限制连接数:
要限制连接,必须先有一个容器对连接进行计数,在http段加入如下代码:
“zone=” 给它一个名字,可以随便叫,这个名字要跟下面的 limit_conn 一致
$binary_remote_addr = 用二进制来储存客户端的地址,1m 可以储存 32000 个并发会话
上图配置说明:现在某个ip的并发数为1,超过1就返回500,记warn级别日志,网站返回速率为50字节/s
(1)limit_req指令
leaky bucket算法:
limit_req_zone:$binary_remote_addr zone=one:10m rate=2r/m;
location / {
limit_req zone=one burst=3 nodely;
}
第一段配置参数:
- $binary_remote_addr :表示通过remote_addr这个标识来做限制,“binary_”的目的是缩写内存占用量,是限制同一客户端ip地
- zone=one:10m:表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息
- rate=2r/s:表示允许相同标识的客户端的访问频次,这里限制的是每秒1次,即每秒只处理一个请求,还可以有比如30r/m的,即限制每2秒访问一次,即每2秒才处理一个请求。
第二段配置参数:
- zone=one :设置使用哪个配置区域来做限制,与上面limit_req_zone 里的name对应
- burst=5:重点说明一下这个配置,burst爆发的意思,这个配置的意思是设置一个大小为5的缓冲区当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内等待,但是这个等待区里的位置只有5个,超过的请求会直接报503的错误然后返回。
- nodelay:
- 如果设置,会在瞬时提供处理(burst + rate)个请求的能力,请求超过**(burst + rate)的时候就会直接返回503,永远不存在请求需要等待的情况**。(这里的rate的单位是:r/s)
- 如果没有设置,则所有请求会依次等待排队
说明:limit_conn模块优先级大于limit_req。
3.5.5 access阶段
- access模块(对用户ip校验)
判断请求是否可以继续向下访问。
- auth_basic模块(对用户用户名+密码验证)
示例:
- auth_request模块(用于做统一用户鉴权系统)
- satisfy指令
satisfy all:access的3和指令均放行这个请求,这个请求才向下执行,任何一个拒绝,400或500返回。
anny:access的3和指令有一个指令放行这个请求,这个请求就向下执行。
- 不会。
- 有影响。
- 可以
- 可以
- 没有机会输入
3.5.6 precontent阶段
- try_file指令
- mirror模块(做流量拷贝)
3.5.7 content阶段
- static模块
(1)root和alias指令
/root :404
/root/1.txt :—>html/first/root/1.txt 也是404
/alias:—>html/index.html 200
/alias/1.txt:—>html/first/1.txt 200
(2)3个nginx变量
(3)static模块对url不以斜杠结尾却访问目录的的做法
- index模块和autoindex模块
- concat模块(合并小文件,提升网络性能)
3.5.8 log阶段
3.5.9 HTTP过滤模块
- sub模块
示例:
- addition模块
3.6 HTTP的变量
3.6.1 HTTP请求相关变量
3.6.2 TCP连接相关的变量
3.6.3 请求中产生的变量
3.6.4 方式HTTP响应时相关的变量
3.6.5 系统变量
3.7 referer模块
valid_referers指令
结果:403,valid,valid,valid,403,valid,403,valid
3.8 map模块
3.9 split_clients模块
3.10 geo模块
3.11 geoip模块
3.12 keepalive
四、反向代理和负载均衡
4.1 基本介绍
4.1.1 负载均衡
一个服务的扩展方向:
A:水平扩展,加机器;
B:纵向扩展:把业务复杂的服务,拆分为业务小的服务,上层nginx通过location把请求分发到不同的服务中去;
C:Z轴扩展:基于用户的信息进行扩展。例如根据请求ip或者其他信息,把请求分发到特定的服务中去;
4.1.2 反向代理
4.1.3 缓存
时间缓存:Nginx从下游服务拿到信息后,一边发给客户端,一边把返回内容缓存在nginx中;
空间缓存:上游服务请求nginx,nginx可以预请求下游服务,把数据缓存到nginx中;
4.2 负载均衡策略
4.2.1 upstream与server指令
- 加权Round-Robin负载均衡算法:
- 对下游服务使用keepalive长链接:
- upstream_keepalive指令:
keepalive:nginx和下游服务最多保持多少个空闲的http连接
keepalive_request:一个tcp最多跑多少个请求
keepalive_timeout:一个tcp连接空闲多少秒后关闭
- resolver指令
官方解释下就是:反向代理的场景,upstream后端用域名时,配置resolver以便于nginx能够解析该域名。
当proxy_pass 后面接变量时,而且设置了resolver,会把变量的负载值通过resolver来解析,其他情况通过本地dns服务,etc或host 来解释域名。
https://www.jianshu.com/p/5caa48664da5
4.3 负载均衡哈希算法
4.3.1 upstream_ip_hash模块
4.3.2 upstream_hash模块
4.3.3 演示
upstream iphashtest {
ip_hash;
hash user_$arg_username;#使用username作为hash算法的关键字
server 127.0.0.1:8011;
server 127.0.0.1:8012;
}
4.4 一致性hash算法
使用hash算法,下游服务器异常会导致大量请求的路由策略失效。一致性hash算法能有效解决该问题。
把0-232围成一个环,4个服务均有的分布在环上,0-230请求第一个服务,以此类推;
扩容后:
扩容后只会改变node2—node4前半段的hash点。
4.4.1 使用方法
4.5 最小连接数算法
4.5.1 upstream_least_conn模块
4.6 怎么跨worker生效:upstream_zone模块
如上所有算法均可以使用upstream_zone来使得所有worker生效。
4.7 upstream模块间的顺序
4.8 http upstream提供的变量
4.9 反向代理
4.9.1 http反向代理proxy处理请求的流程
4.9.2 proxy模块
- 案例
http://proxyups/addurl会被换为:http://proxyups/a
4.9.3 修改请求下游服务的的请求
4.10 接收用户请求body
client_body_buffer_size:Nginx分配给请求数据的Buffer大小,如果请求的数据小于client_body_buffer_size直接将数据先在内存中存储。如果请求的值大于client_body_buffer_size小于client_max_body_size,就会将数据先放到client_body_buffer_size的内存中,再一遍一遍地写到临时文件中。
client_body_in_single_buffer:客户端请求数据的body一律存储到内存buffer中。当然,如果HTTP包体的大小超过了下面client_body_buffer_size设置的值,包体还是会写入到磁盘文件中。
client_max_body_size 默认 1M,表示 客户端请求服务器最大允许大小,在“Content-Length”请求头中指定。如果请求的正文数据大于client_max_body_size,HTTP协议会报错 413 Request Entity Too Large。就是说如果请求的正文大于client_max_body_size,一定是失败的。如果需要上传大文件,一定要修改该值。
2次读取body时间超过60s后,返回408.
4.11 连接下游服务器
当与下游服务建立连接超时时,再换一台服务器重新连接。
4.11.1 TCP keepalive
关闭无用的连接,减少资源浪费。
使用操作系统设置的默认keepalive相关配置来控制tcp的keepalive来降低资源使用。
4.11.2 HTTP keepalive
4.11.3 修改TCP连接中的local address
proxy_bind隶属于proxy_module,为向后端建立连接时的local ip,在nginx源码中只支持bind一个ip进行回源,若想使用多个ip进行回源时,可以修改源码支持bind ip数组。在实际应用中我就是这样做的。bind ip数据轮询选择ip进行回源与upstream建立连接,以解决单ip回源连接数限制问题。
proxy_bind:它的用法主要有两类用途,第一类用途就是当我们nginx上有多个ip地址时,可能有多个路由的策略是不同的,比如内网或者外网等,这个时候不要使用系统默认给我们选择的ip地址,而是主动使用一个ip地址。这个时候用proxy_bind。第二种场景,很可能为了传递一个ip地址,就是透传ip地址的策略,比如在stream反向代理中会很常用,在之后还会详述。这里先说下proxy_bind用法。
proxy_bind $remote_addr 也就是客户端的地址绑定到这里。绑定变量的时候呢,如果地址不是本地的地址,linux必须要加transparent。非linux操作系统呢需要保证worker进程有root 权限的,才能人为的修改socket的local address。
4.12 接收下游的响应
4.12.1 接收下游服务的响应头部
proxy_buffer_size:限定了接收自上游的http response中header的最大值。所以当上游的server发送了http响应,如果有set cookie这种特别长的header可能就会导致整个全部的response header超出了这个值。超出完之后这个请求就不能够被nginx正确的处理了。我们的error.log中会看到upstream sent too big header
就是这样的一个原因。
4.12.2 接收下游服务的响应body
proxy_buffering:来控制我们是不是先接收完整的包体,接收完了才开始转发。或者说不接收完,而是每接收一部分就同步的向客户端发送我收到的那部分响应。这两种方式各有各的好处。通常情况下默认开启(on)。因为我们认为上游服务和我们nginx走的是内网,网速更快。如果我们边发边接收上游边往客户端发。因为客户端跟nginx之间的网速可能很慢。所以就会导致,对于比较大的body 的时候,nginx长时间与上游建立连接。而上游比如说tomcat、Django等它们的并发能力是很弱的。当然如果我们用了proxy_buffering off 它的优点是能让客户端及时接收到响应,特别是一些大包体的情况下。客户端不用再等待。
在我们接收上游发来的HTTP包体,即使我们开启了proxy_buffering on也并不一定向磁盘中写入包体,因为如果包体非常的小,在内存中就可以放入的话,就没有必要写到磁盘中,因为磁盘io总是比较慢的。所以这个时候就有了proxy_buffers指令。也就是包体大小没有超过这个设定值就不用写入磁盘。否则的话就要写入磁盘了。
proxy_buffering ,默认开启,希望尽快释放上游服务器的连接,当然proxy_buffering 还有一个nginx特定的header。这个header(X-Accel-Buffering头部)只有nginx才会认。当上游的tomcat 如果在response中加入X-Accel-Buffering头部,如果配置为yes,就会强制要求nginx先接收完上游的http body 再向client发送。也就是它会替换指定的内容。
当我们向磁盘中写入包体的时候还有三个指令,proxy_max_temp_file_size、proxy_temp_file_write_size、proxy_temp_path。
proxy_max_temp_file_size:限制写入磁盘中这个文件的最大值。如果上游服务中返回了非常大的文件超出了临时文件大小也会出错的。默认是1G。
proxy_temp_file_write_size:每一次向磁盘文件中写入的字节数。
proxy_temp_path:设定了存放临时文件的目录在哪里,以及目录level层级。
4.12.3 及时转发body
proxy_busy_buffers_size。虽然被缓存所有的响应,我们希望更及时的向客户端发送部分响应。比如我们收到1G文件。当我们接收到前8k或前16k(proxy_busy_buffers_size 8k|16k)的时候,就先向客户端转发接收到的这一部分响应的话就可以使用proxy_busy_buffers_size 。
4.12.4 接收下游服务时网络速度相关指令
proxy_read_timeout:两次读取操作之间最多60秒的超时。两次读取是一个TCP层的一个概念。
proxy_limit_reate:限速,和客户端limit_rate 有些类似,但是它限制的是读取上游的响应,而不是发送给上游服务的网速。设置为0表示不限制读取上游响应的速度。
4.12.5 上游body的持久化
proxy_store_access:配置指定目录权限。
proxy_store:把临时文件改名到root对应的目录下,默认不开启,如果是string
,可再次指定,使用变量的方式,指定这个文件存放的位置。
4.13 处理下游响应头部
4.13.1 返回响应-加工响应内容
我们接收到了上游发来的http header跟http body,其实对上游发来的http header是有很多控制nginx行为的这样一些头部的。我们在反向代理这一层也可以去修改上游发来的header中的内容,以及它们所产生的效用。接下来看看这两种行为是怎么发生的。
第三部分讲的http 过滤模块。当我们生成的响应向客户端发送的时候。这个内容必须经历过滤模块的处理。对于nginx作为反向代理的时候也是同样的。nginx下游服务返回的一些header会被那些过滤模块处理(图中)。
比如ngx_http_modified_filter_module,它根据上游服务返回的cache control等等这些header,去修改到底是发送200还是304响应码给客户端。所以上游一些header的内容会改变作为反向代理的nginx的行为。
- proxy_ignore_header指令
proxy_ignore_header可以禁止某些响应头改变nginx行为。
- proxy_hide_header指令
nginx提供了一个指令proxy_ignore_header,禁用上游中一些header 的功能。当然不是所有的header 都具有功能,这个指令只针对于具有特殊功能的header才发生作用。
说明:
(1)X-Pad:是apache 使用的header,目前已经很少用了。
(2)X-Aceel-:只有nginx才认。
(3)如果上述这四类header你想发送给客户端,就使用proxy_pass_header。
- 修改返回的set-cookie头部
修改下游服务器response header中的头部set-cookie中的内容。proxy_cookie_domain修改下游服务器返回的cookie中的域名;
- 修改返回的Location头部
proxy_dedirect:对于我们上游服务发送的响应中有一个location,location后面的url可以做一次替换。
4.13.2 处理返回响应演示
(1)反向代理服务配置:
(2)下游服务配置:
直接访问localhost:8012如下所示有个aaa的header:
访问反向代理服务时也有个aaa的header:
(3)禁用反向代理服务的aaa:
proxy_hide_header aaa;
再次访问反向代理服务,发现没有了aaa的header。
(4)打开proxy_pass_header,可以把下游服务的nginx版本返回给客户端:
proxy_pass_header server
再次访问反向代理服务,发现server是nginx 1.15.6而非反向代理的openresty。
总结:以上几节课包括从proxy_pass指令来指定由反向代理来指定请求到生成向上游发送请求的内容,以及接收客户端发来的http body并与上游服务建立连接并上游服务的响应内容以及处理响应头部,以上几个环节,就是nginx作为反向代理处理客户端与上游服务之间的所有流程。
4.13.3 下游返回失败时处理方法
我们讨论了nginx作为反向代理时,从客户端接收http body,到完整的接收下游的响应并转发响应的流程。其中在nginx与下游服务建立连接时。提到过proxy_next_upstream指令,这个指令可以在第一台的错误的响应后重新选择另一台下游服务器处理请求返回给客户端这样的功能。
能够生效的前提是没有向客户端发送一个字节。只要向客户端已经发送了一个字节了,说明下游的服务已经生效了,就不能选用一个新的下游服务了。它一定是在接收到并开始转发一个字节之前nginx判定为错误,这个功能才能生效。proxy_next_upstream后面可以跟很多不同的参数(error、timeout那些)。
配置:
- error:
nginx与上游建立连接读取响应,发送请求等等这些过程中,只要出现错误等等。error都可以满足这样的场景。这种错误主要是网络错误。比如TCP层、IP层的错误。
- timeout:
超时有connection timeout 、read timeout、 write timeout,这个timeout可以命中这些场景。当出现这种场景的时候将执行重选另一个下游服务。
- invalid_header:
则是我们收到的下游服务http header,它是不合法的。
- http_:
http_可以跟一个明确的响应code。上游返回一个403或500,其实它既不是网络错误,也不是超时,也不是invalid_header。但是我们就是可以针对这样的错误从新选择一个新的upstream上游去处理。
- non_idempotent:
根据RFC 7231文档中规定了post、lock等method的请求。在下游服务不能使用next_upstream上游服务的,去重选一个新的服务时候,当我们配置了这个non_idempotent就可以启用proxy_next_upstream功能。
- off:
关闭 proxy_next_upstream功能。
两个相关指令:
proxy_next_upstream_timeout 超时时间;proxy_next_upstream_tries 重试次数
4.13.4 下游返回失败时处理演示
反向代理配置:
下游服务:
(1)关闭proxy_next_upstream,访问反向代理服务就会在8011和8012端口服务上轮询
(2)打开proxy_next_upstream,且关闭下游8013服务,访问反向代理服务,一直是8011服务做响应。
(3)修改反向代理服务配置:
location /httperr {
proxy_next_upstream http_500;//下游服务返回500时生效
proxy_pass http://nextups;
}
访问反向代理服务,一直是8011服务做响应。
4.13.5 使用error_page拦截下游失败响应
当下游响应码大于等于 300时,就应该使用error_page来处理请求返回给客户端。
proxy_intercept_errors 默认off。这个时候上游返回怎样的响应,客户端就会原封不动的拿到这个响应。比如上游返回来一个404,我们的error_page 配置的404是不会生效的。只有把proxy_intercept_errors 设置为on的时候error_page 就会生效了。
- 演示:
代理服务配置:
proxy_intercept_errors 为on时,配置了error_page,发现500错误码的时候返回test1.txt。
4.14 对下游使用SSL连接
Nginx提供以上4种关于证书的指令。
- 对上游使用证书
- 验证上游证书
- 对下游使用证书
- 验证下游证书
4.14.1 SSL模块提供的变量
4.14.2 创建证书命令示例
4.15 浏览器缓存
在互联网中使用缓存是最有效的提升访问速度的方法。在web服务器场景中不仅要考虑nginx作为缓存服务时的使用方法,还要考虑浏览器缓存生效的场景。浏览器的缓存是否生效可以通过nginx的指令去控制。而浏览器的缓存对用户的体验提升也是最大的。
4.15.1 浏览器缓存和Nginx缓存的比较
4.15.2 浏览器缓存
- Etag头部
- If-None-Match
- If-Modified-Since
Last-Modified:比较简单,也就是我们访问的资源,比如我们使用了一个js文件,这个js文件的在服务器上上次被修改时间。
4.16 Nginx决策浏览器过期缓存是否有效
4.16.1 Expires指令
演示:
4.16.2 not_modified过滤模块
not_modified过滤模块处理过程:
- if_modified_since
- If-Match
- If-Unmodified-Match
4.17 Nginx缓存
这节课介绍在nginx之上配置上游服务器返回的响应的缓存。会涉及到一些指令的值,它是与第二部分课程中介绍过nginx进程结构的时候谈到的Cache Manager、Cache Loader 这两个进程。
4.17.1 nginx的cache使用
内容是放到磁盘上的,但是它的元素信息为了加快访问是放到内存中的。所以首先在proxy_cache_path指令中定义好共享内存。因为我们有多个worker进程,所以这些元信息一定是在共享内存中的。第二个定义在磁盘中哪个位置去存放缓存文件。proxy_cache_path定义好以后,其中keys_zone的name就是共享内存的名字,size就是共享内存的大小。共享内存的名字就是给proxy_cache使用的。也就是在proxy_cache_path定义了一批缓存文件存放的位置和共享内存的名称。也许有很多location,它们定义了各自独立的缓存的key或缓存策略。但是它们都可以使用同一个proxy_cache_path指定的keys_zone。所以proxy_cache在location中可以通过这个zone指定使用哪一个proxy_cache中的设置。
4.17.2 proxy_cache_path
use_temp_path:使用这个临时文件目录,最后改名都会放到path中。但为什么会有这个设置呢?是因为,很可能nginx所在的机器中有多个文件系统,甚至有些网络文件系统。如果我们开始的use_temp_path 目录是在一个磁盘上,而path是在另外一个磁盘上。跨磁盘复制是在cp文件。如果在一个磁盘上,那最后的改名也只是改名而已。
4.17.3 proxy_cache_valid: 缓存什么样的响应
4.17.4 proxy_no_cache: 什么样的响应不缓存
proxy_cache_convert_head:默认为on,会把header方法转换成get方法。
4.17.5 upstream_cache_status变量
EXPIRED:表示nginx cache_vaild设置的时间还没有过期,用户的请求获取到缓存,但是上游服务器指定的缓存时间也许是小于cache_vaild设置的时间,根据上游的说法来说缓存已经过期了,但nginx配置的时候,这个缓存仍然在使用。所以和这个时候是EXPIRED,缓存已经过期。
4.18 对客户端请求的缓存处理
补充说明:
proxy_cache_methods GET | POST|HEAD...//对哪些请求方法使用缓存
4.19 接收下游响应缓存处理
补充说明:
- X-Accel-Expires头部:
从下游服务定义缓存多少时间(0不缓存;@前缀表示缓存当天的某个时间)
- Vary头部:
- Set-Cookie:
4.19.1 缓存流程
4.19.2 使用分片提示缓存效率
- slice模块
- slice模块运行流程
4.19.3 open_flie_cache提示系统性能
- 缓存元信息
- 其他open_flie_cache指令
- 案例
下游服务配置:
第一次访问:
第二次访问:
4.20 Nginx缓存失效时如何减轻下游服务压力
nginx重启或者意外退出时,所以缓存就会失效,这个时候接收下游服务全部穿透Nginx打到下游服务,导致下游服务压力剧增。
4.20.1 合并回源请求
4.20.1 减少回源请求
proxy_cache_use_stale:第一个请求来了后打到下游服务,后续的请求给客户端响应旧的缓存内容,直至第一个请求的下游响应被缓存。
对应缓存有问题的响应:
例子:
proxy_cache_background_update:第一个请求来了把旧的缓存响应给客户端,同时向下游服务发送一个子请求,后续的请求给客户端响应旧的缓存内容。直至第一个请求的子请求收到下游服务响应内容后,更新缓存,后续的请求均可使用到新缓存。
4.21 及时清除缓存
4.22 http反向代理和uwcgi、fastcgi、scgi反向代理的指令对照
4.22.1 构造请求内容的指令对照
4.22.2 建立连接并发送请求的指令对照
4.22.3 接收下游响应的指令对照
4.22.4 转发响应的指令对照
4.22.5 SSL的指令对照
4.22.6 缓存的指令对照
4.22.7 独有配置
4.23 memcached反向代理用法
4.23.1 memcached指令
演示:
代理服务器配置:
[root]:#curl xxx.com/get?key=hello
[root]:#world//取出了hello的值
补充说明:
memcached_gzip_flag:如果设置的key使用了gzip压缩,取出的时候需要这个指令来标识。
4.24 webSocket反向代理
- 功能
由nginx_http_proxy_module模块实现
- 配置
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”;
websocket协议帧
websocket协议和扩展
测试
4.25 HTTP2.0
4.25.1 http2.0特性
4.25.2 http2.0核心概念
- 协议分层:
- 多路复用
- 传输无序,接收时组装
- 数据流优先级
- 标头压缩
- Frame格式
- 服务器PUSH
4.25.3 搭建http2.0服务并推送资源
- 使用模块ngx_http_v2_module
- 推送资源
- 测试
服务器配置:
设置了http2_push和http3_push_preload
请求:nghttp2 -ns https://localhost:4430/
请求:nghttp2 -ns https://localhost:4430/test/test.html
- 其他指令
- 最大并行推送数
- 超时控制
- 并发请求控制
- 连接最大请求数
- 缓存区大小设置
4.26 grpc反向代理
协议:grpc协议(https://grpc.io)
模块:ngx_http_grpc_module,默认开启,可以通过without–ngx_http_grpc_module禁用
依赖:ngx_http_v2_module模块
2.26.1 grpc代理和http反向代理指令对照
4.27 传输层stream四层反向代理的七个阶段
- 七个阶段
- stream模块的ssl阶段
- content阶段return模块
4.27.1 常用变量
- 系统变量
- 演示
服务器配置:
连接:
telnet localhost:10004,返回如下:
4.27.2 POST_ACCEPT阶段realip模块
proxy_protocol协议
Nginx读取proxy_protocol协议超时控制:
stream处理proxy_protocol流程
post_accept阶段:realip模块
演示
服务器配置:
连接:
4.27.3 PREACCESS阶段limit_conn模块限制并发
limit_conn模块指令
4.27.4 ACCESS阶段access模块限制ip
access模块指令
4.27.5 LOG阶段stream_log模块记录日志
4.27.6 stream四层反向代理处理SSL上游客户端请求
stream模块TSL/SSL应用场景
stream中的ssl
对比stream模块的ssl和http模块的ssl配置
stream中的ssl模块提供的变量
stream ssl模块实战
反向代理配置:
下游服务配置:
收到上游带ssl的请求,剥离调ssl证书,向下游服务发送请求。
stream_preread取出ssl关键信息
实战:
反向代理层根据域名代理到不同的下游服务。
4.27.7 stream proxy四层反向代理的用法
proxy模块对上下游的的限速指令
反向代理相关指令
4.28 UDP反向代理
基本流程:
简要说明:
实战:
4.29 透传IP地址的3种解决方案
方案一:protocol协议
方案二:修改IP报文
B–>A:
五、Nginx的系统层性能优化
优化方法论:
5.1 内存效率
5.1.1 用tcmalloc优化内存分配
tcmalloc是什么?
使用方法?
5.2 CPU效率
5.2.1 如何增大Nginx使用CPU的有效时长?
worker进程数等于cpu核数
worker_process number | auto #一般设置为cpu核数
为何一个CPU能同时运行多个进程?
确保进程在运行态
减少进程间切换
延迟处理新连接
如何查看上下文切换次数?
什么决定CPU时间片的大小?
O1调度算法(CFS)
设置worker进程的静态优先级
5.2.2 多核间的负载均衡
使用linux内核3.9以上,打开reuseport,提高性能。
多队列网卡对多核CPU的优化(RSS、RPS、RFS)
绑定worker到指定CPU,提升CPU的缓存利用率
worker_cpu_affinity:
NUMA架构,提升CPU访问内存的效率
5.3 网络效率
5.3.1 控制TCP三次握手参数
SYN_SENT状态
主动建立连接时应用层超时时间
SYN_RECVD状态
Linux服务器处理三次握手
5.3.2 建立TCP连接的优化
如何应对SYN攻击
设置tcp_syncookies
设置句柄数上线
设置worker进程最大连接数量
设置两个队列的长度
开启TCP Fast Open
说明:正常TCP三次握手需要等待server返回syn+ack后,发送ack+http请求,fast open tcp是第一次建立连接后,client保存cookie,第二次直接带上cookie请求sever,减少前面2次握手。
5.3.3 传输数据阶段滑动窗口和读写缓冲区
滑动窗口
通告窗口?
收到对方的syn后,在回复ack时需要告诉请求方我还有多大的空间接收你的内容,这就是通告窗口。
发送TCP消息
TCP接收消息
TCP接收消息发生CS(进程间切换)
TCP消息接收时新报文到达
Nginx的超时指令和滑动窗口
TCP传输时的丢包重试
5.3.4 优化缓冲区和传输效率
TCP缓冲区
TCP调整接收窗口和应用缓存
BDP=带宽*时延,吞吐量=窗口/时延
禁用Nagle算法?
Nginx可以避免发送小报文
启用CORK算法
5.3.5 慢启动和拥塞窗口
滑动窗口:发送方主动限制流量
滑动窗口:接收方限制限制流量
实际流量:滑动窗口和滑动窗口的最小值
拥塞窗口
RTT和RTO
5.3.6 TCP协议的keepalive功能
应用场景
检查实际断连的连接
用于维持和客户端的防火墙有活跃的网络包
设置
5.3.7 减少关闭连接时的time_wait端口数量
服务器被动关闭连接端的状态
服务器主动关闭连接端的状态
优化time_wait
time_wait状态过短或者不存在会怎么样?
tcp_tw_reuse
5.3.8 lingering_close延迟关闭TCP连接
lingering_close延迟的意义?
lingering配置指令
以RTS代替正常的四次握手关闭连接
5.3.9 应用层协议优化
TLS/SSL优化握手性能
TLS/SSL中的会话票证tickets
HTTP长链接
gzip压缩模块
压缩哪些请求的响应?
是否压缩下游的响应?
其他压缩参数:
升级更高效的http2.0协议
5.4 磁盘IO效率
5.4.1 大致优化方向
减少磁盘IO
直接IO绕开磁盘高速缓存
直接IO适用于大文件:
异步IO
异步读IO线程池
做静态资源服务读取文件时使用异步读IO线程池:
异步IO缓存
5.4.2 减少磁盘读写次数
empty_gif模块
access日志的压缩
error.log日志输出到内存
syslog协议
rsyslog与nginx:
5.4.3 零拷贝与gzip_static模块
sendfile 零拷贝提升性能
注意:直接IO会自动禁用sendfile
gzip_static模块
gunzip模块
5.5 监控分析Nginx运行状态
5.5.1 使用gperftools定位Nginx性能问题
文本展示说明:
5.5.2 使用stub_status模块监控Nginx状态
stub_status模块监控项
六、从源码视角深度使用Nginx和OpenResty
6.1 Nginx与第三方模块的关联关系
6.1.1 第三方模块源代码的快速阅读方法
config结构
configure脚本分析
configure执行结果
(1)configure概要,使用了哪些库或者功能
(2)一些重要文件的路径
6.1.2 Nginx启动流程
HTTP第三方模块初始化
HTTP模块的11个阶段:初始化
添加模块到11个阶段的两种常用方式
过滤模块的单链表
添加过滤模块的方式
6.1.3 Rewrite脚本
协程与Rewrite脚本
Rewrite脚本设置变量
脚本式编程:指令的实现
6.1.4 if指令
当if指令块连续出现时
说明:当if指令连续出现时,最后一个if为真的模块将一直影响到该模块结束。
if指令出现问题的根本原因
if指令的正确姿势
6.1.5 解读Nginx核心转储文件(coredump)
coredump核心转储文件
gdb调试基本命令
启用多进程模式
Debug定位问题:出现错误时的应对方案
6.1.6 通过debug日志定位问题
控制debug级别error.log日志的输出
debug日志类别
6.2 OpenResty的用法
6.2.1 OpenResty概述
OpenResty组成部分
OpenResty运行机制
OpenResty中的SDK
OpenResty的使用要点
6.2.2 OpenResty中的Nginx模块
核心模块
反向代理模块
工具模块
6.2.3 OpenResty中的Lua模块
6.2.4 如何在Nginx中嵌入Lua代码
Nginx启动过程中嵌入Lua代码
在11个HTTP阶段中嵌入Lua代码
控制rewrite/access是否延迟执行Lua代码
Http反向代理流程嵌入Lua代码(balancer_by_lua)
Http反向代理收到响应-加工响应内容
在负载均衡和过滤响应时嵌入Lua代码
在OpenSSL处理SSL协议时嵌入Lua代码
在Lua代码中获取当前阶段
6.2.5 Openresty中Lua和C代码交互原理
Lua FFI
系统级配置指令
取得配置参数的SDK
6.2.6 获取、修改请求和响应的SDK
读取、修改变量的SDK
客户端提前关闭连接
获取请求头部的SDK
获取请求URL的SDK
获取请求Body的SDK