Nginx(一)——初学

安装之类的我就略过了,本文章是基于尚硅谷nginx教学写的笔记

nginx 的目录结构

此处借鉴这位大佬,他的文章很详细,很推荐。

[root@localhost ~]# tree /usr/local/nginx
/usr/local/nginx
├── client_body_temp                 # POST 大文件暂存目录
├── conf                             # Nginx所有配置文件的目录
│   ├── fastcgi.conf                 # fastcgi相关参数的配置文件
│   ├── fastcgi.conf.default         # fastcgi.conf的原始备份文件
│   ├── fastcgi_params               # fastcgi的参数文件
│   ├── fastcgi_params.default       
│   ├── koi-utf
│   ├── koi-win
│   ├── mime.types                   # 媒体类型
│   ├── mime.types.default
│   ├── nginx.conf        #这是Nginx默认的主配置文件,日常使用和修改的文件
│   ├── nginx.conf.default
│   ├── scgi_params                  # scgi相关参数文件
│   ├── scgi_params.default  
│   ├── uwsgi_params                 # uwsgi相关参数文件
│   ├── uwsgi_params.default
│   └── win-utf
├── fastcgi_temp                     # fastcgi临时数据目录
├── html                             # Nginx默认站点目录
│   ├── 50x.html   # 错误页面优雅替代显示文件,例如出现502错误时会调用此页面
│   └── index.html                   # 默认的首页文件
├── logs                             # Nginx日志目录
│   ├── access.log                   # 访问日志文件
│   ├── error.log                    # 错误日志文件
│   └── nginx.pid    # pid文件,Nginx进程启动后,会把所有进程的ID号写到此文件
├── proxy_temp                       # 临时目录
├── sbin                             # Nginx 可执行文件目录
│   └── nginx                        # Nginx 二进制可执行程序
├── scgi_temp                        # 临时目录
└── uwsgi_temp                       # 临时目录

大致运行流程


Nginx:

  1. 启动服务器,通过 /sbin/nginx 来启动 master 进程,master 进程通过读取配置文件来进行子线程的创建启动。
  2. worker 线程开始等待请求,接收到请求后,解析请求,然后通过已加载的配置去指定目录下寻找资源,并作出响应(这里我是存疑的,因为我看过有的文章说,是通过 master 进程接受请求并分配,希望有大佬看到后,能够指正)

master 进程:

  • 监控worker进程的运行状态
  • 协调各 worker 进程
  • 当worker进程退出后(异常情况下),会自动重新启动新的worker进程。

worker 进程:主要处理基本的网络事件,多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。

配置文件

worker_processes  1; #允许进程数量,建议设置为cpu核心数或者auto自动检测,注意Windows服务器上虽然可以启动多个processes,但是实际只会用其中一个

events {
    #单个进程最大连接数(最大连接数=连接数*进程数)
    #根据硬件调整,和前面工作进程配合起来用,尽量大,但是别把cpu跑到100%就行。
    worker_connections  1024;
}


http {
    #文件扩展名与文件类型映射表(是conf目录下的一个文件)
    include       mime.types;
    #默认文件类型,如果mime.types预先定义的类型没匹配上,默认使用二进制流的方式传输
    default_type  application/octet-stream;

    #sendfile指令指定nginx是否调用sendfile 函数(zero copy 方式)来输出文件,对于普通应用,必须设为on。如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络IO处理速度。
    sendfile        on;
    
     #长连接超时时间,单位是秒
    keepalive_timeout  65;

 #虚拟主机的配置
    server {
    #监听端口
        listen       80;
        #域名,可以有多个,用空格隔开
        server_name  localhost;

	#配置根目录以及默认页面
        location / {
            root   html;
            index  index.html index.htm;
        }

	#出错页面配置
        error_page   500 502 503 504  /50x.html;
        #/50x.html文件所在位置
        location = /50x.html {
            root   html;
        }
        
    }

}

sendfile on(讲零拷贝问题)

