NginxCodeReview
Ningx代码研究.
概述Nginx ("engine x") 是一个高性能的 HTTP 和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器 。 Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的Rambler.ru 站点开发的,它已经在该站点运行超过四年多了。 Igor 将源代码以类BSD许可证的形式发布。自Nginx 发布四年来,Nginx 已经因为它的稳定性、丰富的功能集 、示例配置文件和低系统资源的消耗而闻名了。目前国内各大门户网站已经部署了Nginx, 如新浪、网易、腾讯等;国内几个重要的视频分享网站也部署了Nginx,如六房间、酷6等。 新近发现Nginx 技术在国内日趋火热,越来越多的网站开始部署Nginx。 - from http://wiki.nginx.org/NginxChs 我们研究nginx的源代码的动机是为了完成分段反向代理项目的开发,由于分段反向代理的需求要求对web server的并发性很强,并且是给予http协议的基础上进行的, 所以我们选择了使用Nginx的模块的形式进行开发。 我们发现目前学习nginx的例子很少,主要是emiller的模块开发介绍这篇文章, 但是单独研究这篇文章发现很多晦涩难懂的地方,而目前还没有其他更好的文章来对这些地方做解释, 有些东西必须要通过源代码的研读才可以了解的更加清楚,所以我们决定开始进行代码研究计划,以便于更好的完成开发任务 根据目前的状况,我们决定使用最新的稳定版本进行研究,故而选择 0.7.61 版作为调研对象。 http://sysoev.ru/nginx/nginx-0.7.61.tar.gz 研究计划下面是王晓哲帮助制定的调研计划
参与人员徐景(rainx), 王晓哲(chaoslawful) 研究文档学习emiller的文章http://www.evanmiller.org/nginx-modules-guide.html 熟悉nginx的基本数据结构nginx 代码的目录结构解开nginx的代码后,在src目录下发现有如下的几个目录 core event http mail misc os 其中 :
为了方便了解整个结构, 我们在src目录下建立了一个子目录,叫做demo,然后根据每个章节内容的不同,分别在demo下建立子目录,存放一些学习时使用的代码,如我们目前的章节是学习基本的数据类型,所以建立basic_types子目录 rainx@rainx-laptop:~/land/nginx-0.7.61/src$ mkdir -p demo/basic_types/ nginx简单的数据类型的表示在 core/ngx_config.h 目录里面定义了基本的数据类型的映射,大部分都映射到c语言自身的数据类型 typedef intptr_t ngx_int_t; typedef uintptr_t ngx_uint_t; typedef intptr_t ngx_flag_t; 其中 ngx_int_t, nginx_flag_t, 都映射为 intptr_t; ngx_uint_t映射为 uintptr_t 这两个类型在/usr/include/stdint.h的定义为: /* Types for `void *' pointers. */ #if __WORDSIZE == 64 # ifndef __intptr_t_defined typedef long int intptr_t; # define __intptr_t_defined # endif typedef unsigned long int uintptr_t; #else # ifndef __intptr_t_defined typedef int intptr_t; # define __intptr_t_defined # endif typedef unsigned int uintptr_t; #endif 所以基本的操作和整形/指针类型的操作类似 建立文件 #include <stdio.h> #include "../../core/ngx_config.h" int main() { ngx_uint_t a; ngx_int_t b; a = 1000; b = -1000; printf ("%d + %d = %d\n", a, b, a+b); return 0; } 编译测试 gcc -I ../../../objs/ -I ../../os/unix/ basic_types_int.c -o basic_types_int ./basic_types_int 1000 + -1000 = 0 nginx字符串的数据类型的表示nginx对c语言的字符串类型进行了简单的封装, core/ngx_string.h/c 里面包含这些封装的内容 其中定义了 ngx_str_t ,ngx_keyval_t, ngx_variable_value_t 这几个基础类型的定义如下 typedef struct { size_t len; u_char *data; } ngx_str_t; typedef struct { ngx_str_t key; ngx_str_t value; } ngx_keyval_t; typedef struct { unsigned len:28; unsigned valid:1; unsigned no_cacheable:1; unsigned not_found:1; unsigned escape:1; u_char *data; } ngx_variable_value_t; 可以看出 ngx_str_t 在原有的uchar* 的基础上加入的字符串长度的附加信息, 初始化使用ngx_string宏进行,他的定义为: #define ngx_string(str) { sizeof(str) - 1, (u_char *) str } 测试字符串的代码 demo/basic_types/basic_types_str.c #include <stdio.h> #include "ngx_config.h" #include "ngx_conf_file.h" #include "nginx.h" #include "ngx_core.h" #include "ngx_string.h" #include "ngx_string.h" volatile ngx_cycle_t *ngx_cycle; void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const char *fmt, ...) { } int main() { u_char* p = NULL; ngx_uint_t size; ngx_str_t dst; ngx_str_t mystr = ngx_string("hello, world !"); ngx_keyval_t pair = {ngx_string("url"), ngx_string("http://rainx.cn/index.php?test=1")}; int dst_len =ngx_base64_encoded_length(mystr.len); printf("source length is %d, destination length is %d\n", mystr.len, dst_len ); p = malloc( ngx_base64_encoded_length(mystr.len) + 1); dst.data = p; ngx_encode_base64(&dst, &mystr); printf("source str is %s\ndestination str is %s\n", mystr.data, dst.data); free(p); size = pair.value.len + 2 * ngx_escape_uri(NULL, pair.value.data, pair.value.len, NGX_ESCAPE_URI); p = malloc (size * sizeof(u_char)); ngx_escape_uri(p, pair.value.data, pair.value.len, NGX_ESCAPE_URI); printf("escaped %s is : %s (%d)\noriginal url size is %d\n", pair.key.data, p, size, pair.value.len); free(p); return 0; } 编译运行 gcc -c -O -pipe -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Wunused-function -Wunused-variable -Wunused-value -Werror -g -I ../../../objs/ -I ../../os/unix/ basic_types_str.c -I../../core/ -I../../event/ -I../../os/ -o basic_types_str.o gcc -o basic_types_str basic_types_str.o ../../../objs/src/core/ngx_{string,palloc}.o ../../../objs/src/os/unix/ngx_alloc.o -lcrypt -lpcre -lcrypto -lz rainx@rainx-laptop:~/land/nginx-0.7.61/src/demo/basic_types$ ./basic_types_str source length is 14, destination length is 20 source str is hello, world ! destination str is aGVsbG8sIHdvcmxkICE= escaped url is : http://rainx.cn/index.php%3ftest=1 (34) original url size is 32 core/ngx_string.h/c 中同时也封装了一批字符/字符串处理的函数和宏,他们的使用大多数情况下和c标准库中的类似,只是在内存分配相关的函数中有一定的区别。 比如 u_char *ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src); 除了源字符串驻外,还要传入ngx_pool_t的指针作为参数,使用nginx自己的内存分配方式进行内存的分配。 除了标准的字符串操作外, nginx还实现了例如: // base64 编码/解码函数和宏 #define ngx_base64_encoded_length(len) (((len + 2) / 3) * 4) #define ngx_base64_decoded_length(len) (((len + 3) / 4) * 3) void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src); ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src); //utf-8 编码/解码相关函数 uint32_t ngx_utf8_decode(u_char **p, size_t n); size_t ngx_utf8_length(u_char *p, size_t n); u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len); // urlencode和html实体的编码解码 uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type); void ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type); uintptr_t ngx_escape_html(u_char *dst, u_char *src, size_t size); 等对于http服务有帮助的宏和函数
[LINK]http://code.google.com/p/nginxsrp/wiki/NginxCodeReview |