• nginx学习笔记


    第一章

    一: nginx简介

    1. nginx(2002年开发,2004年10才出现第一个版本0.1.0):web服务器,市场份额,排在第二位,Apache(1995)第一位;

    2. web服务器,反向代理负载均衡,邮件代理;运行时需要的系统资源比较少,所以经常被称呼为轻量级服务器;

    3. 是一个俄罗斯人(Igor Sysoev),C语言(不是c++)开发的,并且开源了;

    4. nginx号称并发处理百万级别的TCP连接,非常稳定,热部署(运行的时候能升级),高度模块化设计,自由许可证。

    5. 很多人开发自己的模块来增强nginx,第三方业务模块(c++开发); OpenResty;

    6. linux epoll技术; windows IOCP

    二:为什么选择nginx

    1. 单机10万并发,而且同时能够保持高效的服务,epoll这种高并发技术好处就是:高并发只是占用更多内存就能做到;

    2. 内存池,进程池,线程池,事件驱动等等;

    3. 学习研究大师级的人写的代码,是一个程序开发人员能够急速进步的最佳途径;

    三: 安装nginx,搭建web服务器

    3.1安装前提

    1. epoll,linux 内核版本为2.6或者以上;

    2. gcc编译器,g++编译器

    3. pcre库:函数库;支持解析正则表达式;

    4. zlib库:压缩解压缩功能

    5. openssl库:ssl功能相关库,用于网站加密通讯

    3.2nginx源码下载以及目录结构简单认识

    1. nginx官网 http://www.nginx.org

    2. nginx的几种版本

    ​ a. mainline版本:版本号中间数字一般为奇数。更新快,一个月内就会发布一个新版本,最新功能,bug修复等,稳定性差一点;

    ​ b. stable版本:稳定版,版本号中间数字一般为偶数。经过了长时间的测试,比较稳定,商业化环境中用这种版本;这种版本发布周期比较长,几个月;

    ​ c. Legacy版本:遗产,遗留版本,以往的老版本;

    1. 安装,现在有这种二进制版本:通过命令行直接安装;

    2. 灵活:要通过编译 nginx源码手段才能把第三方模块弄进来;

    3. 目录介绍

    ​ 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命令时用到

    1. 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的启动和简单使用

    1. 启动: sudo ./nginx

    2. 乌班图:192.168.1.128

    ​ //百度:“服务器程序端口号”

    ​ //百度:“监听端口号”

    四: nginx的整体结构

    1. master进程和worker进程概览(父子关系)

    2. 启动nginx,看到了一个master进程,一个worker进程

    3. ps -ef命令

      第一列:UID,进程所属的用户id

       第二列:进程ID(PID),用来唯一的标识一个进程
      
       第三列:父进程ID(PPID)。 fork(),worker进程是被master进程通过fork()创建出来的-worker进程是master进程的子进程,master是父进程
      
      • 1
      • 2
      • 3
    4. nginx进程模型

      a. 1个master进程,1到多个worker进程 这种工作机制来对外服务的;这种工作机制保证了 nginx能够稳定、灵活的运行;

      b. master进程责任:监控进程,不处理具体业务,专门用来管理和监控worker进程;master,角色是监工,比如清闲;

      c. worker进程:用来干主要的活的,(和用户交互);

      d. master进程和worker进程之间要通讯,可以用 信号 ,也可以用 共享内存 ;

      e. 稳定性,灵活性,体现之一:worker进程 一旦挂掉,那么master进程会立即fork()一个新的worker进程投入工作中去;

    5. 调整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源码总述

      1. 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

      二:终端和进程的关系

      1. pts(虚拟终端),每连接一个虚拟终端到乌班图linux操作系统,就会出现 一个bash进程(bash = shell = 命令行解释器)

      2. 每个进程还属于一个进程组:一个或者多个进程的集合,每个进程组有一个唯一的进程组ID,可以调用系统 函数来创建进程组、加入进程组

      3. “会话”(session):是一个或者多个进程组的集合

        一般,只要不进行特殊的系统函数调用,一个bash(shell)上边运行的所有程序都属于一个会话,而这个会话有一个session leader;

      ​ 那么这个bash(shell)通常就是session leader; 你可以调用系统功函数创建新的session。

      1. ps -eo pid,ppid,sid,tty,pgrp,comm | grep -E ‘bash|PID|nginx’

      2. 如果我 xshell终端要断开的话,系统就会发送SIGHUP信号(终端断开信号),给session leader,也就是这个bash进程, bash进程收到 SIGHUP信号后,bash会把这个信号发送给session里边的所有进程,收到这个SIGHUP信号的进程的缺省动作就是退出;

      3. strace工具的使用

      ​ linux下调试分析诊断工具:可以跟踪程序执行时进程的系统调用以及所收到的信号;

       		跟踪nginx进程  :  **sudo strace -e trace=signal -p 1359**
      
      • 1
      1. 终端关闭时如何让进程不退出

        ​ 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”

      1. 信号如何产生:

        a)某个进程发送给另外一个进程或者发送给自己;

        b)由内核(操作系统)发送给某个进程

        b.1)通过在键盘输入命令ctrl+c[中断信号],kill命令

        b.2)内存访问异常,除数为0等等,硬件都会检测到并且通知内核;

      2. 信号处理的相关动作:

        a) 当某个信号出现时,我们可以按三种方式之一进行处理,我们称之为信号的处理或者与信号相关的动作;

          (1)执行系统默认动作 ,绝大多数信号的默认动作是杀死你这个进程;
        
          (2)忽略此信号(但是不包括SIGKILL和SIGSTOP)
        
        • 1
        • 2
        • 3

        ​ (3)捕捉该信号:我写个处理函数,信号来的时候,我就用处理函数来处理;(但是不包括SIGKILL和SIGSTOP)

      3. 为什么要区分用户态,内核态;

        (1)一般情况下,程序都运行在用户态状态,权限小,不至于危害到系统其它部分;当你干一些危险的事情的时候,系统给你提供接口,让你去干;

        (2)这些接口是系统提供给你的,那么这些接口也是操作系统统一管理的;

        (3) 资源是有限的, 如果大家都来访问这些资源,如果不加以管理,一个是访问冲突,一个是被访问的资源如果耗尽,那系统还可能崩溃;

      4. 那么什么时候从用户态切换到内核态去呢?

        a)系统调用,比如调用malloc();

        b)异常事件,比如来了个信号;

        c)外围设备中断:

      5. 可重入函数: 在信号处理程序中保证调用安全的函数,这些函数是可重入的并被称为异步信号安全的

        ​ 在写信号处理函数的时候,要注意的事项:

          	a)在信号处理函数中,尽量使用简单的语句做简单的事情,尽量不要调用系统函数以免引起麻烦
        
          	b)如果必须要在信号处理函数中调用一些系统函数,那么要保证在信号处理函数中调用的 系统函数一定要是可重入的
        
          	c)如果必须要在信号处理函数中调用那些可能修改errno值的可重入的系统函数,那么 就得事先备份errno值,从信号处理			函数返回之前,将errno值恢复;
        
        • 1
        • 2
        • 3
        • 4
        • 5

        四: 信号相关函数

        信号相关函数

          a)sigemtpyset():把信号集中的所有信号都清0,表示这60多个信号都没有来;
        
          b)sigfillset();把信号集中的所有信号都设置为1,跟sigemptyset()正好相反;
        
          c)用sigaddset(),sigdelset()就可以往信号集中增加信号,或者从信号集中删除特定信号;
        
          d)sigprocmask,sigmember
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7

        五:守护进程

        1. 守护进程基本概念:

        ​ 基本特点:

          	a)生存期长,一般是操作系统启动的时候他就启动,操作系统关闭的时候他才关闭
        
          	 b)守护进程跟终端无关联,也就是说他们没有控制终端,控制终端退出,也不会导致守护进程退出
        
          	c)守护进程是在后台运行,不会占着终端,终端可以执行其他命令
        
        • 1
        • 2
        • 3
        • 4
        • 5
        1. 共同点总结:

          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  ,普通的源码文件,跟主文件关系密切,又不值得单独放在 一个目录;
          
          • 1
          • misc目录:专门存放各种杂合性的不好归类的1到多个.c文件;暂时为空

          • net目录:专门存放和网络处理相关的1到多个.c文件,暂时为空

          • proc目录:专门存放和进程处理相关的1到多个.c文件,暂时为空

          • signal目录:专门用于存放和信号处理相关的1到多个.c文件;

          二: 内存泄漏

          内存泄漏的检查工具:

          1. Valgrind:帮助程序员寻找程序里的bug和改进程序性能的工具集。擅长是发现内存的管理问题;

          2. 里边有若干工具,其中最重要的是Memcheck(内存检查)工具,用于检查内存的泄漏;

          3. 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

          1. sigsuspend()函数讲解

            • a)根据给定的参数设置新的mask 并 阻塞当前进程【因为是个空集,所以不阻塞任何信号】

            • b)此时,一旦收到信号,便恢复原先的信号屏蔽【我们原来的mask在上边设置的,阻塞了多达10个信号,从而保证我下边的执行流程不会再次被其他信号截断】

            • c)调用该信号对应的信号处理函数

            • d)信号处理函数返回后,sigsuspend返回,使程序流程继续往下走

          2. printf()函数不加\n无法及时输出的解释:

            • 存在行行缓冲区
            • fflush(stdout) //手动刷新缓存区
            • setvbuf(stdout,NULL,_IONBF,0); //这个函数. 直接将printf缓冲区禁止
          3. 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知识的讲解

          epoll源码

          epoll事件解释

          惊群

          信号量和条件变量 唤醒丢失事件

          top

          文件描述符

          内存池

          类型安全

          行缓冲

          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  :查看当前已经分配的,分配了没使用的,文件句柄最大数目
      
         架构师之路
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61

    己挂起来了,让出了自己时间片。

           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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
  • 相关阅读:
    vue2 在循环里,给字体加上随机颜色并加上随机图标且少重复
    iPhone 的健康数据采用的是 FHIR 传输格式
    k8s-master 高可用
    如何手写动态代理实现数据库事务
    怎么关闭管理员权限?
    “深入探究SpringMVC的工作原理与入门实践“
    MATLAB学习笔记(系统学习)
    【云原生】设备入云之FlexManager数据通道的具体部署
    C#程序全局异常处理—WPF和Web API两种模式
    二叉树的一些基本操作
  • 原文地址:https://blog.csdn.net/qq_45892987/article/details/127698422