本文在这基础上分析nginx服务器收到http请求行、请求头部后,http框架是如何调度各个http模块共同完成这个http请求。例如: http框架调度静态模块,获取服务器目录下的某个html页面返回给客户端; 或者http框架调度access权限访问模块,判断这个客户端是否有权限访问服务器。
一、event事件与http框架的交互
在接收完http请求行、http请求头部后,会调用ngx_http_process_request这个函数开始处理http请求。因为一个http请求由11个处理阶段组成,而每一个处理阶段都允许多个http模块介入,因此在这个函数中,将调度各个阶段的http模块共同完成这个请求。
//接收到http请求行与请求头后,http的处理流程,是第一个http处理请求的读事件回调 //这个函数执行后,将把读写事件的回调设置为ngx_http_request_handler。这样下次再有事件时 //将调用ngx_http_request_handler函数来处理,而不会再调用ngx_http_process_request了 static void ngx_http_process_request(ngx_http_request_t *r) { ngx_connection_t *c; c = r->connection; //因为已经接收完http请求行、请求头部了,准备调用各个http模块处理请求了。 //因此需要接收任何来自客户端的读事件,也就不存在接收http请求头部超时问题 if (c->read->timer_set) { ngx_del_timer(c->read); } //重新设置当前连接的读写事件回调 c->read->handler = ngx_http_request_handler; c->write->handler = ngx_http_request_handler; //设置http请求对象的读事件回调,这个回调不做任何的事情。 //那http请求对象的读事件回调,与上面的连接对应的读事件回调有什么关系呢"color: #ff0000">ngx_http_request_handler。而请求对象的读事件设置为ngx_http_block_reading, 请求对象的写事件回调设置为ngx_http_core_run_phases, 这个回调在ngx_http_handler内设置。这样在事件再次到来时不会调用ngx_http_process_request函数处理了。那event事件模块的读写事件回调与http请求对象的读写事件回调有什么关系呢"https://img.jbzj.com/file_images/article/201706/201706301419073.jpg" alt="" />
//http请求处理读与写事件的回调,在ngx_http_process_request函数中设置。 //这个函数中将会调用http请求对象的读写事件回调。将event事件模块与http框架关联起来 static void ngx_http_request_handler(ngx_event_t *ev) { //如果同时发生读写事件,则只有写事件才会触发。写事件优先级更高 if (ev->write) { r->write_event_handler(r); //在函数ngx_http_handler设置为:ngx_http_core_run_phases } else { r->read_event_handler(r); //在函数ngx_http_process_request设置为:ngx_http_block_reading } //处理子请求 ngx_http_run_posted_requests(c); }可以看到,连接对象的读事件回调中,会调用http请求对象的读事件回调。连接对象的写事件回调会调用http请求对象的写事件回调。
图中可看出,在event的读事件发生时,epoll返回后会调用读事件的回调ngx_http_request_handler。在这个读事件回调中,又会调用http框架,也就是http请求对象的读事件回调ngx_http_block_reading,这个http请求对象的读事件回调是不做任何事情的,相当于忽略读事件。因此http框架将会返回到事件模块。那为什么要忽略读事件呢"text-align: center">
对于写事件的处理就复杂多了, 在event的写事件发生时,epoll返回后会调用写事件的回调ngx_http_request_handler,在这个写事件回调中,又会调用http框架,也就是http请求对象的写事件回调ngx_http_core_run_phases。这个http框架的回调会调度介入11个请求阶段的各个http模块的hander方法,共同完成http请求。
二、调度http模块处理请求
在上面代码中,会调度ngx_http_core_run_phases这个函数,使得各个http模块能介入到http请求中来。而这个函数是在ngx_http_handler设置的。
//调用各个http模块协同处理这个请求 void ngx_http_handler(ngx_http_request_t *r) { //不需要进行内部跳转。什么是内部跳转"htmlcode">//调用各个http模块协同处理这个请求, checker函数内部会修改phase_handler void ngx_http_core_run_phases(ngx_http_request_t *r) { cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ph = cmcf->phase_engine.handlers; //调用各个http模块的checker方法,使得各个http模块可以介入http请求 while (ph[r->phase_handler].checker) { rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); //从http模块返回NGX_OK,http框架则会把控制全交还给事件模块 if (rc == NGX_OK) { return; } }假设阶段2有三个http模块介入了http请求, 阶段3有一个模块介入了http请求、阶段4也有一个模块介入了请求。当开始处理阶段2时,将调用阶段2中的所有http模块进行处理,此时phase_handler指向阶段2的开始位置。之后每处理完阶段2中的一个模块时,phase_handler指向阶段2的下一个模块,直到阶段2处理完成。
当阶段2中的所有http模块都处理完成时,phase_handler将指向阶段3
因阶段3只有一个http模块,因此当阶段3中的所有http模块都处理完成时,phase_handler将指向阶段4
那这个handlers数组是什么时候创建的呢"font-size: 18px">三、11个http请求阶段数组创建
在解析nginx.conf配置文件时,解析到http块时,会调用ngx_http_block这个函数开始解析http块。在这个函数中,也会把所有需要介入到11个http请求阶段的http模块,注册到数组中。
//开始解析http块 static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { //http配置解析完成后的后续处理,使得各个http模块可以介入到11个http阶段 for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } module = ngx_modules[m]->ctx; if (module->postconfiguration) { //每一个http模块的在这个postconfiguration函数中,都可以把自己注册到11个http阶段 if (module->postconfiguration(cf) != NGX_OK) { return NGX_CONF_ERROR; } } } }例如ngx_http_static_module静态模块,会将自己介入11个http阶段的NGX_HTTP_CONTENT_PHASE阶段回调设置为ngx_http_static_handler
//静态模块将自己注册到11个http请求阶段中的NGX_HTTP_CONTENT_PHASE阶段 static ngx_int_t ngx_http_static_init(ngx_conf_t *cf) { cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); //静态模块在NGX_HTTP_CONTENT_PHASE阶段的处理方法 *h = ngx_http_static_handler; return NGX_OK; }例如: ngx_http_access_module访问权限模块,会将自己介入11个http阶段的NGX_HTTP_ACCESS_PHASE阶段回调设置为ngx_http_access_handler
//访问权限模块将自己注册到11个http请求阶段中的NGX_HTTP_ACCESS_PHASE阶段 static ngx_int_t ngx_http_access_init(ngx_conf_t *cf) { cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); //访问权限模块在NGX_HTTP_ACCESS_PHASE阶段的处理方法 *h = ngx_http_access_handler; return NGX_OK; }上面的这些操作,只是把需要介入到11个http阶段的http模块保存到了ngx_http_core_main_conf_t中的phases成员中,并没有保存到phase_engine中。那什么时候将phases的内容保存到phase_engine中呢"htmlcode">
//开始解析http块 static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { //初始化请求的各个阶段 if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } }假设阶段1有一个http模块介入请求,阶段2有三个http模块介入请求、阶段3也有一个http模块介入请求。则ngx_http_init_phase_handlers这个函数调用后,从ngx_http_phase_t phases[11]数组转换到ngx_http_phase_handler_t handlers数组的过程如下图所示:
//初始化请求的各个阶段 static ngx_int_t ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf) { //11个http请求阶段,每一个阶段都可以有多个http模块介入。 //这里统计11个节点一共有多个少http模块。以便下面开辟空间 for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) { n += cmcf->phases[i].handlers.nelts; } //开辟空间,存放介入11个处理阶段的所有http模块的回调 ph = ngx_pcalloc(cf->pool,n * sizeof(ngx_http_phase_handler_t) + sizeof(void *)); cmcf->phase_engine.handlers = ph; n = 0; //对于每一个http处理阶段,给该阶段中所有介入的http模块赋值 for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) { h = cmcf->phases[i].handlers.elts; switch (i) { case NGX_HTTP_SERVER_REWRITE_PHASE://根据请求的uri查找location之前,修改请求的uri阶段 if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) { cmcf->phase_engine.server_rewrite_index = n; //重定向模块在数组中的位置 } checker = ngx_http_core_rewrite_phase; //每一个阶段的checker回调 break; case NGX_HTTP_FIND_CONFIG_PHASE://根据请求的uri查找location阶段(只能由http框架实现) find_config_index = n; ph->checker = ngx_http_core_find_config_phase; n++; ph++; continue; case NGX_HTTP_REWRITE_PHASE: //根据请求的rui查找location之后,修改请求的uri阶段 if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) { cmcf->phase_engine.location_rewrite_index = n; } checker = ngx_http_core_rewrite_phase; break; case NGX_HTTP_POST_REWRITE_PHASE: //NGX_HTTP_REWRITE_PHASE阶段修改rul后,防止递归修改uri导致死循环阶段 if (use_rewrite) { ph->checker = ngx_http_core_post_rewrite_phase; ph->next = find_config_index;//目的是为了地址重写后,跳转到NGX_HTTP_FIND_CONFIG_PHASE阶段,根据 //url重写查找location n++; ph++; } continue; case NGX_HTTP_ACCESS_PHASE: //是否允许访问服务器阶段 checker = ngx_http_core_access_phase; n++; break; case NGX_HTTP_POST_ACCESS_PHASE: //根据NGX_HTTP_ACCESS_PHASE阶段的错误码,给客户端构造响应阶段 if (use_access) { ph->checker = ngx_http_core_post_access_phase; ph->next = n; ph++; } continue; case NGX_HTTP_TRY_FILES_PHASE: //try_file阶段 if (cmcf->try_files) { ph->checker = ngx_http_core_try_files_phase; n++; ph++; } continue; case NGX_HTTP_CONTENT_PHASE: //处理http请求内容阶段,大部分http模块最愿意介入的阶段 checker = ngx_http_core_content_phase; break; default: //NGX_HTTP_POST_READ_PHASE, //NGX_HTTP_PREACCESS_PHASE, //NGX_HTTP_LOG_PHASE三个阶段的checker方法 checker = ngx_http_core_generic_phase; } n += cmcf->phases[i].handlers.nelts; //每一个阶段中所介入的所有http模块,同一个阶段中的所有http模块有唯一的checker回调, //但handler回调每一个模块自己实现 for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--) { ph->checker = checker; ph->handler = h[j]; ph->next = n; ph++; } } return NGX_OK; }四、http阶段的checker回调
在11个http处理阶段中,每一个阶段都有一个checker函数,当然有些阶段的checker函数是相同的。对每一个处理阶段,介入这个阶段的所有http模块都共用同一个checker函数。这些checker函数的作用是调度介入这个阶段的所有http模块的handler方法,或者切换到一下个http请求阶段。下面分析下NGX_HTTP_POST_READ_PHASE,NGX_HTTP_PREACCESS_PHASE,NGX_HTTP_LOG_PHASE三个阶段的checker方法。
//NGX_HTTP_POST_READ_PHASE, //NGX_HTTP_PREACCESS_PHASE, //NGX_HTTP_LOG_PHASE三个阶段的checker方法 //返回值: NGX_OK,http框架会将控制权交还给epoll模块 ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r,ngx_http_phase_handler_t *ph) { ngx_int_t rc; //调用http模块的处理方法,这样这个http模块就介入到了这个请求阶段 rc = ph->handler(r); //跳转到下一个http阶段执行 if (rc == NGX_OK) { r->phase_handler = ph->next; return NGX_AGAIN; } //执行本阶段的下一个http模块 if (rc == NGX_DECLINED) { r->phase_handler++; return NGX_AGAIN; } //表示刚执行的handler无法在这一次调度中处理完这一个阶段, //需要多次调度才能完成 if (rc == NGX_AGAIN || rc == NGX_DONE) { return NGX_OK; } //返回出错 /* rc == NGX_ERROR || rc == NGX_HTTP_... */ ngx_http_finalize_request(r, rc); return NGX_OK; }到此http框架调度各个http模块共同完成http请求已经分析完了,
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]