此处借鉴黑马 netty 讲义,具体哪节课我不知道了,也不想去找了,有兴趣的可以去看看这个课程,绝对值得。

首先先讲一些 java 中常见的拷贝方式

传统拷贝
File f = new File("helloword/data.txt");
RandomAccessFile file = new RandomAccessFile(file, "r");
// 将文件内容加载到用户态
byte[] buf = new byte[(int)f.length()];
file.read(buf);
// 将用户缓冲区内容加载到socket中
Socket socket = ...;
socket.getOutputStream().write(buf);

image

  1. java 本身并不具备 IO 读写能力,所以必须先通过程序将用户态转为内核态,再通过内核态的读取将数据拷贝到内核缓存中,该过程中 cpu 无需参与,只需要使用 DMA 完成即可。

需要了解的是:DMA 是完成 IO 设备和内存数据交互的硬件单元。

  1. 从内核态切换回用户态,然后将内核缓存中的数据加载到用户态,也就是写入到上述代码的字节数组中,期间需要 CPU 参与拷贝
  2. 调用 write 方法,将数据写入到 socket 缓存中,CPU 需要参与拷贝
  3. Java 也不具备向网卡写入数据的能力,所以需要进行用户态到内核态的切换,调用操作系统的写操作,通过 DMA 完成 socket 缓存数据拷贝到网卡的操作,不需要使用 CPU
  • 用户态与内核态的切换发生了 3 次,这个操作比较重量级,数据拷贝了共 4 次
DirectByteBuffer 优化

ByteBuffer.allocateDirect(10): DirectByteBuffer 使用的是操作系统内存,java和操作系统都可以直接访问

image

大部分操作与上述一致,简述区别:

  1. 并不会减少用户态内核态转换次数,因为该直接内存只是都可以访问,仍然需要转换成内核态将数据拷贝到直接内存,仍然需要内核态转换成用户态,将数据拷贝到 socket 缓存。
  2. 复制次数减一,在上述第二步内核缓存数据拷贝到用户态的拷贝过程被优化了
  3. java 是使用 DirectByteBuf 将堆外内存映射到 jvm 内存中来直接访问使用
transferTo 实现零拷贝

image

  1. java 调用 transferTo 方法后,要从 java 程序的用户态切换至内核态,使用 DMA 将数据读入内核缓冲区,不会使用 cpu

  2. 数据从内核缓冲区传输到 socket 缓冲区,cpu 会参与拷贝

  3. 最后使用 DMA 将 socket 缓冲区的数据写入网卡,不会使用 cpu

显然优势出来了:

  • 只进行了一次内核态用户态的转换,因为并不涉及内核态数据拷贝到用户态,而是直接拷贝到 socket 缓存。
  • 不需要将数据拷贝到 JVM 内存中,这就是零拷贝,而不是不拷贝。

server

  • 一个 server 对应一个主机
  • 一个 nginx 可以同时配置多个主机,这里我是存疑的,对于一个监听的端口是否可以相同,nginx是否可以通过server_name来区分

location

用于匹配 URI,对于 URL 和 URI 建议读这篇博文
location 的资源匹配有精准匹配、普通匹配,正则匹配。执行顺序也是依次的。
不同匹配确定的资源位置不一样:

  1. 精准匹配:location =/text.html,通过内部的 root 与text.html拼接组成最终位置,结束
  2. 普通匹配: location / ,通过nginx服务所在目录+内部root+最长匹配结果,记忆该结果
  3. 正则匹配:location ~ image,可以是直接通过正则,也可以是普通匹配记忆的结果作为输入,通过内部root+内部index成为最终结果,第一次匹配成功就退出

image

存疑

  1. worker 子进程是自己接受请求还是 master 进程统一接收并分配
  2. 对于一个监听的端口是否可以相同,nginx是否可以通过server_name来区分

暑假内我将尽可能日更,望诸君督促,若当日未更,次日双更。

posted on 2022-07-06 22:16  wtgiiwtg  阅读(26)  评论(0)    收藏  举报