nginx那些事儿
本文为我学习nginx时的笔记与心得,如有错误或者不当的地方,还望不吝指出
1 基本概念
1.1 正向代理和反向代理
正向代理:
一般来说,我们说的代理技术就是指正向代理技术。
使用正向代理技术一般用来访问我们无法访问的服务器。正向代理服务器介于用户和目标服务器之间,比如用户A想去访问目标服务器B,但是各种原因无法直接访问,这时就可以通过使用正向代理服务器C,用户A向代理服务器C发送一个请求并指定目标服务器B,代理服务器会将请求转发给B并将获取的结果返回给用户A。
使用正向代理,往往需要客户去进行相关的配置。典型的例子是,为防火墙内的局域网用户提供访问访问Internet的途径,有些公司内部的局域网本身是无法访问互联网的,但是可以通过设置代理实现访问。还有另一个典型的例子就是“”。
反向代理
与正向代理技术相对的就是反向代理技术。
使用反向代理时,客户端的用户不需要任何设置,而且用户始终认为他访问的代理服务器B就是目标服务器,用户将请求发送给反向代理服务器,由反向代理服务器判断应该将请求转交给哪台目标服务器,然后将结果返回给用户。
对比
通过对比,我们可以有一个更清晰的认识。
a.谁在“墙”里边
对使用正向代理的环境来说,用户位于墙里边,墙限制了用户与外界沟通的能力,这时,我们使用正向代理技术来赋予用户访问外界环境的能力。
对使用反向代理的环境来说,目标服务器位于墙里边 ,这面墙有个门,但只向反向代理服务器开放。本来任何用户都无法访问目标服务器,但反向代理服务器的出现使之变成了可能,用户直接访问反向代理服务器即可。
b.用户访问的是谁
对正向代理来说,用户访问的就是目标服务器。比如使用代理的局域网用户访问IPA时,他实际上是就是向IPA发起的请求,只是这个请求实际发给了正向代理服务器,然后由代理服务器做了个转发,用户需要告诉代理服务器请求的内容以及请求的目标。
对于反向代理来说,用户访问的是反向代理服务器。先拿nginx举个例子,当我们使用nginx做负载均衡时,一台nginx后边是多台目标服务器,比如nginx的地址为IPA,目标服务器的地址为IPB1,IPB2,IPB3。用户实际请求的是IPA,然后由反向代理服务器来决定将请求转发给哪一台目标服务器.
c.用户是否需要配置
对于正向代理来说,用户需要进行相应的配置。
对于反向代理来说,用户不需要配置,而是需要对反向代理服务器配置与之关联的目标服务器。
1.2 nginx
是什么?
nginx是一个高性能的web和反向代理服务器,同时也支持IMAP/POP3/SMTP 代理。
做什么?
nginx主要就是用来做反向代理及负载均衡。
2 nginx
2.1 安装
一般情况,使用如下命令直接安装即可
sudo apt-get install nginx
安装之后启动nginx进行测试,使用下面两种命令都可以启动
sudo /etc/init.d/nginx start
或者
sudo service nginx start
启动后在浏览器地址栏输入:http://localhost 进行确认,如果进入nginx的欢迎界面,说明nginx已安装并且启动成功
2.2 命令
命令 | 功能 |
---|---|
sudo service nginx start |
启动nginx |
sudo /etc/init.d/nginx start |
启动nginx |
sudo service nginx stop |
停止nginx |
sudo /etc/init.d/nginx stop |
停止nginx |
sudo service nginx restart |
重启nginx |
sudo /etc/init.d/nginx restart |
重启nginx |
3 nginx进程及运行机制
3.1 master & worker
大多数情况下nginx是以多进程的方式执行的(好像也支持单进程模式:nginx的单进程模式)
nginx启动后,会在后台产生一个master进程和多个worker进程。master和worker是主进程与子进程的关系。
master进程类似于监工,而worker进程类似于工人。master进程主要负责管理worker进程。
master进程的工作包含:
1.接收外界信号,并向各个worker进程发送信号。
2.监控worker进程(工作进程)的运行状态,当worker进程异常退出后,会自动重新启动新的worker进程。
worker进程的工作为:
基本的网络事件会由worker进程来执行,多个worker一同竞争客户端请求,而这个竞争是公平的,每个worker进程都有同等的机会获取到客户端请求。一个请求只能被一个worker进程处理,一个worker也不能处理其他进程处理的请求。
3.2 具体流程
首先了解一下linux的fork函数:
fork的作用是创建子进程,fork创建的子进程会基本复制主进程的全部信息,也就是说子进程会继承主进程的所有属性。
对于nginx来说,master进程会首先建立好需要的socket,然后fork生成子进程worker(worker进程的个数一般与机器cpu的核数一致),此时每个worker进程都继承了master进程的属性,包括socket,当然,这个socket不是同一个,每个进程都有一个自己的socket,只不过都会监听同一个IP和端口。当有一个连接请求发生时,由于每个worker进程在获取请求上都是公平的,所以每个worker进程都会收到通知。为了确保一个请求只被一个进程处理,所以nginx中使用了互斥锁accept_mutex,所有的worker进程首先去争夺互斥锁,只有抢到互斥锁的进程才能去处理该请求。一个worker进程处理请求的过程包括:获取连接,读取请求,解析请求,处理请求,断开连接。
3.3 同步&异步 阻塞&非阻塞
通过3.2我们知道一个worker进程只能处理一个请求,而worker进程的数量又是有限的,那nginx何来处理高并发一说呢?
首先我们先了解一下 同步&异步 阻塞&非阻塞
偶然在网上看到一个例子,很生动,给大家分享一下:
故事背景:隔壁老王煮开水
出场人物:老王,普通水壶,鸣笛水壶(水烧开后会响)
第一天:
老王把普通水壶放在火上,盯着水壶等着水开。(同步阻塞)
老王觉得这样太浪费时间。
第二天:
老王把普通水壶放在火上,去客厅看电视,时不时去看一下水是否开了(同步非阻塞)
老王觉得这样太麻烦。
第三天:
老王把响水壶放火上,盯着水壶等着水开鸣笛(异步阻塞)
老王觉得自己有点傻
第四天:
老王把响水壶放火上,去客厅看电视,等水壶响了再去拿壶(异步非阻塞)
在这个故事中,
同步&异步是针对水壶来说的,普通水壶是同步,响水壶是异步。所谓同步,就是需要调用者主动去等待调用的结果,异步是调用发生后,调用者不会立即得到调用结果,而是被调用者在处理完后将结果通知给他。
阻塞&非阻塞是针对老王来说的。傻等的老王是阻塞,看电视的老王是非阻塞。阻塞&非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。所以阻塞的老王只能等水开,而非阻塞的老王在水开前可以看电视。
而nginx使用的就是异步非阻塞,比如nginx会在一个线程中处理所有的IOq请求(IO多路复用技术),当一个IO操作A开始时,nginx并不会等待这个IO操作完成,而是会继续处理一下IO操作B,直到获得这个IO操作A已完成的通知再进行下一步处理。所以nginx可以处理高并发的请求。
4 nginx配置
使用nginx -t
命令可以查看nginx配置文件的存放位置。
首先看一下默认配置:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
#mail {
# # See sample authentication script at:
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
# # auth_http localhost/auth.php;
# # pop3_capabilities "TOP" "USER";
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
# server {
# listen localhost:110;
# protocol pop3;
# proxy on;
# }
#
# server {
# listen localhost:143;
# protocol imap;
# proxy on;
# }
#}
首先需要了解的就是nginx是基于模块化的配置,配置的结构图如下:
4.1 高级配置
其中,文件顶部的为高级配置:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
对于user和pid,我们应该保持默认设置。
worker_processes表示worker进程的数量,设置为auto时将与可用的CPU内核数保持一致。
除此之外,下面的配置用来设置worker进程打开文件数的限制:
worker_rlimit_nofile 100000;
如果没有设置的话,该值将与操作系统的限制值一样,设大一点可以避免“too many open files”的问题
4.2 events 模块
events模块中包含nginx中所有处理连接的设置
默认配置为:
events {
worker_connections 768;
# multi_accept on;
}
worker_connections:表示一个worker进程可以同时打开的最大连接数。
multi_accept :告诉nginx收到一个新连接通知后接受尽可能多的连接。
初次之外,还提供了use属性
use epoll;
设置用于复用客户端线程的轮询方法。如果你使用Linux 2.6+,你应该使用epoll。如果你使用*BSD,你应该使用kqueue。
4.3 http模块
HTTP模块控制着nginx http处理的所有核心特性.
对比上边的配置文件
sendfile
服务器响应一个http请求的步骤为:
1.把磁盘文件读入内核缓冲区
2.从内核缓冲区读到内存
3.处理(静态资源不需处理)
4.发送到网卡的内核缓冲区(发送缓存)
5.网卡发送数据
而使用linux的sendfile()可以跳过2,3步,实现数据在磁盘和socket之间的互相拷贝。sendfile属性就是让sendfile()函数发挥作用
tcp_nopush
该参数表示将在一个数据包中发送所有头文件,而不是一个一个发送。
tcp_nodelay
告诉nginx不要缓存数据,而是一段一段的发送
keepalive_timeout
表示与客户端链接的超时时间,超过设置的时间后将会断开连接
types_hash_max_size
types_hash_bucket_size 设置了每个散列块占用的内存大小,types_hash_max_size影响散列表的冲突率。types_hash_max_size越大,就会消耗更多的内存,但散列key的冲突率会降低,检索速度就更快。types_hash_max_size越小,消耗的内存就越小,但散列key的冲突率可能上升
server_tokens
用于隐藏页面中的nginx版本号
server_names_hash_bucket_size
该属性可以解决虚拟主机多域名的问题,当配置多个虚拟主机时,必须配置该属性,并适当太大对应的值,以32的倍数为宜。
server_name_in_redirect
nginx的重定向规则。
当 URL 指向一个目录并且在最后没有包含“/”时,Nginx 内部会自动的做一个 301 重定向,这时会有两种情况:
1、server_name_in_redirect on(默认),URL 重定向为: server_name 中的第一个域名 + 目录名 + /;
2、server_name_in_redirect off,URL 重定向为: 原 URL 中的域名 + 目录名 + /。
client_header_timeout
设置请求头的超时时间
client_body_timeout
设置请求体的超时时间。
以上为nginx的http模块的基础设置。
access_log
设置nginx是否将存储访问日志。关闭这个选项可以让读取磁盘IO操作更快(aka,YOLO)。
error_log
设置为只记录严重的错误。
reset_timeout_connection
设置为on时,将会关闭不响应的客户端链接
send_timeout
客户端的响应超时时间。
以上为nginx关于log的配置。
gzip
告诉nginx采用gzip压缩的形式发送数据。这将会减少我们发送的数据量。
gzip_disable
为指定的客户端禁用gzip功能。我们设置成IE6或者更低版本以使我们的方案能够广泛兼容。
gzip_static
告诉nginx在压缩资源之前,先查找是否有预先gzip处理过的资源。这要求你预先压缩你的文件(在这个例子中被注释掉了),从而允许你使用最高压缩比,这样nginx就不用再压缩这些文件了(想要更详尽的gzip_static的信息,请点击这里)。
gzip_proxied
允许或者禁止压缩基于请求和响应的响应流。我们设置为any,意味着将会压缩所有的请求。
gzip_min_length
设置对数据启用压缩的最少字节数。如果一个请求小于1000字节,我们最好不要压缩它,因为压缩这些小的数据会降低处理此请求的所有进程的速度。
gzip_comp_level
设置数据的压缩等级。这个等级可以是1-9之间的任意数值,9是最慢但是压缩比最大的。我们设置为4,这是一个比较折中的设置。
gzip_type
设置需要压缩的数据格式。上面例子中已经有一些了,你也可以再添加更多的格式。
4.4 负载配置
在http模块中添加如下代码:
upstream proxy_set{
server 10.0.2.16:80 weight=1;
server 10.0.2.17:80 weight=1;
}
server{
listen 80;
server_name localhost 127.0.0.1;
location / {
proxy_pass http://proxy_set
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#client_max_body_size 10m;
}
}
首先,在http节点下新增upstream节点,名称为proxy_set,这个名称可以自己指定,并在节点内配置后端服务器的IP:port以及权重,权重可以省略。
然后配置server节点,listen为监听的端口,server_name为服务器地址,多个地址以空格隔开,我这里设置了两个个,分别为localhost和127.0.0.1,.然后配置server的location节点,proxy_pass的至为http://+upstream的名称,其他配置保持一致(client_max_body_size为设置文件上传大小的限制,nginx默认值为1M)。
配置完以后重启nginx服务。
在浏览器地址栏输入localhost:80/项目名或者127.0.0.1:80/项目名即可访问。
推荐阅读
-
Bash漏洞那些事儿
-
nginx那些事儿
-
【技术分享】关于Python漏洞挖掘那些不得不提的事儿
-
SpringBoot事务隔离等级和传播行为的那些事儿
-
从零开始入门 K8s| 阿里技术专家详解 K8s 核心概念 nginx虚拟机阿里巴巴sqlgo
-
那些年实现的炫酷自定义控件库
-
在VC6.0下,探索栈帧的那些事
-
mac上安装Nginx详细教程
-
Nginx实战系列之功能篇----后端节点健康检查 博客分类: sa_运维_network_硬件
-
非常推荐:搭建一个大型网站架构的实验环境(FreeBsd+Nginx+Squid+Apache) 博客分类: 网站架构 FreeBSDApachenginx教育.net