nginx与Apache处理HTTP事务不同:以餐厅服务为例 (涉及nginx的上下文结构)

缘由

今天读到 深入理解nginx 的第四章,读到这个例子的时候非常棒,感觉非常有利于理解两个非常出名的服务器为什么不同。也可以说是为什么nginx就能支持高并发。

背景

我们去餐厅吃饭,一般都步骤都是:
  1. 找到座位
  2. 点餐
  3. 用餐
  4. 付钱
  5. 走人
如果把web服务器当作餐厅,那么
  • 一个请求当作一个客人
  • 一个worker进程当作一个服务员。

那么,让我们来看看两款web服务器分别是如何为它们的客人提供服务的。


Apache

Apache的服务方式,非常简单,那么就是
  1. 指定一个服务员(进程)全程服务一个客人(请求)。从客人入门开始,这个服务员就带领着这个客人走向合适的位置。
  2. 接着,在那里等着客人点餐,点餐之后又把去通知厨房。
  3. 接着又去厨房等着把这个菜给客人端上来
  4. 等等...
等这位客人吃完饭,付了钱,走了之后,这个服务员再去给下一位客人。
我们可以看出这是一家非常高档的餐厅,那么这家餐厅能够服务的客人的数量受限制于服务员的人数。但是当服务员的人数越多,确实能够服务的客人就越多,可惜,从计算的角度来看,那么进程也就越多,进程越多,在不同的进程间切换的频率会非常高,成本会非常高,进而影响了服务器的性能。

Nginx

其实Nginx做法,更像是我们在一家寻常的餐厅,只有一个服务员,服务所有客人。
  1. 当客人进入这家餐厅时,
  2. 服务员带领客人走到正确的用餐地点
  3. 然后这个服务员给了你一份菜单,就走开去服务别的客人了
  4. 当你点好餐之后你就去叫服务员,服务员拿起菜单给了厨房,之后又去服务别的客人了。
  5. 厨房做好菜之后又叫了服务员
  6. 服务员去把菜端给客人,接着又去服务了其他别人....

Nginx的上下文

为什么需要上下文

我相信现在大家都这两种模式都有大概的认识了吧。那么这里就引出了另一个问题,那就是nginx这家餐厅的服务员当被客人叫过去的时候,怎么知道客人这个时候是要点餐,还是要收钱呢?大家想象一下,我们在餐厅用餐的是我们客人告诉服务员我想走什么是吧?我要点餐,我就喊“点餐”,我要付钱,我就喊“买单”。确实如此,所以,这里对每一个客人,每一个请求,维护一个上下文的信息,这个上下文信息由每一个请求自己持有,当进程过来服务这个客人的时候,回去查这个上下文信息,那么进程就知道这个时候是要给客人拿点菜单,还是收钱了。
很显然,这个过程对于Apache是不用的,因为Apache的那个进程守着这个客人呢?眼睁睁看着客人,难道不知道客人这个时候要是点餐还是付钱?

有几个上下文

后来我理解到了,其实对一个请求来讲,至少有两个上下文:
  • 一个是一类模块的上下文:所谓一类模块,也就是HTTP模块需要一个不同与I/O事件的上下文。
  • 一个是一个模块的上下文:比如我昨天做的例子的mytest,作为一个单独的模块需要一个上下文。

一类模块的上下文

一类模块的上下文主要使用ngx_module_t来存放,之前学过的在安装过程中会在nginx目录下产生:
nginx-1.2.9/objs/ ngx_modules.c 
文件,我们可以看到 ngx_modules.c 文件内装的就是这个nginx服务器会使用到的模块,这份文档中有一个数组,就是ngx_module_t结构体的,所以,可以看出每一个模块都有一个ngx_module_t结构体,而这个结构体里面就会存放一类模块的上下文,如下所示:
struct ngx_module_s {
...
    void                 *ctx;
    ngx_uint_t            type;
...
};

我省略了很多无关信息,那么其中这个ctx变量就是存放的一类模块的上下文,而这个type变量和这个ctx是相关的,用type指向不同类的上下文,参数主要有:
NGX_HTTP_MODULE
NGX_CORE_MODULE
NGX_CONF_MODULE
NGX_EVENT_MODULE
NGX_MAIL_MODULE
如果type是 NGX_HTTP_MODULE,那么说明这个模块是和HTTP相关的,所以其上下文ctx必须是 ngx_http_module_t

一个模块的上下文

这个是属于单独的一个模块。比如,我昨天写的mytest模块,就可以有一个自己的上下文。我可以定义这个上下文的结构体:
比如:
typedef struct{
	ngx_uint_t my_step;
}ngx_http_mytest_ctx_t;

那么当这个请求需要使用到这个模块的时候,就可以使用这个上下文,具体这上下文里面放什么当然和这个小模块的功能有关,如果是自己写的话,那么可以自己定义。也就是说,这个定义这个上下文的结构体,可以给其赋值和取值。
nginx为我们定义了赋值和取值的时候宏:
#define ngx_http_get_module_ctx(r, module)  (r)->ctx[module.ctx_index]
#define ngx_http_set_ctx(r, c, module)      r->ctx[module.ctx_index] = c;


那么什么时候赋值呢?

对于我们自己完成的例子,书上所写是在ngx_http_mytest_handler函数内。
倒推回去,ngx_http_mytest_handler会被ngx_http_mytest所调用

ngx_http_mytest是写在ngx_command_t内的回调函数。

疑问

书上在举例的时候,说Nginx用了一个进程应对了所有的客人,但是实际上Nginx也是有很多worker进程的,那么同一个客人,在每次呼叫服务的时候,到底是不是喊的同一个进程的呢?
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值