好像这是唯一的一份模块开发文档.. 以下简称 guide 读代码!!
nginx 程序启动
把 nginx.conf 读入内存 处理模块 ngx_http_module_t 定义的 configuration. 应该是如下的执行顺序 pre create_main_conf 执行 ngx_command_t
里面定义的函数. 可以视为 init_main_conf 的自动处理部分 init_main_conf ... postconfig listen fork 子进程(真正的服务器进程 master), 父进程退出
master 执行 setsid 脱离终端; getpid 写入 pid 文件; 启动 worker
Handlers, Filters
Handler. Handler 可以做的工作包括: 作为 handler-chain 的一部分, 返回 NGX_DECLINED 指示继续交给后继的 chain 运行. 现在 mod_passport 就仅仅是在这个机制上工作的 handler-chain 分成好几个 phases, 包括 NGX_HTTP_POST_READ_PHASE, NGX_HTTP_SERVER_REWRITE_PHASE, ... 参考 src/http/ngx_http_core_module.h 直接处理 request, 返回 response Filter. 貌似 handler 的 phases 缺乏处理返回给客户端的内容的阶段.
于是这个任务就在 Filter 里面完成了 Header Filter Body Filter
Upstream Handler, Load-Balancer
基本上需要用到这个来写模块的可能性就很小了
Upstream Handler. 从guide的翻译:"假设需要和后台服务器通信,比如 FastCGI, Memcached.. 那怎么才能避免网络 IO 阻塞 primary event loop 呢?这就要靠 Nginx 内置的 upstream 网络连接机制, 以及 hook 对应的 handler" Load-Balancer. 咱们邮件中心应用这个机制的一种可能是把请求 dispatch 到后端的桶上去... 这样恐怕 Upstream 需要能配置为泛域名——因为如果每增加一个桶
前面就改一次配置就太恶心了——或者自己实现一个 Upstream 连接机制
这里多说一句,只要架构准备好了,扩展其他业务就简单。比如 nginx 的 smtp/pop3/imap4 反向代理功能
内存池, 数据结构, 以及对应函数
忍不住要抱怨一下, 相比较于 apr, nginx 的内置数据结构及其函数简直太XXXX了, 这直接导致了编写 module 成为一件门槛很高的工作
常用函数的包装(也是为了跨平台编译, 甚至更好的性能), 见 src/core/ngx_string.h ngx_memset ngx_memzero ngx_memcpy ... 资源池 pool, 见 src/core/ngx_palloc.c ngx_palloc, 这个是最基本的malloc包装 ngx_calloc, 包装完 ngx_palloc 后再 memzero 一下 ngx_pool_cleanup_add, 正是因为这个功能的存在,pool 成为一个资源池而不仅仅是内存池. 调用 ngx_pool_cleanup_add 后返回一个指针 ptr,
然后设置 ptr->handler 为释放资源的函数就可以. 具体参考 ngx_destroy_pool 看看是怎么调用 handler 的 神秘的 temp_pool, 还没有搞清楚为什么许多数据结构里面除了 pool 外还提供了一个 temp_pool ngx_str_t, 它提供了一个存储 binary string 的方案.
不过并没有提供 realloc 相关的运算(这样的话内存管理就
复杂了)
在 log/sprintf 函数族输出的时候, 用 %V 来对应 ngx_str_t *, 以避免哪些没有 '\0' 结尾的字符串输出 ngx_array_t, 本质是一个栈表. 就我所观察, 它是所有复杂数据结构的基石, 好在不难理解.
提示:ngx_array_push 的用法和 ngx_pool_cleanup_add 类似, 也是先返回一个指针, 然后调用者再对这个指针做操作——而不是调用者先在外面把 insert/push 的数据准备好再传递进去. 从此可一窥 nginx 的编程风格. ngx_list_t 提供一个单向链表, 链表的每个单元是一个固定
大小的 array 这个东东比 ngx_array_t 有什么优势呢??array 每次都扩大内存都 double palloc 再 memcpy, ngx_list_t 效率上会好一些. 仍然是 ngx_list_push() 这样的风格 虽然效率高, 但 ngx_list_t 被使用的相比 ngx_array_t 就少多了, 大概和迭代算法写起来稍麻烦有关系. src/core/ngx_hash.c ngx_table_elt_t 是在 ngx_hash.h 里面定义的,也算一个常用的数据结构,但和 hash table 没有什么关系
首先是 ngx_hash_keys_arrays_t, 它几乎可以说是一个完备的 hash table 'keys' 成员就是当向里面添加 key/value 的时候, 依次 push 进去的 array 'keys_hash' 和 'hsize' 对应, 用来分布 hash, 在初始化的时候 palloc 好,以后就无法改变了. ngx_hash_keys_array_init() 里面的 asize 就是初始化 hash table 的 'keys' array 大小. ngx_hash_add_key() 用来放 key/value 这个数据结构只提供了 add 方法(甚至没有 set), 遍历之也还算简单, 就是没有快速的 get 的实现!! 虽然没有 get, 但 ngx_hash_add_key() 里的 ngx_hash_key_t *hk->key_hash 为接下来的操作埋下了伏笔 看起来 ngx_hash_t 才是真正可以 get(find) 的 hash_table ngx_hash_t 好像不能一个一个的插入,只能从一个 array table 里面去初始化. 一个有趣的事实是,nginx 自己的模块里面,所有用 ngx_hash_keys_arrays_t 的地方,最后都把 'keys' 用ngx_hash_init() 初始化出一个 ngx_hash_t (为什么 nginx 要这么设计 hash table 呢?难道和 wildcard 支持相关?没有继续深究了,现
有的知识已经足够完成 mod_passport 的功能了) ngx_rbtree 红黑树 ngx_radix_tree radix树用来放路由表这样的结构是比较好的... 看看代码, 果然是只在 geo_module 里面用到了这个结构 ngx_regex