nginx代码分析

http://hi.baidu.com/beibeiboo/blog/item/cb975473dfbb7f148701b097.html


系列文章都可以从上面的文章中找到

nginx代码分析之(一)——初探

 
  他的代码我全看过了,感觉是高层模型下做的最优精简指令。效果还是不错的。

  发现nginx是无意间在浏览器中看到新浪的一个错误页面“nginx ...”,不由起了好奇心,google了一把,发现这是一个支持负载均衡的反向代理服务器,俄罗斯人开发的,虽然没有走GNU或BSD的License,但是也算是一个开源软件。

  开源代理服务器最熟悉的还是Squid和Apache,但这两者都是正反向代理通吃的,而作为反向代理,实际上和正向代理有较大的差别。我想既然新浪也用它,那自然有它的独到之处。查了一下,中文的网页上说它的HTTP性能可以达到13000TPS以上,但是没有说明数据的出处,国外的网站上暂时找不到相应的数据,但很多人拿它和lighttpd相比。

  很快下载了nginx 0.5.32版本的代码,代码不多,才8万多行,在openssl的基础上支持HTTPS。和Apache的30多万行相比,精简了很多,

  作为web server或反向代理,要的就是一个快,要做到快,除了精简的代码之外,更关键的一点就是并发模型。

  Apache的弱点就在于它的并发模型是普通的进程/线程池,连接数和进程/线程数是1:1的,因此无论是prefork还是worker模式,都将每一个连接对应到一个独立的进程/线程。

  这样的并发模型在连接数不太多(1000以内)时还算可以,但在大规模并发时,其进程/线程总数会非常多。由于Apache本身也比较吃内存,所以到了1000以上的并发时,服务器的内存基本上也就被吃的差不多了,操作系统也在频繁地做进程/线程的切换,非常吃力。

  相比之下,更高级的大型网络服务系统(如电信的智能网系统)一般采用进程/线程池+状态机的模型——也即接数和进程/线程数是m:n的,这样进程/线程总数就不会由于连接的增多而增多,避免了内存和调度切换的开销,但这种做法对程序逻辑的要求较高,需要一个连接拆分为多个逻辑状态(创建,读,写,关闭等,根据实际业务还可以更加细化)每个进程/线程处理完某一种状态后,需要改变该连接的状态值,后续状态由下一个空闲的进程/线程处理。

  nginx就采用了这样的并发模型,对于连接状态的存储,nginx主要采用了这样一个复杂结构。

  struct ngx_connection_s {

  void *data;

  ngx_event_t *read;

  ngx_event_t *write;

  ...

  };

  结构ngx_event_t存储了连接IO状态的详细信息,同时所有的ngx_event_t组成了两个全局的链表,以便进行存取操作。

  在这两个数据结构的基础上,nginx使用了下面这两个函数来完成每个进程/线程的循环

  1. ngx_locked_post_event

  这个函数负责更新某一个连接的状态,在检查到连接IO状态改变(比如通过select)后被调用。

  nginx以module的方式提供了select语义的多种实现:

  poll

  devpoll

  epoll

  eventport

  kqueue

  rtsig

  后面4种,都是BSD/Linux为加速IO操作而提供的异步IO模型

  2. ngx_event_thread_process_posted

  这个函数检查event表,并调用event对应的handler函数,每次处理1个event。

  这两个函数组合使用,就实现了最基本的m:n并发模型。

  nginx代码分析之(二)——Empty Gif是如何工作的

  访问新浪时,时常会有一些网页返回空白(但不是“此页无法显示”),从浏览器的信息中可以知道此时服务器返回了一个1×1的空白gif图片。

  这实际上是nginx实现的,nginx有一个名为Empty Gif的module,专门负责此项工作。

  由于这个module比较简单,我们就先从它入手,来看看nginx的模块实现。

  

  模块注册Empty Gif这个module只有一个文件——ngx_http_empty_gif_module.c

  这个文件比较简单,一开始定义并初始化了3个变量。

  static ngx_command_t ngx_http_empty_gif_commands[] = {...};

  static ngx_http_module_t ngx_http_empty_gif_module_ctx = {...};

  ngx_module_t ngx_http_empty_gif_module = {...};

  其中只有ngx_http_empty_gif_module是非静态的,我将暂时将其称为module主结构变量,

  而其余两个变量都可以由它访问到。

  但是如果继续查看nginx的源码,会发现没有其他地方引用ngx_http_empty_gif_module,

  那这个module是怎么注册并应用起来的呢?

  如果熟悉Apache的代码,会发现这和Apache 2.0的module机制非常类似——每个module都对应到一个module主结构变量,通过这个主结构变量可以访问到这个module的其他内容,该module所有的函数也用函数指针的方式存放在这些结构变量中。

  而且Apache同样没有其他地方的代码引用到module主结构变量。这是因为module不是必须的,该module在某一个特定的编译版本里是可以不存在的。因此一个module是否有效,不是通过代码来决定,而是通过编译选项来实现。

  在nginx代码的auto目录中,有一个名为sources的文件,根据编译选项(configure的参数)的不同,m4宏变量HTTP_MODULES的值会发生变化:

  如果指定了使用empty gif模块(默认就是使用了),则最终m4宏变量HTTP_MODULES的值可能如下:

  HTTP_MODULES="ngx_http_module \

  ngx_http_core_module \

  ngx_http_log_module \

  ngx_http_upstream_module \

  ngx_http_empty_gif_module "

  注意:这里的ngx_http_empty_gif_module字符串对应了ngx_http_empty_gif_module.c文件中的Module主结构变量名。

  编译之前的configure结束后,会在objs目录下生成一个名为ngx_modules.c的文件,此文件的内容如下:

  #include

  #include

  extern ngx_module_t ngx_core_module;

  extern ngx_module_t ngx_errlog_module;

  extern ngx_module_t ngx_conf_module;

  ...

  extern ngx_module_t ngx_http_empty_gif_module;

  ...

  ngx_module_t *ngx_modules[] = {

  &ngx_core_module,

  &ngx_errlog_module,

  &ngx_conf_module,

  ...

  &ngx_http_empty_gif_module,

  ...

  NULL

  };

  在此生成了对ngx_http_empty_gif_module变量的引用,并将其放到了ngx_modules表中,

  通过相关函数可以进行存取。

  这样,在编译时就完成了Empty Gif模块注册的过程。

  模块的初始化和应用初始化一般都是根据配置文件的内容来进行,但和我们一般写程序的做法不同——nginx并没有在一个统一的地方处理所有的配置,而是让每个模块负责处理自己的配置项,如果没有编译这个模块,则其对应的配置项就无法处理,这也是又一个和Apache的相似之处。

  nginx使用了ngx_command_t结构来描述某一个模块对应的配置项及处理函数。

  以Empty Gif模块为例:

  static ngx_command_t ngx_http_empty_gif_commands[] = {

  { ngx_string("empty_gif"),

  NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,

  ngx_http_empty_gif,

  0,

  0,

  NULL },

  0);"> ngx_null_command

  };

  上面的定义表明:

  1. Empty Gif模块只处理一个配置项——“empty_gif”

  2. 这个配置是一个Location相关的配置(NGX_HTTP_LOC_CONF),

  即只有在处理某一个URL子集,如 /test_[0-9]*.gif时才生效。

  

  实际的配置文件可能如下:

  location ~ /test_[0-9].gif {

  empty_gif;

  }

  3. 这个配置项不带参数(NGX_CONF_NOARGS)

  4. 配置处理函数是ngx_http_empty_gif

  ngx_http_empty_gif函数的实现很简单:

  static char *

  ngx_http_empty_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

  {

  ngx_http_core_loc_conf_t *clcf;

  clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

  clcf->handler = ngx_http_empty_gif_handler;

  return NGX_CONF_OK;

  }

  ngx_http_conf_get_module_loc_conf是一个宏,用于获得Location相关的配置表cf中ngx_http_core_module对应的项,获取之后,Empty Gif模块将自己的处理函数挂到了ngx_http_core_module对应的handler上。

  这样,nginx在处理HTTP请求时,如果发现其URL匹配到Empty Gif所属的Location,

  如URL(/test_1.gif)匹配到Location(/test_[0-9].gif),

  则使用ngx_http_empty_gif作为处理函数,这个函数直接向浏览器写回一幅1×1的空白gif图片。


