命令行
nginx命令参数
|
|
信号
可以通过向nginx的master进程发送信号来触发相应操作
|
|
应用
静态资源服务器
gzip静态资源压缩
|
|
开启后响应头出现Content-Encoding:gzip
响应速率限制
|
|
$limit_rate 是http核心模块的内置变量,在nginx中很多模块都有自己的内置变量,在配置文件中,你可以直接使用这些变量 http模块内置变量
日志格式
nginx支持定义多种日志格式,并可自定义命名
|
|
反向代理服务器
upstream
调度策略:
- 轮询(默认) 每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除
- weight 指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况
- ip_hash(不可开启weight) 每个请求按访问ip的hash结果分配,每个访客固定访问一个后端服务器
- fair (需开启upstream_fair模块) 按响应时间分配请求,响应时间短的优先分配
- url_hash(需开启hash模块) 每个url定向到同一台服务器
设备状态
- down表示当前server暂时不参与负载
- weight默认为1,weight越大,负载的权重就越大
- max_fails:一定时间内允许请求失败的次数,默认为1。当超过该次数后,返回proxy_next_upstream模块定义的错误
- fail_timeout: 失败/超时时间,默认10秒。2个作用: 1.在该时间内max_fails次失败后,服务器不可用 2.不可用的时间
- backup: 备机,nginx不会给它转发任何请求。只有当其他节点全部无法连接的时候,nginx才会启用这个节点。 一旦有可用的节点恢复服务,该节点则不再使用,又进入后备状态。
|
|
代理缓存
对于后端服务内容不经常变动的情况,我们可以在反向代理上加代理缓存来减少后端访问次数,减小后端压力
|
|
Nginx架构基础
Nginx的进程结构
多进程: 利用多核优势
多进程-多线程: 处于高可用、高可靠的目的,多线程共享同一块地址空间,当某个第三方模块引发了一个地址错误导致的段错误时会导致整个Nginx进程挂掉
Master子进程分为两类: worker进程和cache相关进程
缓存要在多个worker进程间共享,且不光要被worker进程使用,还要被cache manager和cache loader进程使用,这些进程间的通讯都是使用共享内存解决
master进程和子进程通讯是通过信号进行管理的
绑核的好处?nginx采用事件驱动模型后,每个worker进程从头到尾占用一个核,绑核可以更好的使用每颗CPU核上的CPU缓存,减少缓存失效的情况
多进程间通讯可以使用共享内存、信号等,但我们做进程间的管理时,通常只使用信号
子进程终止时会向父进程发送CHLD信号
Nginx Reload流程
子进程会继承所有父进程已经打开的文件描述符
Nginx 热升级流程
Worker进程优雅的关闭
对于HTTP可以做到优雅关闭,TCP/UDP等由于无法判定是否传输完毕,所以无法实现优雅关闭
网络收发与Nginx事件间的关系
MTU与MSS
MTU和MSS功能基本一致,都是根据对应包大小进行分片,但是实现效果却不太一样。
网络层提供的是一个不可靠的传输方式,如果任何一个包在传输中丢失了,网络层是无法发现的,需要靠上层应用来保证,也就是说如果一个大IP包分片传输时丢失了其中的一部分,之后无法组合出完整的IP包,即使上层应用发现了传输失败,也无法做到仅重传丢失的分片,只能对IP包整个重传,IP报越大的话重传的代价也就越高
传输层提供是一个可靠的传输方式,与网络层不同的是,TCP自身实现了重传机制,丢失任何一篇数据包都能单独重传,所以TCP为了高效传输,需要极力避免被网络层分片,所以就有个MSS标志,并且MSS的值就是根据MTU计算得出的,即避免了网络层上的分片,又保证了最大的传输效率。
MSS=MTU-IP头-TCP头,当在MTU为1500的网络上传输时,MSS为1460(1500-20字节IP头-20字节TCP头)
TCP协议与非阻塞接口
事件分发消费器
Nginx事件循环
Epoll
前提: 高并发连接中,每次处理的活跃连接占比其实是很小的
仅仅遍历活跃连接链表
一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。执行epoll_create时,创建了红黑树和就绪链表,执行epoll_ctl时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可。
Nginx的请求切换
在传统服务器中,一个进程只处理一个连接,当网络事件不满足或时间片用完时,比如就会切换到其他进程,如此往复,每次进程切换都有开销,在大并发情况下,这类开销很客观。
而Nginx在用户态进行连接切换,这样就没有了进程切换的成本,进程时间片通常是5ms~800ms,我们通常将nginx进程优先级调整为-19,这样操作系统分配的时间片会更大。
同步&异步、阻塞&非阻塞
阻塞&非阻塞主要指的是对于操作系统或者底层C库提供的方法或系统调用,我们调用这个方法时,这个方法可能会导致我的进程进入sleep状态,为什么会进入sleep状态呢?当前的条件不满足时,操作系统主动把我的进程切换为另外一个进程,让另外一个进程在使用这个cpu,这样就是一个阻塞方法。非阻塞方法就是我们调用该方法永远不会当我们时间片未用完时把我们进程主动切换掉。
Nginx 模块
如何加入指定模块
在编译时通过--add-module
参数指定模块位置,这样编译时会将该模块一起编译
如何查看模块是否被成功被编译进去了呢?
编译后在objs/ngx_modules.c中查看ngx_modules数组,里面是所有编译进nginx中的模块
如何了解某模块的具体使用方式
每个官方模块都有非常详细的文档说明,举例HTTP_LOG模块
http_log模块文档描述比较简单,仅有一个Directives(指令)单元,我们看一下access_log指令
Syntax: **access_log** *path* [*format* [buffer=*size*] [gzip[=*level*]] [flush=*time*] [if=*condition*]];
**access_log** off;
Default: access_log logs/access.log combined;
Context: http
,server
,location
,if in location
,limit_except
context
:可以使用的上下文环境
另一种方法是去看模块的源代码,比如gzip模块,我们打开src/http/modules/ngx_http_gzip_filter_module.c文件,其中有一个唯一的ngx_command_t类型数组,其中定义了该模块的可用的指令
Nginx模块是如何定义的
Ngx_module_t中的成员字段type
定了子类型模块属于哪个模块
index表示了顺序
成员变量commands类型是ngx_command_t,意思是可用的指令
Nginx 模块分类
Nginx模块分为以下几个大类
- Ngx_core_module
- Ngx_conf_module
- Ngx_event_module
- Ngx_http_module
- 请求处理模块
- 响应过滤模块 关键字filter
- upstream相关模块
- Ngx_stream_module
- Ngx_mail_module
我们会在每类模块的第一个模块中定义该类模块通用的逻辑,比如event_core、ngx_http_core_module、ngx_mail_core_module等
在拿到一个第三方模块时,可以根据模块命名简单判断其工作用途
Nginx如何通过连接池处理请求
每个worker进程中都有一个独立的ngx_cycle_t数据结构,其中connections就是连接池。
配置项worker_conntections表示每个worker进程同时可以打开的最大连接数,对于高性能条件下需要调高配置。
需要注意如果用于反向代理服务器,一个客户端会消耗2个连接,即对下游客户端的连接和对上游服务器的连接
每个连接自动对应一个读事件,一个写事件,即ngx_cycle_t中的read_events、write_events
当你调整worker_connections配置项后,同时也会消耗更多的内存,每个connection连接使用了多大的内存呢?
每个connection即ngx_connection_s结构体,在64位操作系统中占用大约232字节。另外刚才也说到每个conenction对应两个事件,事件的结构体是ngx_event_s,占用96字节
所以我们使用一个连接的时候,占用的内存是232+96*2=424字节。
接着我们来看下ngx_event_s中有哪些成员
这里我们比较关注的是handler,这是一个回调方法,很多第三方模块会把handler设置为自己的实现
timer字段,当我们对http请求做读超时、写超时等设置时,其实是在操作读事件和写事件中的timer,比如client_header_timeout等配置项
我们看下ngx_connection_s中的其他成员
|
|
内存池
如果你开发过nginx第三方模块,虽然我们在写C源代码,但是我们不需要关心内存资源的释放。官方推荐通常不需要做修改
继续说上节的ngx_connection_s结构体,其中有个成员变量pool,它对应的就是这个连接所使用的内存池。这个内存池可以通过配置项connection_pool_size
去配置。
这里有个问题,我们为什么需要内存池?
内存池会提前分配好一块内存,这样可以减少申请内存的次数,而且当我们使用小块内存的时候,通过内存池内部连接机制,可以大大减少内存碎片。
- 连接内存池: 每个连接都会分配对应的连接内存池,大小由
connection_pool_size
控制,默认是256|512 - 请求内存池: 每个http请求都会分配对应的请求内存池,大小由
request_pool_size
控制,默认是4k
请求结束或连接结束后请求内存池/连接内存池会自动销毁,这样第三方模块开发者只需要关注我是从请求内存池中申请分配的内存还是从连接内存池申请分配的内存,无需关心释放,大大加快了第三方模块开发速度
所有worker进程协同工作的关键:共享内存
Nginx进程间通讯方式主要有两种
- 信号
- 共享内存
如果要做数据同步,只能通过共享内存。所谓共享内存,也就是我们打开了一块内存,多个worker可以同时访问它,包括读取和写入。
由于涉及同时操作,所以这里引入了锁。
现在Nginx中使用的锁都是自旋锁。使用自旋锁要求所有的Nginx模块必须快速的使用共享内存(即快速拿锁->快速释放锁)。
在模块中手动编写分配把共享内存给到不同的对象是非常繁琐的,所以这里使用了Slab内存管理器来解决这个问题。
Nginx那些模块使用了共享内存呢?
Ngx_http_lua_api模块是openresty的核心模块,openresty在该模块中定义了lua_shared_dict指令。
lua_shared_dict使用红黑树来保存一个key-value,当内存占用超过分配大小时,lua_shared_dict会使用lru策略,进行淘汰,在此使用链表存储数据用于淘汰数据
|
|
Slab内存管理
之前我们谈到Nginx不同worker进程间需要共享信息时只能通过共享内存,我们也谈到共享内存上可以使用链表、红黑树这样的数据结构。但是每个红黑树上有许多结点,每个结点都需要分配内存去存放,那么怎样把一整块共享内存切割成小块内存供给红黑树使用呢?下面我们看下Slab内存管理是怎样应用于共享内存上的
哈希表的max_size与bucket_size如何配置
Nginx容器
- 数组
- 链表
- 队列
- 哈希表
- 红黑树
- 基数树
数组(ngx_array_t),这里的数组和平时理解的数组还有有所不同的,它是多块连续内存,其中每块连续内存中可以存放许多元素,这样一种数据结构
链表(ngx_list_t)
队列(ngx_queue_t)
基数树是自平衡排序二叉树的一种,不过它的key只能是整型
其中最主要的两种容器是哈希表和红黑树
哈希表
nginx的哈希表和正常见到的哈希表相比,还是稍有不同的。
我们从实现层面上来看,与正常哈希表相似,也就是每个元素会顺序地放在一块连续的内存中,每个元素它的key同样是通过哈希函数来映射的。每个元素就是一个ngx_hash_elt_t结构,name就是它的key,value就是指向实际内容的指针。
不同处在于nginx中哈希表的应用场景不同,nginx中的哈希表仅仅应用于静态不变的内容,也就是在运行过程中,这个哈希表通常是不会出现插入和删除的,nginx刚启动时就可以确定哈希表中一共有多少个元素。nginx中使用哈希表的模块通常会暴露出两个参数—bucket_size、max_size,max_size仅仅控制了最大的哈希表bucket的个数,而不是实际bucket的个数,bucket_size需要注意的是对齐问题,会主动向上对齐,且最好不要超过64字节
Nginx中最长用的容器: 红黑树
红黑树是一棵自平衡二叉查找树,其有以下特点:
- 高度不会超过2倍log(n)
- 增删改查算法复杂度O(log(n))
- 遍历复杂度O(n)
详解HTTP模块
指令的context
指令的合并
处理HTTP请求头部的流程
三次握手用户ACK后,内核认为连接已经建立成功,操作系统会根据自身的负载均衡算法选中某个worker进程,这个worker进程会通过epoll_wait方法拿到刚刚这个连接的句柄,刚刚的ACK其实是一个读事件,根据这个读事件我们就可以调用accept方法,到accpet方法时我们需要分配连接内存池,然后我们的http模块开始从事件模块手中接入请求的处理过程,http模块在启动时定义了一个回调方法ngx_http_init_connection,这时需要将读事件加入到epoll中,并添加超时定时器(client_header_timeout:60s).
接收数据时,通过回调方法ngx_http_wait_request_handler从连接内存中中分配内存,client_header_buffer_size:1k
ps: connection_pool_size只是初始512K,还可以扩
处理请求和处理连接是不一样的,处理连接我只要把它收到我的nginx内存中就ok了,但处理请求时,可能要做大量的请求分析,包括协议、每个header,这里会分配一个请求内存池。
有时请求url特别大,超过了1K内存,这是我们回分配更大的内存large_client_header_buffers:4 8k (最大32k)
HTTP请求的11个阶段
postread阶段
realip模块: 获取真实的客户端ip地址
remote_addr、remote_port会显示真实客户端ip及端口
该模块默认没有被编译进nginx,需编译时手动—with-http_realip_module指定开启
rewrite阶段
rewrite按顺序分为两个rewrite,依次是server rewrite和普通rewrite
301永久重定向 302临时重定向
rewrite_log
开启后,rewrite的每一步都会在error_log中记录
freturn
返回信息
rewrite
|
|
preaccess阶段
对请求做限制 limit_req
漏斗法: 速率恒定,削峰
burst 漏斗大小
|
|
对连接做限制 limit_conn
|
|
access阶段
限制ip访问 access模块
|
|
对用户名密码校验 auth_basic模块
使用第三方做授权控制的auth_request模块
precontent阶段
try_files
|
|
实时流量拷贝 mirror模块
|
|
content阶段
static模块
|
|
concat
当页面需要访问多个小文件时,把他们的内容合并到一次http响应中返回,提升性能
Https://github.com/alibaba/nginx-http-concat
log阶段
access_log模块
|
|
HTTP过滤模块的调用流程
sub模块
功能: 将响应中指定的字符串,替换成新的字符串
默认没有编译进nginx
水电费三分
addition模块
功能: 在响应前或响应后增加内容,而增加内容的方式是通过新增自请求的响应完成
默认没有编译进nginx
Nginx变量
运行原理
变量的特性
- 惰性求值
- 变量值可以时刻变化,其值为使用的那一刻的值
存放变量的哈希表
|
|
HTTP框架提供变量
除了许多http模块会提供变量,http框架本身也会提供一些变量,包括以下几类
- http请求相关的变量
- TCP连接相关的变量
- Nginx处理请求过程中产生的变量
- 发送HTTP响应时相关的变量
- Nginx系统变量
HTTP请求相关的变量
|
|
host 先从请求行中获取,如果含有host头部,则用其值替换掉请求行中的主机名,如果前两者都取不到,则使用匹配上的server_name
TCP连接相关的变量
|
|
Nginx处理请求过程中产生的变量
|
|
发送HTTP响应时相关的变量
|
|
Nginx系统变量
|
|
防盗链手段
简单的防盗链手段referer 模块
思路: 用invalid_referer变量根据配置判断referer头部是否合法
secure_link模块
通过验证URL中哈希值的方式防盗链
默认没有编译进nginx
过程
由某服务器(也可以是nginx)生成加密后的安全连接url,返回给客户端
客户端使用安全url访问nginx,由nginx的secure_link变量判断是否验证通过
原理
- 哈希算法是不可逆的
- 客户端只能拿到执行过哈希算法的URL
- 仅生成URL的服务器,验证URL是否安全的nginx这两者,才保存执行哈希算法前的原始字符串
- 原始字符串通常由一下部分有序构成
- 资源位置,例如HTTP中指定资源的URI,防止攻击者拿到一个安全URLL后可以访问任意资源
- 用户信息,例如用户IP地址,限制其他用户盗用安全URL
- 时间戳,使安全URL及时过期
- 密钥,仅服务器端拥有,增加攻击者猜测出原始字符串的难度
为复杂业务生成新的变量:map 模块
地理位置
geo模块
geoip模块
对客户端您使用keepalive提升连接效率
这里指的是HTTP中的keepalive
功能
多个HTTP请求通过复用TCP连接,实现以下功能
- 减少握手次数
- 通过减少并发连接数减少服务器资源的消耗
- 降低TCP拥塞控制的影响
协议
|
|
反向代理与负载均衡
原理
负载均衡原理
Nginx可扩展性
x轴是把完全相同的工作分给上游,x轴扩展特点是服务是无状态的,无论其多少个服务他们是通等地对用户提供服务的,也就是我们通常理解的水平扩展。
y轴是按业务分割好再分给上游。
z轴是按用户的相关属性(价值、位置等)分给上游
负载均衡策略
round-robin
|
|
指定上游服务地址的upstream与server指令
功能: 置顶一组上游服务器地址,其中,地址可以是域名、IP地址或unix socket地址。可以再域名或IP地址后加端口,如果不加端口,那么默认使用80端口。
通过用参数:
- backup: 指定当前server为备份服务,仅当非备份server不可用时,请求才会转发到该server
- down 标识某台服务已经下线,不在服务
加权Round-Robin负载均衡算法
|
|
对上游服务使用keepalive长连接
功能: 通过复用连接,降低nginx与上游服务器建立、关闭连接的消耗,提升吞吐量的同时降低时延。
模块: ngx_http_upstream_keepalive_module
对上游连接的http头部设定
|
|
Upstream_keepalive的指令
|
|
指定上游服务域名解析的resover指令
|
|
示例:
|
|
哈希算法
Ip_hash
以客户端IP地址作为hash算法的关键字,映射到特定的上游服务器
|
|
hash
指定任意关键字作为hash key,基于hash算法映射到置顶的上游服务器
|
|
当upstream中一台服务器出现问题时,如果直接移除该机器,导致机器数量减少,这种行为影响到hash算法
一致性哈希算法可以缓解这个问题
一致性哈希算法(upstream_hash模块提供)
宕机或扩容时,hash算法引发大量路由变更,可能导致上游缓存大范围失效。
upstream使用一致性哈希算法非常简单,只需要加上consistent参数
最少连接算法
优先选择连接最少的上游服务器:upstream_least_conn模块
功能: 从所有上游服务器中,找出当前并发连接数最少的一个,将请求抓饭到它
|
|
使用共享内存使负载均衡策略对所有worker进程生效: upstream_zone模块
分配出共享内存,将其他upstream模块定义的负载均衡策略数据、运行时每个上游服务的状态数据存放在共享内存上,,以对所有nginx worker进程生效
|
|
举个例子,默认round robin时是在worker各自的内存中存放计数的,多worker的情况下,由于每次访问时处理请求的worker不同,于是计数就打在了不同的地方,导致轮训没有按设定的方式执行,如图设置8081与8082权重是3和1,但是却出现了非3比1的情况,使用zone 可以解决这个问题。
upstream模块提供的变量
|
|
反向代理
http反向代理流程
接收请求优化逻辑:默认接收请求时先接收完客户端信息,然后再去连接上游服务器
响应优化逻辑:默认接收完上游响应后再向客户端发送响应
对http协议的反向代理: proxy模块
功能: 对上游服务使用http/https协议进行反向代理
|
|
proxy_pass 后面接域名时会DNS缓存,导致这个域名背后的机器负载不均衡,官方推荐是使用变量解决
生成发往上游的请求头部
|
|
接受用户请求包体的方式
用户请求包体是由http 请求框架所处理的,但其实这部分主要是被反向代理使用
|
|
客户端包体的接受
|
|
存在包体时,接受包体所分配的内存:
- 若接收头部时已经接受完全部包体,则不分配
- 若剩余待接收包体长度小于client_body_buffer_size,则仅分配所需大小
- 分配client_body_buffer_size大小内存接收包体
最大包体的长度限制
|
|
临时文件路径格式
|
|
与上游服务建立连接
|
|
上游连接启用TCPkeepalive
|
|
修改TCP连接中的local address
|
|
当客户端关闭连接时
|
|
向上游发送HTTP请求
|
|
接收上游的响应
接收上游的HTTP响应头部
|
|
接收上游的HTTP包体
|
|
及时转发包体
|
|
接收上游时网络速度相关指令
|
|
上游包体的持久化
|
|
处理上游的响应头部
|
|
修改返回的Set-Cookie头部内容
|
|
修改上游服务返回的Location头部内容
|
|
上游出现错误时的容错方案
proxy_next_upstream
当上游中某server返回的响应存在某种错误时,我们可以通过向该集群其他server重新发送请求,以此达到容错的目的
默认非幂等方法的请求不会重传
重新发送请求的前提的未向客户端发送任何内容
|
|
限制proxy_next_upstream的时间与次数
|
|
|
|
对于错误码大于等于300的响应指定错误页面
|
|
及时清除缓存
第三方模块nginx_cache_purge
功能: 接收到指定HTTP请求后立即清除缓存
|
|
示例
|
|
uwsgi、fastcgi、scgi指令表
Uwsgi、fastcgi、scgi与proxy模块一样都是反向代理模块
面向下游使用http协议、面向上游使用独立协议
搭建websocket反向代理
服务端向客户端推送请求,长连接
websocket缺点:
- 数据分片有序
- 不支持多路复用 一个连接上不能同时进行多个未完成的请求
- 不支持压缩
|
|
用分片提升缓存效率
对于断点续传、大文件传输等含有range协议的场景时,slice模块非常有用
|
|
open file cache提升系统性能
对于打开的文件句柄优化
|
|
性能优化方法论
如何高效实用CPU
网络优化
TCP协议优化
滑动窗口
功能: 用于限制连接的网速,解决报文乱序和可靠性问题
nginx中limit_rate等限速指令皆依赖它实现
由操作内核实现
连接两端各有发送窗口和接收窗口
缓冲区
流量控制
拥塞处理
TCP关闭连接
time_wait优化
如果服务器上有大量的CLOSE_WAIT,一定是应用程序有bug
|
|
MSL(maximum segment lifetime): 报文最大生存时间,默认MSL为60秒
维持2MSL时长的TIME-WAIT状态: 保证至少一次报文的往返时间内端口是不可用的
lingering_close延迟关闭
当Nginx处理完成调用close关闭连接后,若接收缓冲区仍然街道客户端发来的内容,则服务器会向客户端发送RST包关闭连接,导致客户端由于收到RST而忽略http response
应用层协议优化
TLS/SSL优化握手性能
|
|
TLS/SSL中的会话票证tickets
Nginx将会话session中的信息作为tickets加密发给客户端,当客户端下次发起TLS连接时带上tickets,由nginx解密验证后复用会话session。
会话票证虽然更易在nginx集群中使用,但破坏了TLS/SSL的安全机制,有安全风险,必须频繁更新tickets密钥
|
|
http长连接
- 减少握手次数
- 通过减少并发连接数减少了服务器资源的消耗
- 降低TCP拥塞控制的影响
对下游及上游都有keepalive_requests指令
|
|
gzip压缩
|
|
nginx默认不会压缩来自上游的响应
|
|
升级更高效的http2协议
向前兼容http/1.x协议
传输效率大幅度提升
IO优化
减少磁盘IO
优化读取
sendfile零拷贝
内存盘、ssd盘
减少写入
AIO
增大error_log级别
关闭access_log
压缩access_log
是否启用proxy_buffering
syslog替代本地IO
线程池thread pool
静态资源服务场景下应用IO线程池会有性能有较大幅度提升
直接IO绕开磁盘高速缓存
对于大文件,不太可能在内存中缓存住,通过裸IO可以减少数据多次拷贝
减少磁盘读写次数
empty_gif模块
从前端页面做用户行为分析时,由于跨域等要求,前端打点的上报数据一般是GET请求,且考虑到浏览器解析DOM树的性能消耗,所以请求透明图片消耗最小,而11的gif图片体积最小(仅43字节),故通常请求gif图片,并在请求中把用户行为信息上报服务器
Nginx可以在access日志中获取到请求参数,进而统计用户行为。但若在磁盘汇总读取11的文件则有磁盘IO消耗,empty_gif模块将图片放在内存中,加快处理速度
|
|
access日志压缩
sendfile零拷贝提升性能
- 减少了内存拷贝次数
- 减少内存切换次数
使用sendfile与gzip配合时有点小问题,gzip压缩是动态压缩,一定要先拷贝到用户态再压缩,所以gzip会导致sendfile失效。这里可以借助gzip_static模块及gunzip模块进行相应优化。
对于静态文件可以使用gzip_static静态预先压缩(gzip),解决这个问题。
nginx离线压缩
如果服务器端只有压缩文件,但是客户端浏览器不支持接收压缩文件,可以利用gunzip进行响应时动态解压缩
linux默认的gzipC库分配内存能力不高,可以考虑google的tcmalloc
stub_status监控nginx
通过—with-http_stub_status_module启用模块
功能:
通过HTTP接口,实时监测nginx的连接状态。
统计数据存放于共享内存中,所以统计值包含所有worker进程,且执行reload不会导致数据清0,但热升级会导致数据清0
|
|
handled 小于accepts 一定是worker_connections配置小了
源码阅读
第三方模块源码的快速阅读方法
1.分析模块提供的config文件
2.分析ngx_module_t模块
3.分析ngx_command_t数组看看支持哪些配置指令
4.对于http模块,分析ngx_http_module_t中在http{}解析前后实现了哪些回调
5.根据第3,4步,找出该模式生效方式
- 在11个阶段中哪个阶段处理请求
- 在过滤响应中生效吗?
- 在负载均衡中生效吗?
- 是否提供了新的变量?
debug日志
—with-debug
|
|
开启debug级别后访问nginx,在error_log日志中可以看到debug信息
Openresty
Openresty概述
Openresty组成部分
ngx_http_lua_module
模块给予nginx各阶段嵌入lua的能力ngx_stream_lua_module
是在stream四层反向代理中加入lua
openresty的运行机制
openresty中的SDK
- cosocket通讯
- udp
- tcp
- 基于共享内存的字典shared.DICT (跨worker进程)
- 定时器
- 基于协程的并发编程
- 获取客户端请求与响应的信息
- 修改客户端请求与响应,包括发送响应
- 子请求
- 工具类
- 正则表达式
- 日志
- 系统配置
- 编解码
- 时间
Openresty使用要点
- 不破坏nginx的事件驱动体系,不使用任何会阻塞nginx进程进行事件调度的方法
- 不调用会导致nginx进程主动休眠的方法
- 谨慎调用第三方库,若不是通过cosocket实现(例如Lua标准网络库或第三方服务提供的SDK库),就无法融入nginx的事件驱动体系中,那么对TCP消息的处理通常会导致nginx进程进入sleep状态
- 不调用长时间占用CPU的方法
- 避免lua代码块执行密集计算指令
- 不调用会导致nginx进程主动休眠的方法
- 不破坏nginx的低内存消耗有点,避免对每个请求分配过大内存
- 对会导致分配内存的lua SDK,谨慎分配内存,考虑用状态机多次分批处理
- 理解lua代码块如何嵌入nginx中执行
- 理解lua sdk详细用法,及他们如何继承在nginx中处理请求
- 保持lua代码的高效
Openresty中的Nginx模块
核心模块
反向代理模块
该页面中模块都比较老,不建议使用
工具模块
Or官方Lua模块
如何在Nginx中嵌入Lua代码
在Nginx启动过程中嵌入Lua代码
在11个HTTP阶段中嵌入Lua代码
控制rewrite/access是否延迟lua代码
|
|
在过滤响应、负载均衡时嵌入lua代码
在openssl处理ssl协议时嵌入lua代码
在lua代码中获取当前阶段
Openresty中Lua与C代码交互原理
系统级配置指令
取得配置参数SDK
获取、修改请求与响应的SDK
工具类型的SDK
sdk
cosocket
cosocket
并发编程
并发编程
FAQ
1.平滑重启时,某些旧worker由于某些原因一直不能关闭,导致旧worker越来越多
使用worker_shutdown_timeout
配置项,设定worker优雅关闭超时时间,超过该时间后,会强制关闭worker进程
2.简略的请求处理流程
建立连接->处理请求头->请求处理11阶段
3.nginx master进程接收到HUP时的系统调用
|
|
4.健康检查
借助upstream模块server
指令的fail_timeout和max_fails以及proxy_next_upstream
指令实现被动健康检查
某server在fail_timeout时间内失败max_fails次后,接下来的fail_timeout秒会被认为是不可用;fail_timeout后会默认为恢复,继续往该主机发送请求,往复循环
对于失败的请求会采用next_upstream策略请求下一个server
如何判定请求server失败呢?这是由*_next_upstream设定的
5.nginx内存池和连接池都是worker级别的