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:
- 启动服务器,通过 /sbin/nginx 来启动 master 进程,master 进程通过读取配置文件来进行子线程的创建启动。
- 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);

- java 本身并不具备 IO 读写能力,所以必须先通过程序将用户态转为内核态,再通过内核态的读取将数据拷贝到内核缓存中,该过程中 cpu 无需参与,只需要使用 DMA 完成即可。
需要了解的是:DMA 是完成 IO 设备和内存数据交互的硬件单元。
- 从内核态切换回用户态,然后将内核缓存中的数据加载到用户态,也就是写入到上述代码的字节数组中,期间需要 CPU 参与拷贝
- 调用 write 方法,将数据写入到 socket 缓存中,CPU 需要参与拷贝
- Java 也不具备向网卡写入数据的能力,所以需要进行用户态到内核态的切换,调用操作系统的写操作,通过 DMA 完成 socket 缓存数据拷贝到网卡的操作,不需要使用 CPU
- 用户态与内核态的切换发生了 3 次,这个操作比较重量级,数据拷贝了共 4 次
DirectByteBuffer 优化
ByteBuffer.allocateDirect(10): DirectByteBuffer 使用的是操作系统内存,java和操作系统都可以直接访问

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

-
java 调用 transferTo 方法后,要从 java 程序的用户态切换至内核态,使用 DMA 将数据读入内核缓冲区,不会使用 cpu
-
数据从内核缓冲区传输到 socket 缓冲区,cpu 会参与拷贝
-
最后使用 DMA 将 socket 缓冲区的数据写入网卡,不会使用 cpu
显然优势出来了:
- 只进行了一次内核态用户态的转换,因为并不涉及内核态数据拷贝到用户态,而是直接拷贝到 socket 缓存。
- 不需要将数据拷贝到 JVM 内存中,这就是零拷贝,而不是不拷贝。
server
- 一个 server 对应一个主机
- 一个 nginx 可以同时配置多个主机,这里我是存疑的,对于一个监听的端口是否可以相同,nginx是否可以通过server_name来区分
location
用于匹配 URI,对于 URL 和 URI 建议读这篇博文。
location 的资源匹配有精准匹配、普通匹配,正则匹配。执行顺序也是依次的。
不同匹配确定的资源位置不一样:
- 精准匹配:
location =/text.html,通过内部的 root 与text.html拼接组成最终位置,结束 - 普通匹配:
location /,通过nginx服务所在目录+内部root+最长匹配结果,记忆该结果 - 正则匹配:
location ~ image,可以是直接通过正则,也可以是普通匹配记忆的结果作为输入,通过内部root+内部index成为最终结果,第一次匹配成功就退出

存疑
- worker 子进程是自己接受请求还是 master 进程统一接收并分配
- 对于一个监听的端口是否可以相同,nginx是否可以通过server_name来区分
暑假内我将尽可能日更,望诸君督促,若当日未更,次日双更。
浙公网安备 33010602011771号