1.引入:
上下文是指HTTP框架为每个HTTP请求所准备的结构体。 HTTP框架定义的这个上下文是针对于HTTP请求的, 而且一个HTTP请求对应于每一个HTTP模块都可以有一个独立的上下文结构体(并不是一个请求的上下文由所有HTTP模块共用) 。
2.概述:
1.简单地讲, 就是在一个请求的处理过程中, 用类似struct这样的结构体把一些关键的信息都保存下来, 这个结构体可以称为请求的上下文。 每个HTTP模块都可以有自己的上下文结构体, 一般都是在刚开始处理请求时在内存池上分配它, 之后当经由epoll、HTTP框架再次调用到HTTP模块的处理方法时, 这个HTTP模块可以由请求的上下文结构体中获取信息。 请求结束时就会销毁该请求的内存池, 自然也就销毁了上下文结构体。 以上就是HTTP请求上下文的使用场景, 由于1个上下文结构体是仅对1个请求1个模块而言的, 所以它是低耦合的。 如果这个模块不需要使用上下文, 也可以完全不理会HTTP上下文这个概念
2.为什么要有上下文:(epoll原理)epoll详解_致守的博客-CSDN博客
因为Nginx是个强大的全异步处理的Web服务器, 意味着1个请求并不会在epoll的1次调度中处理完成, 甚至可能成千上万次的调度各个HTTP模块后才能完成请求的处理
解疑:
1.Apache服务器:
以Apache服务器为例, Apache就像某些高档餐厅, 每位客人(HTTP请求) 都有1位服务员(一个Apache进程) 全程服务, 每位服务员只有从头至尾服务完这位客人后, 才能去为下一个客人提供服务。 因此餐厅的并发处理数量受制于服务员的数量, 但服务员的数量也不是越多越好, 因为餐厅的固定设施(CPU) 是有限的, 它的管理成本(Linux内核的进程切换成本) 也会随着服务员数量的增加而提高, 最终影响服务质量
2.Nginx:
1位服务员同时处理所有客人的需求。 当1位客人进入餐厅后, 服务员首先给它安排好桌子并把菜单给客人后就离开了, 继续服务于其他客人。 当这位客人决定点哪些菜后, 就试图去叫服务员过
来处理点菜需求, 当然, 服务员可能正在忙于其他客人, 但只要一有空闲就会过来拿菜单并
交给厨房, 再去服务于其他客人。 直到厨房通知这位客人的菜已烹饪完毕, 服务员再取来菜
主动地传递给客人, 请他用餐, 之后服务员又去寻找是否有其他客人在等待服务。
当1位客人进入Nginx“餐厅”时, 首先是由客人来“激活”Nginx“服务员”的。Nginx“服务员”再次来处理这位客人的请求时, 有可能是因为这位客人点完菜后大声地叫Nginx“服务员”, 等候她来服务,也有可能是因为厨房做好菜后厨师“激活”了这位客人的服务, 也就是说“激活”Nginx“服务员”的对象是不固定的
餐厅的流程是先点菜, 再上菜, 最后收账单以及撤碗盘, 但客人是不想了解这个流程的, 所以Nginx“服务员”需要为每位客人建立上下文结构体来表示客人进行到哪个步骤, 即他点了哪些菜、 目前已经上了哪些菜, 这些信息都需要独立的保存。 “服务员”不会去记住所有客人的“上下文信息”, 因为要同时服务的客人可能很多, 只有在服务到某位客人时才会去查对应的“上下文信息
餐厅的流程是先点菜, 再上菜, 最后
收账单以及撤碗盘, 但客人是不想了解这个流程的, 所以Nginx“服务员”需要为每位客人建
立上下文结构体来表示客人进行到哪个步骤, 即他点了哪些菜、 目前已经上了哪些菜, 这些
信息都需要独立的保存。 “服务员”不会去记住所有客人的“上下文信息”, 因为要同时服务的
客人可能很多, 只有在服务到某位客人时才会去查对应的“上下文信息
上面说的Nginx“服务员”就像Nginx worker进程, 客人就是一个个请求, 一个Nginx进程同时可以处理百万级别的并发HTTP请求。 厨房这些设施可能是网卡、 硬盘等硬件。 因此, 如果我们开发的HTTP模块会多次反复处理同1个请求, 那么必须定义上下文结构体来保存处理过程的中间状态, 因为谁也不知道下一次是由网卡还是硬盘等服务来激活Nginx进程继续处理这个请求。 Nginx框架不会维护这个上下文, 只能由这个请求自己保存着上下文结构体。
再把这个例子对应到HTTP框架中。 点菜可能是一件非常复杂的事, 因为可能涉及凉菜、 热菜、 汤、 甜品等。 假如HTTP模块A负责凉菜、 HTTP模块B负责热菜、 HTTP模块C负
责汤。 当一位新客人到来后, 他招呼着服务员(worker进程) 和HTTP框架处理他的点菜需求时(假设他想点2个凉菜、 5个热菜、 1个汤) , HTTP模块A刚处理了1个凉菜, 又有其他客
人将服务员叫走了, 那么, 这个客人处必须有一张纸记录着关于凉菜刚点了一个, 另一张纸
记录着热菜一个没点, 由于HTTP模块C知道, 当前的餐厅汤已经卖完, 业务实在是太简单了
所以不需要再有一张纸记录着汤有没有点。 这两张纸只从属于这个客人, 对于其他客人没有意义, 这就是上面所说的, 上下文只是对于一个请求而言。 同时, 每个HTTP模块都可以拥有记录客人(请求) 状态的纸, 这张纸就其实就是上下文结构体。 当这个客人叫来服务员时, 各个HTTP模块可以查看客人身前的两张纸, 了解点了哪些菜, 这才可以继续处理下去
3.使用:
ngx_http_get_module_ctx和ngx_http_set_ctx这两个宏可以完成HTTP上下文的设置和使用。
先看看这两个宏的定义:
#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
解析:
1.ngx_http_get_module_ctx接受两个参数, 其中第1个参数是ngx_http_request_t指针, 第2个
参数则是当前的HTTP模块对象
ngx_http_get_module_ctx返回值就是某个HTTP模块的上下文结构体指针, 如果这个HTTP模块没有设置过上下文, 那么将会返回NULL空指针。 因此, 在任何一个HTTP模块中, 都可以使用ngx_http_get_module_ctx获取所有HTTP模块为该请求创建的上下文结构体。
2.ngx_http_set_etx接受3个参数,其中第1个参数是ngx_http_request_t指针,第2个参数是准备设置的上下文结构体的指针,第3个参数则是HTTP模块对象。
实践:
//创建上下文结构体
typedef struct {
ngx_uint_t my_step;
} ngx_http_mytest_ctx_t
当请求第1次进入mytest模块处理时, 创建ngx_http_mytest_ctx_t结构体, 并设置到这个请
求的上下文中:
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t*r)
{
// 首先调用ngx_http_get_module_ctx宏来获取上下文结构体
ngx_http_mytest_ctx_t* myctx = ngx_http_get_module_ctx(r,ngx_http_mytest_module);
// 如果之前没有设置过上下文, 那么应当返回NULL
if(myctx==NULL)
{
/*必须在当前请求的内存池r->pool中分配上下文结构体, 这样请求结束时结构体占用的内存才会释放*/
myctx = ngx_palloc(r->pool, sizeof(ngx_http_mytest_ctx_t));
if(myctx=NULL)
{
return NGX_ERROR;
}
// 将刚分配的结构体设置到当前请求的上下文中
ngx_http_set_ctx(r,myctx,ngx_http_mytest_module);
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/129609.html