概述 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 版作为调研对象。
### 关于Nginx源码解析文档教程 对于希望深入理解Nginx工作原理和技术细节的人来说,研究其源代码是一项非常有价值的工作。以下是几个有助于理解和探索Nginx源码的方法和资源: #### 官方文档与Wiki 官方提供的文档虽然不会直接涉及底层实现,但对于了解各个功能模块如何配置和使用至关重要。这为后续深入源码打下坚实基础。 #### 社区贡献的文章和书籍 网络上有大量由开发者撰写的博客文章、技术分享以及专门介绍Nginx架构设计的书籍。这些材料通常基于作者个人经验编写而成,能够提供不同视角下的见解[^2]。 例如,《Linux高性能服务器编程》一书中有关章节详细描述了Nginx内部机制,特别是事件驱动模型及其在网络I/O处理方面的应用实例;还有《Nginx HTTP Server》这本书籍则更侧重于实际部署场景中的优化技巧说明。 #### 开源项目分析工具 利用诸如Source Insight这样的IDE或插件可以帮助快速定位特定函数定义位置,并且支持跨文件跳转查看调用关系图等功能特性,极大提高了效率。 #### 配置解析流程 具体到配置加载过程方面,`ngx_conf_read_token()`负责逐字符扫描输入流并执行相应语义动作,如创建新的上下文环境或是设置参数值等操作[^4]。 #### 数据结构剖析 以队列为例,在Nginx中实现了特殊的双向循环链表形式用于管理连接请求和其他动态对象池。为了便于扩展性和灵活性考虑,该类容器并未内置存储空间而是依靠外部实体关联指定字段完成映射转换逻辑[^5]。 ```c struct XXX { int score; ngx_queue_t queue_node; }; ``` 上述C语言片段展示了自定义类型与标准库组件相结合的方式构建复杂业务逻辑单元。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值