Nginx学习笔记 —— 基本数据结构
来源:互联网 发布:怎么向淘宝投诉卖家 编辑:程序博客网 时间:2024/05/22 05:14
Nginx的一些特点
高性能
采用事件驱动模型,可以无阻塞的处理海量并发连接高稳定性
内存池避免了c程序常见的资源泄漏问题
模块化架构使得各个功能模块完全解耦
one master / mutil workers 进程池设计实现了自我监控管理,保证一个worker挂掉也能快速恢复服务低资源消耗
不使用传统的进程或线程服务器模型,没有切换成本
使用很多节约系统资源的编程技巧高扩展性
模块化架构,可以根据需要开发适合自己业务逻辑的模块
Nginx自定义整数类型
跨平台兼容
// 定义在 core/ngx_config.htypedef intptr_t ngx_int_t; // 有符号整数typedef uintptr_t ngx_uint_t; // 无符号整数typedef intptr_t ngx_flag_t; // 标志整数类型
- intptr_t、uintptr_t 是C/C++标准里 大小足够容纳指针的整数类型
// 定义在 core/ngx_rbtree.h 红黑树的键(key)类型typedef ngx_uint_t ngx_rbtree_key_t;typedef ngx_int_t ngx_rbtree_key_int_t;// 定义在 os/unix/ngx_time.h 毫秒的整数类型typedef ngx_rbtree_key_t ngx_msec_t;typedef ngx_rbtree_key_int_t ngx_msec_int_t;
无效值:
在Nginx中,定义了类似python的None,初始化变量以 UNSET = -1 表示未初始化
由于C/C++是强类型语言,Nginx为 -1 定义了不同类型转换的宏
// 定义在 core/ngx_conf_file.h#define NGX_CONF_UNSET -1 // 通用无效值#define NGX_CONF_UNSET_UINT (ngx_uint_t) -1 // 无符号整数的无效值#define NGX_CONF_UNSET_PTR (void *) -1 // 指针类型的无效值...
有了UNSET概念,Nginx 以宏的形式提供了初始化和条件赋值函数:
// 定义在 core/ngx_conf_file.h#define ngx_conf_init_value(conf, default) \ if(conf == NGX_CONF_UNSET) \ { \ conf = default; \ }
当conf未初始化,初始化为default默认值
异常机制错误处理
分离代码逻辑里的正常部分和异常部分,使代码的结构更加清晰
Nginx用宏定义了七个常用的错误码,类型是 ngx_int_t
// 定义在 core/ngx_core.h#define NGX_OK 0 // 执行成功,无错误#define NGX_ERROR -1 // 执行失败,最常见的错误#define NGX_AGAIN -2 // 未准备好,需要重试#define NGX_BUSY -3 // 后端服务正忙#define NGX_DONE -4 // 执行成功,但还需要有后序操作#define NGX_DECLINED -5 // 执行成功,但未做处理#define NGX_ABORT -6 // 发生了严重的错误
内存池
减少了系统调用的次数,而且能够很好的避免内存碎片和泄漏
// 定义在 ngx_core.htypedef struct ngx_pool_s ngx_pool_t; // 简化定义// 定义在 ngx_palloc.hstruct ngx_pool_s{ ... ngx_pool_cleanup_t *cleanup; // 析构时的清理动作 ngx_log_t *log; // 关联的日志对象}
Nginx 会为每一个 TCP/HTTP 请求创建一个独立的内存池————也就是 ngx_pool_t 对象
当请求结束时自动销毁 ngx_pool_t 对象,释放内存池和它拥有的所有内存
// 使用了内存对齐,速度快,但可能会有少量内存浪费void * ngx_palloc(ngx_pool_t * pool, size_t size);// 没有使用内存对齐void * ngx_pnalloc(ngx_pool_t * pool, size_t size);// 内部调用了 ngx_palloc(),并且把内存块清零void * ngx_pcalloc(ngx_pool_t * pool, size_t size);// 释放内存ngx_int_t ngx_free(ngx_pool_t * pool, void * p);
清理机制:
Nginx框架自动管理内存池的生命周期,当请求结束时内存池里的内存会完全归还给系统;
内存只是系统资源的一个方面,其他的系统资源(如文件句柄)并不会随着内存池的销毁而一并释放;
如果不做特殊的操作就可能造成资源泄漏。
这种清理机制就是C++里的析构函数思想,在对象销毁的时候自动调用析构函数
Nginx 定义了一个保存清理信息的结构 ngx_pool_cleanup_t,用来在内存销毁时执行清理动作
// 定义在 ngx_palloc.htypedef void (*ngx_pool_cleanup_t) void * data; // 清理函数指针原型struct ngx_pool_cleanup_s // 清理信息结构体{ ngx_pool_cleanup_pt handler; // 清理动作,函数指针 void *data; // 清理所需数据 ngx_pool_cleanup_t *next; // 后续链表指针};// 定义在 ngx_palloc.hngx_pool_cleanup_pt * ngx_pool_cleanup_add(ngx_pool_t * p, size_t size);
这个函数使用 size 为 ngx_pool_cleanup_t::data 分配内存,返回清理信息 ngx_pool_cleanup_t 对象,
设置它的 handler 和 data,就可以达到向内存池”注册”清理函数的目的
std::vector 有两个模板参数:
template<class T, // 容纳的元素类型 class Alloctor = std::allocator<T> // 内存配置器 > class vector
我们可以自定义内存配置器替换第二个参数,vector就能使用Nginx的内存池
字符串
ngx_str_t 不是传统意义上的字符串,准确的说应该是一个 内存块引用
// 定义在 ngx_string.htypedef struct{ size_t len; // 字符串长度 u_char * data; // 字符串所在地址}ngx_str_t;
这种设计的好处是字符串的操作非常廉价,只有两个整数的成本,
不需要复制大量数据,所以对它的拷贝、修改都非常高效,也节约了内存的使用
(类似 boost::string_ref 或者 std::string_view C++17 )
缺点也显而易见,由于 ngx_str_t 只是引用内存,所以应该尽量以只读方式去使用
多个 ngx_str_t 共享一块内存,擅自修改内容会影响到其他的引用
同时引用的内存地址可能失效,访问到错误的内存区域
初始化与赋值:
#define ngx_string(str) {sizeof(str) - 1, (u_char *) str}#define ngx_null_string {0, NULL}#define ngx_str_set(str, text) \...#define ngx_str_null(str) \...
基本操作:
#define ngx_strcmp(s1, s2) // 大小写敏感比较,参数是 u_char*#define ngx_strncmp(s1, s2, n) // 大小写敏感比较,有长度参数#define ngx_strstr(s1, s2) // 查找子串#define ngx_strlen(s) // 使用'\0'计算字符串长度// 大小写不敏感字符串比较,参数是 u_char*ngx_int_t ngx_strcasecmp(u_char * s1, u_char * s2);ngx_int_t ngx_strncasecmp(u_char * s1, u_char * s2, size_t n);// 字符串转整数类型,参数是 u_char*ngx_int_t ngx_atoi(u_char * line, size_t n);// 内存池复制字符串,参数是 ngx_str_t*u_char * ngx_pstrdup(ngx_pool_t * pool, ngx_str_t * src);
格式化函数:
// 直接向 buf 输出格式化内容,不检查缓冲区的有效性u_char * ngx_sprintf(u_char * buf, const char * fmt, ...);// 参数 max 和 last 指明了缓冲区结束位置u_char * ngx_snprintf(u_char * buf, size_t max, const char * fmt, ...);u_char * ngx_slprintf(u_char * buf, u_char * last, const char * fmt, ...);
函数执行完返回 u_char* 指针,指示格式化输出后在 buf 里的结束位置,可以用来判断结果的长度
时间与日期
Nginx定义了专用的时间数据结构 ngx_time_t:
// 定义在 core/ngx_times.htypedef struct{ time_t sec; // 自 epoch 以来的秒数,即时间戳 ngx_uint_t msec; // 秒数后的小数部分,单位是毫秒 ngx_int_t gmtoff; // GMT时区偏移量} ngx_time_t;Nginx 内部使用了cache机制来存放时间值,使用一个全局指针 ngx_cached_time 指示当前缓存的时间volatile ngx_time_t * ngx_cached_time; // 当前缓存的时间// 获取当前时间的秒数#define ngx_time() ngx_cached_time->sec// 获取完整的时间结构体#define timeofday() (ngx_time_t *)ngx_cached_time// 强制更新缓存时间(需要使用锁,成本较高,当必须获得当前精确时间时调用)void ngx_time_update(void)
日期结构,是标准C结构< ctime>里的tm
// 定义在 os/unix/ngx_time.htypedef struct tm ngx_tm_t;
日期操作函数
// 定义在 core/ngx_times.hvoid ngx_gmtime()time_t t, ngx_tm_t * tp);void ngx_localtime(time_t t, ngx_tm_t * tp);
将 time_t 转换为格林尼治时间/本地时间
u_char * ngx_http_time(u_char * buf, time_t t);u_char * ngx_http_cookie_time(u_char * buf, time_t t);
上面两个函数调用了 ngx_gmtime() 和 ngx_sprintf(), 把 time_t 转换成日期字符串
// 定义在 core/ngx_parse_time.htime_t ngx_parse_http_time(u_char * value, size_t len);
解析字符串形式的日期时间,转换为 time_t
同时,Nginx 使用全局变量提供缓存好的日期字符串,减少频繁调用的成本:
ngx_str_t ngx_cached_err_log_time; // 错误日志的日期字符串ngx_str_t ngx_cached_http_time; // HTTP格式的日期字符串ngx_str_t ngx_cached_http_log_time; // HTTP日志的日期字符串ngx_str_t ngx_cached_http_log_iso8601; // ISO8601 格式日期字符串ngx_str_t ngx_cached_syslog_time; // 系统日志格式日期字符串
运行日志
Nginx 使用结构体 ngx_log_t 表示运行日志
// 定义在 core/ngx_log.hstruct ngx_log_s{ ... ngx_uint_t log_level; // 日志级别 ngx_log_t * next; // 日志对象链表指针};// 定义在 core/ngx_log.hvoid ngx_log_error_core(ngx_uint_t level, ngx_log_t * log, ngx_err_t err, const char * fmt, ...);
使用 ngx_log_t 对象记录 level 级别的日志,字符串消息的格式语法与 ngx_sprintf() 相同
日志级别参数 level 取决于下列的宏,他们对应配置文件里的
debug | info | notice | warn | error | crit | alert | emerg
#define NGX_LOG_STDERR 0 // 最高级别#define NGX_LOG_EMERG 1#define NGX_LOG_ALERT 2#define NGX_LOG_CRIT 3#define NGX_LOG_ERR 4 // 常用级别#define NGX_LOG_WARN 5#define NGX_LOG_NOTICE 6#define NGX_LOG_INFO 7#define NGX_LOG_DEBUG 8 // 最低级别
STDERR 是比emerg还要高的错误级别,如果使用这个级别来记录日志,
那么Nginx将直接输出日志到标准错误输出(通常为终端屏幕)而非写入日志文件。
常用的日志级别为 NGX_LOG_ERR 和 NGX_LOG_WARN
err参数表示调用失败返回的错误码
// 定义在 os/unix/errno.htypedef int ngx_err_t;
日志宏
#define ngx_log_error(level, log, ...) \...
只有当消息的日志级别高于log对象级别(即消息的level值小)时才会调用函数记录日志
* 实际开发中应该使用宏来记录日志 *
C++封装实现:https://github.com/chen892704/Nginx-Learning
- Nginx学习笔记 —— 基本数据结构
- nginx 源码学习笔记(六)——nginx基本数据结构
- nginx 源码学习笔记(六)——nginx基本数据结构
- nginx 学习笔记(三)基本数据结构
- Nginx学习笔记 —— 高级数据结构
- Nginx学习——Nginx基本配置
- 《学习opencv》笔记——基本数据结构,CvMat,矩阵访问
- java学习笔记之实现基本数据结构——栈
- opencv学习笔记——数据结构与基本绘图
- nginx 源码学习(四) 基本数据结构 ngx_queue_t
- nginx 源码学习(五) 基本数据结构 ngx_list_t
- nginx 源码学习(六) 基本数据结构 ngx_array_t
- nginx代码学习wiki-基本数据结构
- Scala学习笔记<基本数据结构>
- Nginx学习(3)—封装的数据结构
- Nginx学习笔记(三):封装的数据结构
- nginx学习笔记(5):高级数据结构ngx_rbtree_t
- nginx 源码学习笔记(八)——基本容器——array数组
- Service层在J2EE分层结构中的作用
- Linux学习之路-文件输入输出
- Android进阶#(11/12)使系统适应变化——重构
- 详细解读KMP模式匹配算法
- PAT_1085. Perfect Sequence
- Nginx学习笔记 —— 基本数据结构
- 编程起航篇
- 苹果审核被拒问题总结
- 构建Visual Studio Code编译调试Linux C++环境
- Hibernate 逆向工程生成pojo----使用自定义pojo模板
- AppOps原理
- 动态规划——《3》最长公共子序列
- 数位DP UVA
- centos7 64位系统安装32位库时发生冲突