nginx学习笔记
Posted ~千里之行,始于足下~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nginx学习笔记相关的知识,希望对你有一定的参考价值。
第一章
一: nginx简介
-
nginx(2002年开发,2004年10才出现第一个版本0.1.0):web服务器,市场份额,排在第二位,Apache(1995)第一位;
-
web服务器,反向代理,负载均衡,邮件代理;运行时需要的系统资源比较少,所以经常被称呼为轻量级服务器;
-
是一个俄罗斯人(Igor Sysoev),C语言(不是c++)开发的,并且开源了;
-
nginx号称并发处理百万级别的TCP连接,非常稳定,热部署(运行的时候能升级),高度模块化设计,自由许可证。
-
很多人开发自己的模块来增强nginx,第三方业务模块(c++开发); OpenResty;
-
linux epoll技术; windows IOCP
二:为什么选择nginx
-
单机10万并发,而且同时能够保持高效的服务,epoll这种高并发技术好处就是:高并发只是占用更多内存就能做到;
-
内存池,进程池,线程池,事件驱动等等;
-
学习研究大师级的人写的代码,是一个程序开发人员能够急速进步的最佳途径;
三: 安装nginx,搭建web服务器
3.1安装前提
-
epoll,linux 内核版本为2.6或者以上;
-
gcc编译器,g++编译器
-
pcre库:函数库;支持解析正则表达式;
-
zlib库:压缩解压缩功能
-
openssl库:ssl功能相关库,用于网站加密通讯
3.2nginx源码下载以及目录结构简单认识
-
nginx官网 http://www.nginx.org
-
nginx的几种版本
a. mainline版本:版本号中间数字一般为奇数。更新快,一个月内就会发布一个新版本,最新功能,bug修复等,稳定性差一点;
b. stable版本:稳定版,版本号中间数字一般为偶数。经过了长时间的测试,比较稳定,商业化环境中用这种版本;这种版本发布周期比较长,几个月;
c. Legacy版本:遗产,遗留版本,以往的老版本;
-
安装,现在有这种二进制版本:通过命令行直接安装;
-
灵活:要通过编译 nginx源码手段才能把第三方模块弄进来;
-
目录介绍
auto / :编译相关的脚本,可执行文件configure一会会用到这些脚本
cc / : 检查编译器的脚本
lib / : 检查依赖库的脚本
os / : 检查操作系统类型的脚本
type / : 检查平台类型的脚本
CHANGES : 修复的bug,新增加的功能说明
CHANGES.ru : 俄语版CHANGES
conf / : 默认的配置文件
configure : 编译nginx之前必须先执行本脚本以生成一些必要的中间文件
contrib / : 脚本和工具,典型的是vim高亮工具
vim / : vim高亮工具
html / : 欢迎界面和错误界面相关的html文件
man / : nginx帮助文件目录
src / : nginx源码目录
core : 核心代码
event : event(事件)模块相关代码
http : http(web服务)模块相关代码
mail : 邮件模块相关代码
os : 操作系统相关代码
stream : 流处理相关代码
objs/:执行了configure生成的中间文件目录
ngx_modules.c:内容决定了我们一会编译nginx的时候有哪些模块会被编译到nginx里边来。
Makefile:执行了configure脚本产生的编译规则文件,执行make命令时用到
- nginx的编译和安装
a. 编译的第一步:用configure来进行编译之前的配置工作
./configure
--prefix:指定最终安装到的目录:默认值 /usr/local/nginx
--sbin-path:用来指定可执行文件目录:默认的是 sbin/ nginx
--conf-path:用来指定配置文件目录:默认的是 conf/nginx.conf
b. 用make来编译,生成了可执行文件 make
c. 用make命令开始安装 sudo make install
3.3:nginx的启动和简单使用
-
启动: sudo ./nginx
-
乌班图:192.168.1.128
//百度:“服务器程序端口号”
//百度:“监听端口号”
四: nginx的整体结构
-
master进程和worker进程概览(父子关系)
-
启动nginx,看到了一个master进程,一个worker进程
-
ps -ef命令
第一列:UID,进程所属的用户id
第二列:进程ID(PID),用来唯一的标识一个进程 第三列:父进程ID(PPID)。 fork(),worker进程是被master进程通过fork()创建出来的-worker进程是master进程的子进程,master是父进程
-
nginx进程模型
a. 1个master进程,1到多个worker进程 这种工作机制来对外服务的;这种工作机制保证了 nginx能够稳定、灵活的运行;
b. master进程责任:监控进程,不处理具体业务,专门用来管理和监控worker进程;master,角色是监工,比如清闲;
c. worker进程:用来干主要的活的,(和用户交互);
d. master进程和worker进程之间要通讯,可以用 信号 ,也可以用 共享内存 ;
e. 稳定性,灵活性,体现之一:worker进程 一旦挂掉,那么master进程会立即fork()一个新的worker进程投入工作中去;
-
调整worker进程数量
a. worker进程几个合适呢?公认的做法: 多核计算机,就让每个worker运行在一个单独的内核上,最大限度减少CPU进程切换成本,提高系统运行效率;
b.物理机:4核(4个processors);
c. 工作站:2个物理cpu ,蓝色的一个cpu,红色的一个cpu
d. 每个物理cpu里边内核数量,是4个;core1 --core4
e. 每个core里边有两个逻辑处理器(超线程技术/siblings)
f. 16个processors(最细小的单位,也就是平时大家说的处理器个数)
第二章
一:nginx源码总述
-
nginx源码查看工具
visual studio,source Insight,visual stuido Code.
采用 Visual Studio Code来阅读nginx源码
Visual Studio Code:微软公司开发的一个跨平台的轻量级的编辑器(不要混淆vs2017:IDE集成开发环境,以编译器);
Visual Studio Code在其中可以安装很多扩展模块;
1.30.0版本,免费的,多平台;
官方地址:https://code.visualstudio.com
https://code.visualstudio.com/download
二:终端和进程的关系
-
pts(虚拟终端),每连接一个虚拟终端到乌班图linux操作系统,就会出现 一个bash进程(bash = shell = 命令行解释器)
-
每个进程还属于一个进程组:一个或者多个进程的集合,每个进程组有一个唯一的进程组ID,可以调用系统 函数来创建进程组、加入进程组
-
“会话”(session):是一个或者多个进程组的集合
一般,只要不进行特殊的系统函数调用,一个bash(shell)上边运行的所有程序都属于一个会话,而这个会话有一个session leader;
那么这个bash(shell)通常就是session leader; 你可以调用系统功函数创建新的session。
-
ps -eo pid,ppid,sid,tty,pgrp,comm | grep -E ‘bash|PID|nginx’
-
如果我 xshell终端要断开的话,系统就会发送SIGHUP信号(终端断开信号),给session leader,也就是这个bash进程, bash进程收到 SIGHUP信号后,bash会把这个信号发送给session里边的所有进程,收到这个SIGHUP信号的进程的缺省动作就是退出;
-
strace工具的使用
linux下调试分析诊断工具:可以跟踪程序执行时进程的系统调用以及所收到的信号;
跟踪nginx进程 : **sudo strace -e trace=signal -p 1359**
-
终端关闭时如何让进程不退出
a)nginx进程拦截(忽略)SIGHUP(nginx收到这个信号并告诉操作系统,我不想死,请不要把我杀死)信号,是不是可以;
b)nginx进程和bash进程不再同一个seeion里
c)孤儿进程
d)setsid函数不适合进程组组长调用;
e)setsid命令:启动一个进程,而且能够使启动的进程在一个新的session中,这样的话,终端关闭时该进程就不会退出
f)setsid ./nginx
g)nohup(no hang up不要挂断),用该命令启动的进程跟上边忽略掉SIGHUP信号,道理相同
h)该命令会把屏幕输出重新定位到当前目录的nohup.out
三: 信号概念
sudo find / -name “signal.h” | xargs grep -in “SIGHUP”
-
信号如何产生:
a)某个进程发送给另外一个进程或者发送给自己;
b)由内核(操作系统)发送给某个进程
b.1)通过在键盘输入命令ctrl+c[中断信号],kill命令
b.2)内存访问异常,除数为0等等,硬件都会检测到并且通知内核;
-
信号处理的相关动作:
a) 当某个信号出现时,我们可以按三种方式之一进行处理,我们称之为信号的处理或者与信号相关的动作;
(1)执行系统默认动作 ,绝大多数信号的默认动作是杀死你这个进程; (2)忽略此信号(但是不包括SIGKILL和SIGSTOP)
(3)捕捉该信号:我写个处理函数,信号来的时候,我就用处理函数来处理;(但是不包括SIGKILL和SIGSTOP)
-
为什么要区分用户态,内核态;
(1)一般情况下,程序都运行在用户态状态,权限小,不至于危害到系统其它部分;当你干一些危险的事情的时候,系统给你提供接口,让你去干;
(2)这些接口是系统提供给你的,那么这些接口也是操作系统统一管理的;
(3) 资源是有限的, 如果大家都来访问这些资源,如果不加以管理,一个是访问冲突,一个是被访问的资源如果耗尽,那系统还可能崩溃;
-
那么什么时候从用户态切换到内核态去呢?
a)系统调用,比如调用malloc();
b)异常事件,比如来了个信号;
c)外围设备中断:
-
可重入函数: 在信号处理程序中保证调用安全的函数,这些函数是可重入的并被称为异步信号安全的
在写信号处理函数的时候,要注意的事项:
a)在信号处理函数中,尽量使用简单的语句做简单的事情,尽量不要调用系统函数以免引起麻烦 b)如果必须要在信号处理函数中调用一些系统函数,那么要保证在信号处理函数中调用的 系统函数一定要是可重入的 c)如果必须要在信号处理函数中调用那些可能修改errno值的可重入的系统函数,那么 就得事先备份errno值,从信号处理 函数返回之前,将errno值恢复;
四: 信号相关函数
信号相关函数
a)sigemtpyset():把信号集中的所有信号都清0,表示这60多个信号都没有来; b)sigfillset();把信号集中的所有信号都设置为1,跟sigemptyset()正好相反; c)用sigaddset(),sigdelset()就可以往信号集中增加信号,或者从信号集中删除特定信号; d)sigprocmask,sigmember
五:守护进程
- 守护进程基本概念:
基本特点:
a)生存期长,一般是操作系统启动的时候他就启动,操作系统关闭的时候他才关闭 b)守护进程跟终端无关联,也就是说他们没有控制终端,控制终端退出,也不会导致守护进程退出 c)守护进程是在后台运行,不会占着终端,终端可以执行其他命令
-
共同点总结:
a) 大多数守护进程都是以超级 用户特权运行的;
b) 守护进程没有控制终端,TT这列显示?
c) 内核守护进程以无控制终端方式启动
d) 普通守护进程可能是守护进程调用了setsid的结果(无控制端)
第三章
一: 目录介绍
-
主目录名nginx
-
_include目录:专门存放各种头文件; 如果分散:#include “sfaf/sdafas/safd.h”
-
app目录:放主应用程序.c(main()函数所在的文件)以及一些比较核心的文件;
link_obj:临时目录:会存放临时的.o文件,这个目录不手工创建,后续用makefile脚本来创建
dep:临时目录,会存放临时的.d开头的依赖文件,依赖文件能够告知系统哪些相关的文件发生变化,需要重新编译,后续用makefile脚本来创建
nginx.c:主文件,main()入口函数就放到这里;
ngx_conf.c ,普通的源码文件,跟主文件关系密切,又不值得单独放在 一个目录;
-
misc目录:专门存放各种杂合性的不好归类的1到多个.c文件;暂时为空
-
net目录:专门存放和网络处理相关的1到多个.c文件,暂时为空
-
proc目录:专门存放和进程处理相关的1到多个.c文件,暂时为空
-
signal目录:专门用于存放和信号处理相关的1到多个.c文件;
二: 内存泄漏
内存泄漏的检查工具:
-
Valgrind:帮助程序员寻找程序里的bug和改进程序性能的工具集。擅长是发现内存的管理问题;
-
里边有若干工具,其中最重要的是Memcheck(内存检查)工具,用于检查内存的泄漏;
-
memcheck的基本功能,能发现如下的问题;
a)使用未初始化的内存
b)使用已经释放了的内存
c)使用超过malloc()分配的内存
d)对堆栈的非法访问
e)申请的内存是否有释放****
f)malloc/free,new/delete申请和释放内存的匹配
g)memcpy()内存拷贝函数中源指针和目标指针重叠;
检查规范:
内存泄漏检查示范
//格式:
-
valgrind --tool=memcheck 一些开关 可执行文件名
-
–tool=memcheck :使用valgrind工具集中的memcheck工具
-
–leak-check=full : 指的是完全full检查内存泄漏
-
–show-reachable=yes :是显示内存泄漏的地点
-
–trace-children = yes :是否跟入子进程
-
–log-file=log.txt:讲调试信息输出到log.txt,不输出到屏幕
-
最终用的命令:
- valgrind --tool=memcheck --leak-check=full --show-reachable=yes ./nginx
-
查看内存泄漏的三个地方:
-
(1) 9 allocs, 8 frees 差值是1,就没泄漏,超过1就有泄漏
-
(2)中间诸如: by 0x401363: CConfig::Load(char const*) (ngx_c_conf.cxx:77)和我们自己的源代码有关的提示,就要注意;
-
(3)LEAK SUMMARY:definitely lost: 1,100 bytes in 2 blocks
-
-
sigsuspend()函数讲解
-
a)根据给定的参数设置新的mask 并 阻塞当前进程【因为是个空集,所以不阻塞任何信号】
-
b)此时,一旦收到信号,便恢复原先的信号屏蔽【我们原来的mask在上边设置的,阻塞了多达10个信号,从而保证我下边的执行流程不会再次被其他信号截断】
-
c)调用该信号对应的信号处理函数
-
d)信号处理函数返回后,sigsuspend返回,使程序流程继续往下走
-
-
printf()函数不加\\n无法及时输出的解释:
- 存在行行缓冲区
- fflush(stdout) //手动刷新缓存区
- setvbuf(stdout,NULL,_IONBF,0); //这个函数. 直接将printf缓冲区禁止
-
write函数
多进程写日志文件:
- 多个进程写一个文件,可能会出现数据覆盖,混乱等情况
- O_APPEND这个标记能够保证多个进程操作同一个文件时不会相互覆盖
- 内核wirte()写入时是原子操作
- 父进程fork()子进程是亲缘关系。是会共享文件表项
掉电导致write()的数据丢失破解法:
-
直接I/O:直接访问物理磁盘:(O_DIRECT) 绕过内核缓冲区。用posix_memalign
-
open文件时用O_SYNC选项【数据直接同步到磁盘,务必大块大块写】
-
缓存同步:尽量保证缓存数据和写道磁盘上的数据一致
-
sync(void):将所有修改过的块缓冲区排入写队列;然后返回,并不等待实际写磁盘操作结束,数据是否写入磁盘并没有保证;
-
fsync(int fd):将fd对应的文件的块缓冲区立即写入磁盘,并等待实际写磁盘操作结束返回;
-
fdatasync(int fd):类似于fsync,但只影响文件的数据部分。而fsync不一样,fsync除数据外,还会同步更新文件属性;
-
第四章
5_5 epoll知识的讲解
gdb调试:
gdb缺省调试主进程,但是gdb 7.0以上版本可以调试子进程【我们需要调试子进程,因为干活的是worker process是子进程】
命令 行下 :gdb -v看版本
- 为了让gdb支持多进程调试,要设置一下 follow-fork-mode选项 ,这是个调试多进程的开关;
取值可以是parent[主] /child[子] ,我们这里需要设置成child才能调试worker process子进程;
- 查看follow-fork-mode: 在gdb下输入show follow-fork-mode
输入 set follow-fork-mode child
- 还有个选项 detach-on-fork, 取值为 on/off,默认是on【表示只调试父进程或者子进程其中的一个】
调试是父进程还是子进程,由上边的 follow-fork-mode选项说了算;
如果detach-on-fork = off,就表示父子都可以调试,调试一个进程时,另外一个进程会被暂停;
查看 show detach-on-fork
输入set show detach-on-fork off ,如果设置为off并且 follow-fork-mode选项为parent,那么fork()后的子进程并不运行,而是处于暂停状态;
-
run 运行程序运行到断点;
-
print。。…打印变量值。这些调试手段,大家自己百度学习;
-
c命令,继续运行
服务器收到的攻击:
-
-
-
syn flood
* DDOS攻击【syn flood攻击也是DDOS攻击的一种】甚至防火墙等设备都可能防不住,甚至得从数据路由器想办法命令信息查看: top -p 3645 [进程号]:显示进程占用的内存和cpu百分比,用q可以退出 cat /proc/3645/status ---------VmRSS: 7700 kB setsockopt(isock, SOL_SOCKET, SO_REUSEPORT,(const void *) &reuseport, sizeof(int))== -1 (reuseport【复用端口】,是一种套接字的复用机制,允许将多个套接字bind到同一个ip地址/端口上) 运行流程: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7V4RcKDi-1667576632522)(C:\\Users\\19437\\Desktop\\nginx学习笔记\\image-20221024115308354.png)] 性能优化: a)一个worker进程运行在一个核上;为什么能够提高性能呢? cpu:缓存;cpu缓存命中率问题;把进程固定到cpu核上,可以大大增加cpu缓存命中率,从而提高程序运行效率; worker_cpu_affinity【cpu亲和性】,就是为了把worker进程固定的绑到某个cpu核上; ngx_set_cpu_affinity,ngx_setaffinity; b)提升进程优先级,这样这个进程就有机会被分配到更多的cpu时间(时间片【上下文切换】),得到执行的机会就会增多; setpriority(); 干活时进程 处于R状态,没有连接连入时,进程处于S pidstat - w - p 3660 1 看某个进程的上下文切换次数[切换频率越低越好] cswch/s:主动切换/秒:你还有运行时间,但是因为你等东西,你把自己挂起来了,让出了自己时间片。 nvcswch/s:被动切换/秒:时间片耗尽了,你必须要切出去; TCP / IP协议的配置选项: (3.1)绑定cpu、提升进程优先级 (3.2)TCP / IP协议的配置选项 (3.3)TCP/IP协议额外注意的一些算法、概念等 a)滑动窗口的概念 b)Nagle算法的概念 c)Cork算法 d)Keep - Alive机制 e)SO_LINGER选项 配置最大允许打开的文件句柄数: cat /proc/sys/fs/file-max :查看操作系统可以使用的最大句柄数 cat /proc/sys/fs/file-nr :查看当前已经分配的,分配了没使用的,文件句柄最大数目 架构师之路
己挂起来了,让出了自己时间片。
nvcswch/s:被动切换/秒:时间片耗尽了,你必须要切出去;
TCP / IP协议的配置选项:
(3.1)绑定cpu、提升进程优先级
(3.2)TCP / IP协议的配置选项
(3.3)TCP/IP协议额外注意的一些算法、概念等
a)滑动窗口的概念
b)Nagle算法的概念
c)Cork算法
d)Keep - Alive机制
e)SO_LINGER选项
配置最大允许打开的文件句柄数:
cat /proc/sys/fs/file-max :查看操作系统可以使用的最大句柄数
cat /proc/sys/fs/file-nr :查看当前已经分配的,分配了没使用的,文件句柄最大数目
架构师之路
//继续学分布式系统架构相关的知识和课程:zookeeper,dubbo,docker容器,hadoop,Redis,memcached ,mysql;
以上是关于nginx学习笔记的主要内容,如果未能解决你的问题,请参考以下文章