Nginx源代码分析
- 格式:docx
- 大小:245.12 KB
- 文档页数:18
nginx开源协议Nginx开源协议。
Nginx是一款高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP 代理服务器。
它具有低内存消耗和高并发能力的特点,因此在互联网领域得到了广泛的应用。
Nginx的开源协议是其成功的重要因素之一,本文将对Nginx开源协议进行介绍和分析。
首先,Nginx采用的是2-clause BSD许可证,也称为简化BSD许可证。
这意味着任何人都可以自由地使用、修改和分发Nginx的源代码,而且不需要公开他们对源代码的修改。
这种开源协议的特点是非常灵活的,允许商业公司将Nginx集成到其产品中,并对其进行修改和定制,而无需公开他们的改动。
这一特点使得Nginx 成为了许多公司和组织首选的服务器软件之一。
其次,Nginx的开源协议鼓励了开发者们对其进行改进和优化。
因为Nginx的源代码是开放的,任何人都可以查看和修改它,这就意味着开发者可以根据自己的需求对Nginx进行定制化开发,以满足特定的业务需求。
这种开放的协作模式使得Nginx在不断地得到改进和完善,从而保持了其在高性能服务器领域的竞争优势。
另外,Nginx的开源协议也为用户提供了更大的自由度。
用户可以根据自己的需求对Nginx进行定制化部署,而无需担心版权和许可证的问题。
这为用户提供了更多的选择和灵活性,使得他们可以更好地适应不同的业务场景和需求。
总的来说,Nginx的开源协议为其在互联网领域的成功提供了坚实的基础。
它的灵活性和开放性吸引了众多开发者和用户,使得Nginx不断地得到改进和优化,保持了其在高性能服务器领域的领先地位。
作为一款开源软件,Nginx的成功也为其他开源项目树立了榜样,展示了开源协作模式的优势和价值。
综上所述,Nginx的开源协议对于其在互联网领域的成功起到了至关重要的作用。
它的灵活性和开放性吸引了众多开发者和用户,促进了Nginx的不断改进和优化,使得其成为了一款备受青睐的高性能服务器软件。
nginx 源码编译
nginx是一个流行的开源Web服务器,广泛用于构建高性能、可扩展的Web应用程序。
在某些特定的情况下需要对nginx进行定制化的编译,即修改源代码后进行编译。
下面是nginx源码编译的步骤:
1. 下载nginx源码。
2.安装编译工具。
进行编译需要GCC编译器和一些相关的编译工具,可以通过以下命令在Linux系统上安装:
```。
sudo apt-get install build-essential。
```。
3.配置编译参数。
进入nginx源码目录,执行以下命令进行编译参数配置:
```。
```。
4.编译。
配置好编译参数后,直接执行以下命令开始编译:
```。
make。
make install。
```。
make命令会编译nginx源码,make install命令会将编译完成的文件安装到指定目录。
5. 运行nginx。
编译完成后,可以使用以下命令启动nginx:
```。
/usr/local/nginx/sbin/nginx。
```。
至此,就完成了nginx源码编译的过程。
其中,可以根据实际需求调整编译参数。
Nginx 源码分析:ngx_array、ngx_list基本数据结构应该说大家对这四个数据结构相当熟悉了,因此我们一并将它们进行分析,瞧一瞧nginx是如何实现它们的。
在此篇之前,我们已经对nginx 内存池(pool)进行了分析,在此基础上来理解ngnix对它们的实现将变得非常简单,特别是内存池(pool)中的ngx_palloc 函数在这四个结构中多次用到,若不清楚想了解原理的可以看看我前面写的文章,它返回的是在内存池分配好空间了的首地址。
一、ngx_array 数组:struct ngx_array_s {void *elts;ngx_uint_t nelts;size_t size;ngx_uint_t nalloc;ngx_pool_t *pool;};参数说明:elts为array数组中元素的首地址,nelts数组中已分配的元素个数,size每个元素大小,nalloc数组容量,pool其所在的内存池。
能够支持五种函数操作:创建数组:ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);数组初始化:ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size) 数组注销:ngx_array_destroy(ngx_array_t *a);添加一个数组元素:ngx_array_push(ngx_array_t *a);添加n个数组元素:ngx_array_push_n(ngx_array_t *a, ngx_uint_t n);ngx_array_create和ngx_array_init,代码比较简明就不多说了,值得注意的是两者之间的差别,ngx_array_init使用情形是已经存在了ngx_array_t的结构体,而ngx_array_create则从零开始建起,贴出代码:View Code重点介绍下ngx_array_push函数View Code代码里面主要就是if和else的逻辑关系,可解释为以下几种情形:第一,如果array当前已分配的元素个数小于最大分配个数,那么用数组元素首地址a->elts 计算出分配元素的首地址,并返回结果。
Nginx源码分析--模块module解析执⾏nginx.conf配置⽂件流程分析⼀ 搭建nginx服务器时,主要的配置⽂件 nginx.conf 是部署和维护服务器⼈员经常要使⽤到的⽂件,⾥⾯进⾏了许多服务器参数的设置。
那么nginx 以模块 module为⾻架的设计下是如何运⽤模块 module来解析并执⾏nginx.conf配置⽂件下的指令的呢?在探究源码之前,需要对nginx下的模块 module 有个基本的认知(详情参考前⾯的博⽂ )同时也要对nginx中常⽤到的⼀些结构有个基本的了解如:内存池pool 管理相关的函数、ngx_string 的基本结构等(详情参考前⾯的博⽂),若不然看代码的时候可能不能很明晰其中的意思,本⽂着重探究的是解析执⾏的流程。
1、从main函数说起。
Nginx的main函数在nginx.c⽂件中(本⽂使⽤release-1.3.0版本源码,200⾏),因为是主函数其中涉及到了许许多多的功能模块的初始化等内容,我们只关注我们需要的部分。
看到326⾏:ngx_max_module = 0;for (i = 0; ngx_modules[i]; i++) {ngx_modules[i]->index = ngx_max_module++;} cycle = ngx_init_cycle(&init_cycle); 可以看出来,这⾥对 ngx_modules (中有介绍)进⾏了索引编号,并且计算得到模块的总数 ngx_max_module。
然后,对cycle进⾏初始化,跳转到 ngx_init_cycle中。
对于cycle 这个变量是nginx的核⼼变量,可以说模块机制都是围绕它进⾏的,⾥⾯的参数⽐较复杂涉及到的内容⼗分多,本⽂并不详细对它讨论,可以将其看作是⼀个核⼼资源库。
2、ngx_init_cycle 函数 这个函数在⽂件ngx_cycle.c中(43⾏),这个函数是nginx初始化中最重要的函数之⼀,⾥⾯涉及到与cycle变量相关的初始化⼯作,看到第188⾏cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module *sizeof(void *)); 这⾥获取了 ngx_max_module 个指针空间,⽤来保存每个模块的配置信息,从cycle 变量的字段conf_ctx 命名中就可以知道,ctx 为context 上下⽂的缩写。
Nginx源代码分析1.Nginx代码的目录和结构nginx的源码目录结构层次明确,从自动编译脚本到各级的源码,层次都很清楚,是一个大型效劳端软件构建的一个范例。
以下是源码目录结构说明:├─auto 自动编译安装相关目录│ ├─cc 针对各类编译器进行相应的编译配置目录,包括Gcc、Ccc等│ ├─lib 程序依托的各类库,包括md5,openssl,pcre等│ ├─os 针对不同操作系统所做的编译配置目录│ └─types├─conf 相关配置文件等目录,包括nginx的配置文件、fcgi相关的配置等├─contrib├─html└─src 源码目录├─core 核心源码目录,包括概念经常使用数据结构、体系结构实现等├─event 封装的事件系统源码目录├─http http效劳器实现目录├─mail 邮件代码效劳器实现目录├─misc 该目录当前版本只包括google perftools包└─os nginx对各操作系统下的函数进行封装和实现核心挪用的目录。
2.大体数据结构2.1.简单的数据类型在core/ 目录里面概念了大体的数据类型的映射,大部份都映射到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/的概念为:/* Types for `void *' pointers. */#if __WORDSIZE == 64# ifndef __intptr_t_definedtypedef long int intptr_t;# define __intptr_t_defined# endiftypedef unsigned long int uintptr_t;#else# ifndef __intptr_t_definedtypedef int intptr_t;# define __intptr_t_defined# endiftypedef unsigned int uintptr_t;#endif因此大体的操作和整形/指针类型的操作类似。
Nginx源码分析- Nginx启动以及IOCP模型本文档针对Nginx1.11.7版本,分析Windows下的相关代码,虽然服务器可能用Linux更多,但是windows平台下的代码也基本相似,另外windows的IOCP完成端口,异步IO模型非常优秀,很值得一看。
Nginx启动曾经有朋友问我,面对一个大项目的源代码,应该从何读起呢?我给他举了一个例子,我们学校大一大二是在紫金港校区,到了大三搬到玉泉校区,但是大一的时候也会有时候有事情要去玉泉办。
偶尔会去玉泉,但是玉泉校区不熟悉,于是跟着百度地图或者跟着学长走。
因为是办事情,所以一般也就是局部走走,比如在学院办公楼里面走走。
等到大三刚来到玉泉,会发现,即使是自己以前来过几次,也觉得这个校区完全陌生,甚至以前来过的地方,也显得格外生疏。
但是当我们真正在玉泉校区开始学习生活了,每天从寝室走到教室大多就是一条路,教超就是另一条路,这两条主要的路走几遍之后,有时候顺路去旁边的小路看看,于是慢慢也熟悉了这个新的校区。
源代码的阅读又何尝不是这样呢,如果没有一条主要的路线,总是局部看看,浅尝辄止不说,还不容易把握整体的结构。
各模块之间的依赖也容易理不清。
如果有一条比较主干的线路,去读源代码,整体结构和思路也会变得明晰起来。
当然我也是持这种看法:博客、文章的作者,写文章的思路作者自己是清楚的,读者却不一定能看得到;而且大家写东西都难免会有疏漏。
看别人写的源码分析指引等等,用一种比较极端的话来说,是一种自我满足,觉得自己很快学到了很多源码级别的知识,但是其实想想,学习乎,更重要的是学习能力的锻炼,通过源码的学习,学习过程中自己结合自己情况的思考,甚至结合社会哲学的思考,以及读源码之后带来的收益,自己在平时使用框架、库的时候,出了问题的解决思路,翻阅别人源码来找到bug的能力。
如果只是单单看别人写的源码分析,与写代码的时候只去抄抄现成的代码,某种程度上是有一定相似性的。
nginx-0.8.38源码探秘先推荐几个研究nginx源码的好网址:/kenbinzhang/category/603177.aspx/p/nginxsrp/wiki/NginxCodeReview/langwan/blog/category/%D4%B4%C2%EB%B7%D6%CE%F6网上分析nginx源码的文章很多,但感觉分析的不够具体和完整,而且都是比较老的nginx版本。
本源码分析基于nginx-0.8.38版本,力求做到更具体和更完整,这是一种自我学习,希望和对此有兴趣的朋友一起探讨,有不正确的地方,也请各位指正。
那么一切从main开始吧!ngx_get_options函数是main调用的第一个函数,比较简单,它负责分析命令行参数,将相应的值赋给对应的全局变量,其中:1.ngx_prefix表示nginx的路径前缀,默认为/usr/local/nginx;2.ngx_conf_file表示nginx配置文件的路径,默认为/usr/local/nginx/conf/nginx.conf;3.ngx_test_config表示是否开启测试配置文件,如配置文件的语法是否正确,配置文件是否可正确打开。
ngx_time_init函数格式化nginx的日志时间,包括ngx_cached_err_log_time,ngx_cached_http_time,ngx_cached_http_log_time,ngx_cached_time。
主要操作在ngx_time_update 内,先获取系统当前时间,与之前保存的时间比较(注意slot),如果已经过时,则将时间重新更新,ngx_cached_time总是指向当前时间的cached_time。
最后还使用了内存屏障ngx_memory_barrier,确保读写顺序。
ngx_log_init函数初始日志结构,主要是对ngx_log变量操作。
初始log 级别为NGX_LOG_NOTICE。
Nginx中upstream模块源码分析2011-10-18/2011/07/nginx-upstream-src-1/一、nginx的upstream目前支持负载均衡方式的分配 (1)1、RR(默认) (1)2、ip_hash (1)3、fair(第三方) (1)4、url_hash(第三方) (2)二、RR策略 (2)2.1 初始化upstream (2)2.2 具体的RR策略 (3)三、Ip_hash策略 (4)3.1 Ip_hash和RR 的策略有两点不同 (5)3.2 RR策略回顾 (5)3.3 ip_hash策略内容 (5)四、ip_hash 模块分析 (6)五、RR模块分析 (10)一、nginx的upstream目前支持负载均衡方式的分配1、RR(默认)每个请求按时间顺序逐一分配到不同的后端服务器,假如后端服务器down掉,能自动剔除。
例如:upstream tomcats {server 10.1.1.107:88 max_fails=3 fail_timeout=3s weight=9;server 10.1.1.132:80 max_fails=3 fail_timeout=3s weight=9;}2、ip_hash每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的题目。
例如:upstream tomcats {ip_hash;server 10.1.1.107:88;server 10.1.1.132:80;}3、fair(第三方)按后端服务器的响应时间来分配请求,响应时间短的优先分配。
4、url_hash(第三方)按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。
下面,我们针对RR和ip_hash的负载均衡策略进行分析。
由于每一种负载均衡策略都是在upstream的框架中使用,upstream控制总的工作流程,负载均衡策略仅仅提供选择或开释server的函数,所以,我们在分析RR时结合upstream(ngx_http_upstream.c)。
NGINX源码分析——概览⼀、概况Nginx可以开启多个进程,每个进程拥有最⼤上限128个⼦线程以及⼀定的可⽤连接数。
最⼤客户端连接数等于进程数与连接数的乘积,连接是在主进程中初始化的,⼀开始所有连接处于空闲状态。
每⼀个客户端请求进来以后会通过事件处理机制,在Linux是Epoll,在FreeBSD下是KQueue放到空闲的连接⾥。
如果设置了线程数,那么被填充的连接会在⼦线程中处理,否则会在主线程中依次处理。
如果解析出是动态脚本请求,会根据fast-cgi的设置访问php-cgi进程,php进程数量的多少依据php-fpm.conf中max_children的设置。
因此Nginx的动态请求能⼒不仅仅依靠Nginx本⾝的设置,还要调试php-fpm。
从源代码级别上看nginx由以下⼏个元素组成:1. worker(进程)2. thread(线程)3. connection(连接)4. event(事件)5. module(模块)6. pool(内存池)7. cycle(全局设置)8. log(⽇志)⼆、MAIN函数整个程序从main()开始算,代码更详细的内容,可以查看两外⼀篇⽂章:ngx_max_module = 0;for (i = 0; ngx_modules[i]; i++) {ngx_modules[i]->index = ngx_max_module++;}这⼏句⽐较关键,对加载的模块点⼀下数,看有多少个。
ngx_modules并不是在原代码中被赋值的,你先执⾏⼀下./configure命令⽣成⽤于编译的make环境。
在根⽬录会多出来⼀个⽂件夹objs,找到ngx_modules.c⽂件,默认情况下nginx会加载⼤约40个模块,的确不少,如果你不需要那个模块尽量还是去掉好⼀些。
接下来⽐较重要的函数是 ngx_init_cycle(),这个函数初始化系统的配置以及⽹络连接等,如果是多进程⽅式加载的会继续调⽤ngx_master_process_cycle(),这是main函数中调⽤的最关键的两个函数。
Nginx源代码分析1.Nginx代码的目录和结构nginx的源码目录结构层次明确,从自动编译脚本到各级的源码,层次都很清晰,是一个大型服务端软件构建的一个范例。
以下是源码目录结构说明:├─auto 自动编译安装相关目录│├─cc 针对各种编译器进行相应的编译配置目录,包括Gcc、Ccc等│├─lib 程序依赖的各种库,包括md5,openssl,pcre等│├─os 针对不同操作系统所做的编译配置目录│└─types├─conf 相关配置文件等目录,包括nginx的配置文件、fcgi相关的配置等├─contrib├─html index.html└─src 源码目录├─core 核心源码目录,包括定义常用数据结构、体系结构实现等├─event 封装的事件系统源码目录├─http http服务器实现目录├─mail 邮件代码服务器实现目录├─misc 该目录当前版本只包含google perftools包└─os nginx对各操作系统下的函数进行封装以及实现核心调用的目录。
2.基本数据结构2.1.简单的数据类型在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_definedtypedef long int intptr_t;# define __intptr_t_defined# endiftypedef unsigned long int uintptr_t;#else# ifndef __intptr_t_definedtypedef int intptr_t;# define __intptr_t_defined# endiftypedef unsigned int uintptr_t;#endif所以基本的操作和整形/指针类型的操作类似。
2.2.字符串的数据类型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 }2.3.内存分配相关(1)系统功能封装内存相关的操作主要在os/unix/ngx_alloc.{h,c} 和core/ngx_palloc.{h,c} 下。
其中os/unix/ngx_alloc.{h,c} 封装了最基本的内存分配函数,是对c原有的malloc/free/memalign 等原有的函数的封装,对应的函数为:∙ngx_alloc 使用malloc分配内存空间∙ngx_calloc 使用malloc分配内存空间,并且将空间内容初始化为0∙ngx_memalign 返回基于一个指定的alignment大小的数值为对齐基数的空间∙ngx_free 对内存的释放操作(2)Nginx的内存池为了方便系统模块对内存的使用,方便内存的管理,nginx自己实现了进程池的机制来进行内存的分配和释放,首先nginx会在特定的生命周期帮你统一建立内存池,当需要进行内存分配的时候统一通过内存池中的内存进行分配,最后nginx会在适当的时候释放内存池的资源,开发者只要在需要的时候对内存进行申请即可,不用过多考虑内存的释放等问题,大大提高了开发的效率。
内存池的主要结构为://ngx_palloc.hstruct ngx_pool_s {ngx_pool_data_t d;size_t max;ngx_pool_t *current;ngx_chain_t *chain;ngx_pool_large_t *large;ngx_pool_cleanup_t *cleanup;ngx_log_t *log;};//ngx_core.htypedef struct ngx_pool_s ngx_pool_t;typedef struct ngx_chain_s ngx_chain_t;下面解释一下主要的几个操作:// 创建内存池ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);大致的过程是创建使用ngx_alloc 分配一个size大小的空间, 然后将 ngx_pool_t* 指向这个空间,并且初始化里面的成员, 其中p->st=(u_char *) p +sizeof(ngx_pool_t);// 初始指向ngx_pool_t 结构体后面p->d.end=(u_char *) p + size;// 整个结构的结尾后面p->max =(size < NGX_MAX_ALLOC_FROM_POOL)? size : NGX_MAX_ALLOC_FROM_POOL;// 最大不超过NGX_MAX_ALLOC_FROM_POOL,也就是getpagesize()-1 大小其他大都设置为null或者0// 销毁内存池void ngx_destroy_pool(ngx_pool_t *pool);遍历链表,所有释放内存,其中如果注册了clenup(也是一个链表结构),会一次调用clenup 的handler 进行清理。
// 重置内存池void ngx_reset_pool(ngx_pool_t *pool);释放所有large段内存,并且将d->last指针重新指向ngx_pool_t 结构之后(和创建时一样)// 从内存池里分配内存void*ngx_palloc(ngx_pool_t *pool, size_t size);void*ngx_pnalloc(ngx_pool_t *pool, size_t size);void*ngx_pcalloc(ngx_pool_t *pool, size_t size);void*ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);ngx_palloc的过程一般为,首先判断待分配的内存是否大于pool->max的大小,如果大于则使用ngx_palloc_large 在large 链表里分配一段内存并返回,如果小于测尝试从链表的pool->current 开始遍历链表,尝试找出一个可以分配的内存,当链表里的任何一个节点都无法分配内存的时候,就调用ngx_palloc_block 生成链表里一个新的节点,并在新的节点里分配内存并返回,同时,还会将pool->current 指针指向新的位置(从链表里面pool->d.failed小于等于4的节点里找出),其他几个函数也基本上为ngx_palloc 的变种,实现方式大同小异// 释放指定的内存ngx_int_t ngx_pfree(ngx_pool_t *pool,void*p);这个操作只有在内存在large链表里注册的内存在会被真正释放,如果分配的是普通的内存,则会在destory_pool的时候统一释放.// 注册cleanup回叫函数(结构体)ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);这个过程和我们之前经常使用的有些区别,他首先在传入的内存池中分配这个结构的空间(包括data段),然后将为结构体分配的空间返回,通过操作返回的ngx_pool_cleanup_t结构来添加回叫的实现。
(这个过程在nginx里面出现的比较多,也就是xxxx_add 操作通常不是实际的添加操作,而是分配空间并返回一个指针,后续我们还要通过操作指针指向的空间来实现所谓的add)2.4.Nginx的基本容器(1)ngx_array对应的文件为core/ngx_array.{c|h}ngx_array是nginx内部封装的使用ngx_pool_t对内存池进行分配的数组容器,其中的数据是在一整片内存区中连续存放的。
更新数组时只能在尾部压入1个或多个元素。
数组的实现结构为:struct ngx_array_s {void*elts;ngx_uint_t nelts;size_t size;ngx_uint_t nalloc;ngx_pool_t *pool;};其中elts 为具体的数据区域的指针,nelts 为数组实际包含的元素数量,size为数组单个元素的大小,nalloc为数组容器预先(或者重新)分配的内存大小,pool 为分配基于的内存池常用的操作有:// 创建一个新的数组容器ngx_array_t *ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);// 销毁数组容器void ngx_array_destroy(ngx_array_t *a);// 将新的元素加入数组容器void*ngx_array_push(ngx_array_t *a);void*ngx_array_push_n(ngx_array_t *a, ngx_uint_t n);//返回n个元素的指针这里需要注意的是,和之前的ngx_pool_cleanup_add一样,ngx_array_push 只是进行内存分配的操作,我们需要对返回的指针指向的地址进行赋值等操作来实现实际数组值的添加。