nginx学习笔记

Posted ~千里之行,始于足下~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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是父进程
    
  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. 终端关闭时如何让进程不退出

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

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

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

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

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

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

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

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

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

      c)外围设备中断:

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

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

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

      四: 信号相关函数

      信号相关函数

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

      五:守护进程

      1. 守护进程基本概念:

      ​ 基本特点:

        	a)生存期长,一般是操作系统启动的时候他就启动,操作系统关闭的时候他才关闭
      
        	 b)守护进程跟终端无关联,也就是说他们没有控制终端,控制终端退出,也不会导致守护进程退出
      
        	c)守护进程是在后台运行,不会占着终端,终端可以执行其他命令
      
      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  ,普通的源码文件,跟主文件关系密切,又不值得单独放在 一个目录;
        
        • 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  :查看当前已经分配的,分配了没使用的,文件句柄最大数目
    
       架构师之路
    

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

       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学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

Nginx 学习笔记

Nginx学习笔记

Nginx学习笔记

nginx学习笔记五(nginx的事件模块定义)

Nginx学习笔记

nginx学习笔记