本文主要介绍了stream流程的:模块启动、accept、read/write
20241021 22:59 遗留问题:在哪里写回响应,是不是写回响应也是一个handler,而不是单独的函数?今天下班了。已解决,就是在handler中处理
1:stream处理流程
upstream和downstream:downstream代表客户端,upstream代表后端服务。客户端发来一个请求到nginx,最先到到downstream流程代码,然后downstream流程把请求转发给backendserver,当backendserver处理完后会发消息给nginx,此时与nginx通信的就是upstream,nginx收到后通过upstrema流程发给client
1-1:模块启动和listenfd注册到epoll流程
总结下来流程就是:1:解析配置文件,边读边解析,递归处理嵌套配置块,不管是最顶层的还是嵌套的配置块,一个配置块都对应一个命令,每个命令都包含一个set函数,每读取到一个配置块就调用命令的set函数来解析该配置块
nginx.main
ngx_module.ngx_preinit_modules #给模块编号,ngxin_modules是一个全局数组,保存了所有模块,每个元素是一个ngx_module_t结构体
#每个ngx_module_t结构体内含有一个ngx_command_t数组和ngx_core_module_t变量,
#这两个变量都是是全局变量,ngx_modules标识一个模块,ngx_command_t标识一个模块支持哪些模块命令
#ngx_core_module_t变量是模块的上下文变量,包含了一系列函数指针
#ngx_module_names是一个module名字数组
#!!!ngx_modules保存的是模块对应的信息,ngx_command_t是一个数组,可以包含多个command,
#!!!每个command对应一个配置块,command的名字就是配置块的名字,
#!!!解析一个配置块就是调用对应command的set函数
#!!!一个配置块与他内部嵌套的配置块对应的配置命令可能会放在不同的模块内。
#就是说一个http模块,对应配置为http{ server{},servername=xx,},
#http配置块对应的命令放在http_modules模块中
#但是server配置块的信息却放在http_core_modules块中,最顶层的块一般只有一个命令,
#主要是用来标识模块,但是块内的嵌套块却可以有多个,因为一个模块内可以有多个配置块
#每个配置块对应一个模块,如上所示,http是最顶层的块,标识检测到了一个http模块
#而server和servername则是http模块内的两个嵌套块(server嵌套块还可以有嵌套块)
#一旦解析到http块就会调用http模块的http命令的set函数(http模块只有一个http命令)
#一旦解析到server块或者servername块,就会调用ngx_http_core_module中对应命令的set函数
for (i = 0; ngx_modules[i]; i++) {
ngx_modules[i]->index = i;
ngx_modules[i]->name = ngx_module_names[i];
}
ngx_cycle.ngx_init_cycle
ngx_conf_file.ngx_conf_parse #解析conf文件,根据conf文件里的配置块来调用对应模块命令的set
for ; ; #for conf文件的每一个配置块,调用对应模块的对应命令的set函数
ngx_conf_file.ngx_conf_read_token #读取一个token
ngx_conf_file.ngx_conf_handler #就是处理块比如workProcessor块、http/stream模块下面举例
if name== modules[i]->commands->name: #name就是配置块的名字,modules[i]->commands就是模块对应的命令,
#commands->name就是该命令对应的名字
#也就是说nginx根据conf文件中块的名字来匹配对应的模块以及调用对应的模块命令的set函数
#备注:一个模块可以有多个命令
cmd=modules[i]->commands
cmd->set #cmd->set就是各个模块对应的set函数,不同的模块对应不同的函数
#比如stream模块就调用ngx_stream_block,http就用ngx_http_block
#!!!也就是说nginx用函数指针来实现了一种多态的效果
#!!!也就是说可以用同一套代码流程来加载http、stream模块
1: worker_processes 块
nginx.ngx_set_worker_processes #对应配置文件里的 worker_processes 配置块
2:event块
ngx_event.ngx_events_block
3: http块,这是最顶层的http 块
ngx_http.ngx_http_block #http模块set函数,包括很多操作,比如初始化好几张表结构(如loaction)等
#block是块的意思,不是阻塞的意思
ngx_conf_file.ngx_conf_parse #递归处理http嵌套块,ngxin是递归嵌套解析的,比如http里的server块
ngx_conf_file.ngx_conf_handler
ngx_http_core_module.ngx_http_core_server #这里以server块为例
...略...
ngx_http.ngx_http_init_phases #回到这里表明嵌套块都处理完了(递归特点决定的),可以进行一些操作了
#http的处理阶段分为11个阶段,这里就列举其phases的初始化和server对象的创建,其他过程略
#这里给每个阶段的handler数组分配空间 ,就是说有很多个阶段,并且每个阶段的handler可以不止一个
ngx_http.ngx_http_init_phase_handlers #!!!给每个阶段都设置对应的checker和handler, 并平铺到一个数组phase_handler中
#举例:pi,ci,hj分别表示:
#第i个阶段,第i个阶段对应的checker,第i个阶段对应的第j个handler
#最终数组为:phase_handler={c0-h0,c0-h1,c1-h0,c1-h1,c1-h2,c2-h0,.....}
#这样处理请求的时候就只要一个for循环就可以调用所有阶段的所有handler
#checker与handler:处理请求时,外部代码是调用checker,然后checker内部会调用handler,
#checker主要用于控制 HTTP 请求在不同阶段(phase)的处理逻辑
#通常是对 handler 的返回值进行检查或进一步处理。
#笔记:处理的时候默认是从handler数组的第0个元素开始调用handler,
#但是对于内部请求等一些场景,并不需要从0开始,可以直接从某个pos直接开始,比如rewrite_index
#一共有11个阶段,默认是一共有14个handler,即数组长度为14,可以百度,这里就不记录了
#可以直接搜索NGX_HTTP_POST_READ_PHASE这个枚举值就可以找到整个枚举,一共11个阶段
#笔记:elts表示数组基地址,nlts表示数组元素
ngx_http.ngx_http_optimize_servers #主要初始化listen socket相关结构
ngx_http.ngx_http_init_listening #!!!注意:只是初始化相关listen socket结构,并不会bind,
#因为此时还是初始化阶段,不能打开端口,所以bind操作不在这里
#等到conf处理完以及其他初始化工作都处理后才开启
ngx_http.ngx_http_add_listening
ls=ngx_create_listening
ls.handler=ngx_http_init_connection #!!!设置connection handler。
#!!!就是accept连接的时候accept_handler会初始化一个connection结构体
#!!!然后填充部分参数,然后调用connection_handler来处理新建立的连接
#!!!这里listenfd对新连接的处理函数设置为ngx_http_init_connection
#!!!stream模块中则设置为ngx_stream_init_connection
#!!!再次强调,ngx通过函数指针来模拟了多态,
#!!!也就是同一套代码可以同时处理httpfd和streamfd
4:stream块。流程和http模块一模一样,只是函数名字不同,比如ls.handler的名字分别为ngx_stream_init_connection和ngx_http_init_connection
ngx_stream.ngx_stream_block #cmd->set函数,即配置块对应的解析函数,这里处理conf中的stream块即stream 模块
ngx_conf_file.ngx_conf_parse #递归处理stream的嵌套块
ngx_conf_file.ngx_conf_handler
...略...
ngx_stream.ngx_stream_init_phases #同http模块,不过stream只有7个阶段
ngx_stream.ngx_stream_init_phase_handlers #同http模块
ngx_stream.ngx_stream_optimize_servers #同http模块
......
ngx_http.ngx_stream_add_listening
ls=ngx_create_listening
ls.handler=ngx_stream_init_connection #!!!设置connection handler。
#!!!就是accept连接的时候accept_handler会初始化一个connection结构体
#!!!然后填充部分参数,然后调用connection_handler来处理新建立的连接
#!!!下面的代码不管是http还是stream,都是同一个处理流程:注册到epollfd
#!!!因为nginx中http和strema模块的不同在于处理函数的不同,而nginx把不同的handler封装在event_t结构体中
#!!!而event_t结构体又包含在connection结构体中,clientfd存在epollfd中的就是connection结构,
#!!!也就是说不管是http还是stream,用的都是connection结构,而他们的区别只有字段值的不同
#!!!正因为如此,所以可以用同一个epollfd来同时处理httpserver-listenfd、streamserver-listenfd
#!!!、httpclientfd、streamclientfd、streamupstreamfd上的读写事件
ngx_connection.ngx_open_listening_sockets #ngx_init_cycle函数中处理完配置文件后就打开所有的listenfd
listenfd=ngx_socket #创建sokcet。前面只是初始化listenfd所需的各种结构,但是listenfd是没有初始化的
#这里创建fd后,直接复制给前面初始化的结构:xx.fd=listenfd
socket.bind #绑定端口
socket.listen #开启监听,也就是这时候可以接受连接。注意:是可以接受连接
#但是listenfd还没有注册到epoll中所以nginx不会accept连接,连接都会放到back队列
ngx_connection.ngx_configure_listening_sockets #对listenfd进行一些设置
ngx_model.ngx_init_modules #!!!前面只是根据配置文件中的信息来调用对应模块的set函数,set函数不是init函数
#因为是根据conf文件对模块进行设置,所以该函数指针才叫做set,这里就是init_modules模块
#ngx规定了每个许多模块都必须实现的函数,init_modules是其中一个
for modules->init_modules
ngx_process_cycle.ngx_master_process_cycle #main函数里调用这个,开始进入多进程
#!!!master主要开启子进程并监控子进程,master不负责接受连接和处理连接
ngx_process