Nginx进程模型
nginx 采用了多进程的进程模型,其中有且只有一个 master 进程,可以配置多个 worker 进程,还有 cache 进程这里暂不讨论。master 进程主要负责监控 worker 进程,当 worker 进程异常退出时,master 进程能够通过信号感知到并重启拉起 worker 进程,保证了服务的高可用性,同时在每个 worker 进程都运行着事件循环,可以很好的利用多核性能。当然 nginx 也支持单进程模式,不过一般只是在调试的时候才使用,生产上使用的都是多进程模式。
Nginx 进程结构体
pid 用于存放 worker 进程的进程 id
status 存放 waitpid 返回的状态
channel 用于父子进程间通信,使用的 unix 域套接字
proc 是进程的处理函数,data 作为函数的参数传入
name 为进程名称
最后几个用位域表示进程当前的状态,其中:
respawn 表示重新生成的进程
just_spawn 表示进程刚刚产生
detached 表示进程与父进程分离
exiting 表示进程正在退出
exited 表示进程已经退出
typedef ...
Nginx配置HTTPS
前提通过源码安装 nginx 并在编译时通过 --with-http_ssl_module 将 ssl 模块编译进 nginx 二进制中,安装后保证 nginx 二进制文件在环境变量搜索的路径下,如果没有就通过软连接的方式将其软连接到 /usr/local/bin 等目录下
安装 Let’s Encrypt 客户端Let's Encrypt 可以为我们办法免费的 CA 证书,有效期 90 天,到期需要续期。下面以 Centos 系统为例:
yum install python2-certbot-nginx
配置 Nginx保证你的 80 端口可以访问,且可以通过域名访问,注意这个域名需要是 DNS 能够解析到的,使用 server_name 指定你的域名,修改好后启动 nginx
server {
listen 80;
server_name your.domain;
location / {
root html;
index index.html index.htm;
...
《C++性能优化指南》读书笔记
优化概述
使用更好的编译器,并打开编译选项
使用最优算法
使用更好的库并用好库
减少内存分配
减少赋值
移除计算
使用最优数据结构
提高并发
优化内存管理
影响优化的计算机行为
在处理器中,访问内存的性能开销远比其他操作的性能开销大
非对齐访问所需的时间是所有字节都在同一个字中时的两倍
访问频繁使用的内存地址的速度比访问非频繁使用的内存地址的速度快
访问相邻地址的内存的速度比访问互相远隔的地址的内存快
由于高速缓存的存在,一个函数运行于整个程序的上下文中的执行速度可能比运行于测试套件中时更慢
访问线程间共享的数据比访问非共享的数据要慢很多
计算比做决定快
每个程序都会与其他程序竞争计算机资源
如果一个程序必须在启动时执行或是在负载高峰期时执行,那么在测量性能时必须加载负载
每一次赋值、函数参数的初始化和函数返回值都会调用一次构造函数,这个函数可能隐藏了大量的未知代码
有些语句隐藏了大量的计算。从语句的外表上看不出语句的性能开销会有多大
当并发线程共享数据时,同步代码降低了并发量
测量性能
必须测量性能
做出可测试的预测并记录预测
记录代码修改
如果每次都记录了实验内容,那么就可以快 ...
Nginx互斥锁
基于原子操作、信号量以及文件锁,Nginx 在更高层次封装了一个互斥锁,当不支持原子操作时,会使用文件锁来实现,支持原子操作却又不支持信号量时,使用自旋锁的方式实现,支持信号量时使用信号量的方式实现。当 Nginx 判断当前操作系统支持原子变量时,将会优先使用原子变量实现的方法(即原子变量锁的优先级高于文件锁)。不过,同时还需要判断其是否支持信号量,因为支持信号量后进程有可能进入睡眠状态。注意:文件锁只能用于多进程直接的互斥,因为一个进程只能持有一个文件锁,因此文件锁不适用于多线程。fcntl 文档sem_init 文档flock 文档
Nginx 互斥锁结构
支持原子操作(定义了 NGX_HAVE_ATOMIC_OPS 宏)
lock 为原子变量锁
支持信号量时(定义了 NGX_HAVE_POSIX_SEM 宏)
wait 表示等待进程的数量
semaphore 表示是否使用信号量
sem 为信号量锁
spin 大于 0 时表示自旋等待其他处理器的时间,如果为 0 或负值则不存在 pause 的机会,在 Nginx 中当 spin 值为 (ngx_uint_t) -1 时,相当于 ...
Nginx原子操作
Ningx 中能够执行原子操作的原子变量只有整形,包括无符号整形 ngx_atomic_uint_t 和有符号整形 ngx_atomic_t ,这两种类型都使用了 volatile 关键字告诉 C 编译器不要做优化。Nginx 支持的原子操作有如下两种:
ngx_atomic_cmp_set :比较并设置原子变量
ngx_atomic_fetch_add :增加原子变量的值并获取旧值
为了兼容不同架构,Nginx 对于上述两种原子操作封装了不同架构的实现,以下通过其中一种较为易读的方式分析
ngx_atomic_cmp_set
首先比较原子变量 lock 与 old 的值,如果相同则设置 lock 的值为 set,返回 1 表示设置成功
对于其他架构的实现,有的使用了原子库有的使用了内联汇编
static ngx_inline ngx_atomic_uint_t
ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
ngx_atomic_uint_t set)
{
if (*lock ...
Nginx共享内存
共享内存是 Linux 下提供的最基本的进程间通信方法,它通过 mmap 或者 shmget 系统调用在内存中创建了一块连续的线性地址空间,而通过 munmap 或者 shmdt 系统调用可以释放这块内存。使用共享内存的好处是当多个进程使用同一块共享内存时,在任何一个进程修改了共享内存中的内容后,其他进程通过访问这段共享内存都能够得到修改后的内容。Nginx 各进程间共享数据的主要方式就是使用共享内存(在使用共享内存时,Nginx 一般在 master 进程创建,在 master 进程 fork 出子进程后,所有的进程开始使用这块内存中的数据)。Nginx 为了兼容性,提供了三种共享内存的实现方式。mmap 文档shmget 文档
Nginx 共享内存特点
实际上 mmap 和 shmget 都支持多个无亲缘关系的进程间通信,mmap 通过映射到文件来实现,shmget 则是通过映射 key 的方式,可以通过 ipcs -m 来查看当前分配的共享内存情况。但是对于 Nginx 来说没有必要,因为 worker 和 master 是具有亲缘关系的进程,因此使用匿名共享内存的方式更为合适
...
Nginx内部变量
Nginx 的配置文件中以 $ 开头的字符串就是变量,如 $remote_addr 这种就是内部变量(也叫嵌入变量)。它是由 Nginx 内部硬编码支持的变量,更多嵌入变量可查看官方文档。Nginx 同时还支持在配置文件中定义外部变量,由 ngx_http_rewrite_module 模块的 set 指令进行定义。以下分析 http 模块内部变量的代码。
Nginx内部变量设计
只有对变量赋值的时候才分配存储变量值的内存空间
支持通过索引值直接找到数组里的相应变量和根据变量名字符串的 hash 散列值从散列表中找到相应变量
支持缓存变量,提高执行速度
将变量名和变量值分离,避免内存浪费
Nginx内部变量结构变量名结构
变量名用字符串 name 表示
设置和获取变量值的方法由 set_handler 和 get_handler 回调函数设置
data 可以表示地址或变量在结构体中的偏移,主要是为了辅助 get_handler 获取变量值
flags 设置变量的特性
index 是变量值在请求中的缓存数组中的索引typedef struct ngx_http_variable_s ...
Nginx惊群效应解决
惊群问题惊群效应是指多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),如果等待的这个事件发生,那么它就会唤醒等待的所有进程(线程),但最终只能有一个进程(线程)获得这个事件的 “控制权”,对该事件进行处理,而其他进程(线程)获取 “控制权” 失败,只能重新进入休眠状态,进程(线程)频繁切换带来了一定的开销,这种现象和性能浪费就叫惊群效应。
结论accpet 惊群以多进程为例,主进程 bind、listen 端口之后,fork 出的子进程对同一个监听 fd 调用 accept 阻塞式获取连接,当一个连接到来后,所有子进程都会被唤醒,但是只有一个进程可以获取连接,其他进程的 accept 返回 -1。在 Linux 2.6 版本后内核通过增加 WQ_FLAG_EXCLUSIVE 解决了 accept 惊群问题。
epoll 惊群多进程(多线程)共用一个 epoll fd以多进程为例,epoll fd 在主进程创建,子进程共同对其进行 epoll_wait 来获取事件。这种情况下,引发 epoll 惊群的原因与 accept 类似,当有事件发生时,等待同一个事件描述符的所有进程(线 ...
Nginx开发指南
介绍代码布局
auto - 构建脚本
src
core - 基本类型和函数(字符串、数组、日志等)
event - 事件核心
modules - 事件通知模块(epoll、kqueue、select 等)
http - 核心 HTTP 模块和通用代码
modules - 其他 HTTP 模块
v2 - HTTP/2
mail - 邮件模块
os - 平台特定代码
unix
win32
stream - 流模块
包含文件以下两个 #include 语句必须出现在每个 nginx 文件的开头:
#include <ngx_config.h>
#include <ngx_core.h>
除此之外,HTTP 代码还应该包含
#include <ngx_http.h>
Mail 代码应该包括
#include <ngx_mail.h>
Stream 代码应该包括
#include <ngx_stream.h>
整数出于一般目的,nginx 代码使用两种整数类型(ngx_int_t 和 ngx_uint_t),它们分别是 intptr_t 和 ui ...
Nginx平滑升级
Nginx 平滑升级可以在不停止服务的情况下升级 Nginx 的版本,为了加深理解,先了解如何实操,再从源码层面学习 Nginx 是如何实现平滑升级的,以 nginx-1.18.0 升级到 nginx-1.24.0 为例,机器是 Centos7 操作系统
安装 nginx
安装必要的工具包
# 更新 yum 源
yum update
# 安装 wget 工具, 用于获取 nginx 源码
yum -y install wget
# 安装 vim 用于编辑文本
yum -y install vim
# 安装 GCC 和 G++ 编译器
yum -y install gcc gcc-c++
# 安装 PCRE 库
yum -y install pcre pcre-devel
# 安装 zlib 库
yum -y install zlib zlib-devel
# 安装 OpenSSL 开发库
yum -y install openssl openssl-devel
获取 nginx-1.18.0 源码, 并解压编译安装运行这个版本的 nginx
wget https://ngi ...