You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ngx_int_tngx_cycle_modules(ngx_cycle_t*cycle)
{
/* * create a list of modules to be used for this cycle, * copy static modules to it */cycle->modules=ngx_pcalloc(cycle->pool, (ngx_max_module+1)
*sizeof(ngx_module_t*));
if (cycle->modules==NULL) {
returnNGX_ERROR;
}
// 将全部的modules都放到cycle->modules中// ngx_modules 是编译后自动生成的文件,里面引用了全部modulesngx_memcpy(cycle->modules, ngx_modules,
ngx_modules_n*sizeof(ngx_module_t*));
cycle->modules_n=ngx_modules_n;
returnNGX_OK;
}
〉 The len field holds the string length and data holds the string data. The string, held in ngx_str_t, may or may not be null-terminated after the len bytes. In most cases it’s not. However, in certain parts of the code (for example, when parsing configuration), ngx_str_t objects are known to be null-terminated, which simplifies string comparison and makes it easier to pass the strings to syscalls
工作中涉及Nginx module开发,分析Nginx/tengine代码
nginx modules初始化
每个 module 必须有 ngx_module_t,且在module不同生命周期都支持 hook 函数
由于 ngx_module_t 为基础类型,对于http和stream,通过 ngx_module_t#ctx进行了扩展,用于放置 http/stream module相关的 hook 函数
比如http module中,ctx 为 ngx_http_module_t 类型
stream module 对 module ctx 扩展
nginx cycle 是最上层的大类,存放全部nginx运行上下文,其中
cycle->modules 存放全部的module,包含http_core modules,第三方http modules, event modules 等
各个hook的调用时机
http://nginx.org/en/docs/dev/development_guide.html#core_modules
init_module
master 进程在每次配置初始化之后调用
init_process
master进程创建完worker后,worker中调用。所以init_process在worker的上下文中
exit_process
worker 退出时调用
exit_master
master退出时调用
module自身配置的创建与解析module指令command
我发现 nginx 遍历全部modules的代码挺多的,为了实现某个功能,经常要遍历全部modules
conf 字段决定set函数的第三个参数传递什么,如果是 NGX_STREAM_SRV_CONF_OFFSET,则nginx会将此module的srv_conf 作为
void *conf
传入set 回调函数传递我们拿一个set函数举例
ngx_conf_t是在nginx启动后创建的变量,用于配置解析和调用module hook时记录当前配置解析的状态上下文。
http://nginx.org/en/docs/dev/development_guide.html#http_conf
main_conf vs srv_conf vs loc_conf
对于http 模块
main_conf: 应用于整个http block,属于module的全局http配置
srv_conf: 应用于单独一个server block,属于单独某个server块的配置
loc_conf: 应用于单独一个location,属于单独某个location块的配置
所以 main_conf 只有一个,而 srv_conf, loc_conf 都有多个。
所以 create_main_conf 只被调用一次, create_srv, create_loc 可被调用多次,取决于配置中有多少个 server,location block
对于 stream 模块
main_conf: 应用于整个stream block,属于module的全局stream配置
srv_conf: 应用于单独一个server block,属于单独某个server块的配置
所以 main_conf 只有一个,而 srv_conf都有多个。
所以 create_main_conf 只被调用一次, create_srv_conf 可被调用多次,取决于配置中有多少个 server block。
比如
由于有
NGX_STREAM_UPS_CONF
标记,配置解析到foo,bar时,分别都会调用module的create_srv_conf配置merge
http://nginx.org/en/docs/dev/development_guide.html#http_conf
以
create_loc_conf
为例,对于module有 create_conf和merge_conf的hook,nginx首先调用 create_conf,此时module创建一个空conf返回给nginx,在merge阶段nginx调用 merge_conf hook,将parent context conf传过来,module就可以拿到配置文件初始化比如下面的合并server block,
cscfp[s]->ctx->srv_conf[ctx_index]
是新创建的 conf,saved.srv_conf[ctx_index]
是对于http block,最关键的是
http block 中开始初始化 http module,
merge_loc_conf
需要location merge是因为 location 可以嵌套
# main context server { # server context location /match/criteria { # first location context } location /other/criteria { # second location context location nested_match { # first nested location } location other_nested { # second nested location } } }
Http 请求处理
Phase
Nginx将请求分成如下几个阶段,请求到来后,按照顺序分别执行每个阶段的handlers
由于nginx支持module插件的形式,必然要有一种机制,当请求来后遍历执行插件,可以在中间某个插件停止处理
Koa 使用洋葱模型,nginx则while迭代handler,handler里决定是否
r->phase_handler++
指向下一个handler,如果handler返回NGX_OK
,则请求处理结束http core module 初始化阶段会将不同module挂载到不同phase上,如下
Stream module hook 调用顺序
先看一个第三方 stream module的 ctx 结构
调用顺序
创建server块的module 配置。举个例子:stream block下会有server block,此时会调用 create_srv_conf,放到 srv_conf 下的module对应的数组索引中。upstream block下也有 server block,也会调用 create_srv_conf,放到 srv_conf 下的module对应的数组索引中。所以 create_srv_conf 会在stream block和upstream block都调用。
到此 nginx 已经完成 upstream,server 块、module 的command/directive 解析。由于每个command都有set hook,set里都已经将value放到了之前创建的 conf 中
// 配置初始化完成后,下面开始调用 ngx_module_t 上的 module 级别的hook
nginx 将 module 自己创建好的 conf 传进来,module 初始化自身配置
每次配置文件reload,init_module都会被重新掉用,传进来 cycle,cycle->old_cycle,用于生成一个新cycle。最后创建好的cycle放在全局变量中,fork 新worker process后,cycle 会自动映射到worker process 地址空间,访问时写时拷贝。
只在 worker process 调用。请求不经过master直接到worker,此hook可以执行module的业务逻辑,初始化处理请求相关的配置。比如
nginx-stream-upsync-module
,在 init_process 里请求consul,etcd,获取下游ipport列表,放在worker自己的变量中worker退出调用
master退出时掉用,优雅退出
Stream 请求处理
Phase
如下几个阶段,顺序执行每个阶段下的handlers
http://nginx.org/en/docs/stream/stream_processing.html
stream upstream module 分析与负载均衡逻辑
upstream作为nginx的LB模块,承担着在server中流量均分的重任。
分析upstream module中,必然绕不开以下几个结构
通过 upstream block 对齐到上面类型
ngx_stream_upstream_srv_conf_t 存放 upstream block 级别的相关配置
ngx_stream_upstream_server_t 存放 upstream 单独一个server block配置,含有
weight, max_fails
等配置ngx_stream_upstream_rr_peers_t
,ngx_stream_upstream_rr_peer_t
与 round robin 相关peer_t 记录LB相关参数,比如已经连接到此下游的连接数,下游响应时间,用于进行LB策略
peers_t#next参数和upstream 的backup指令有关,记录此peers_t的backup servers
当peer_t down,peers_t通过next找到此peer_t的backup peers,再通过 backup peers 找到 backup peer_t
ngx_stream_upstream_rr_peers_s 结构注释
ngx_stream_upstream_rr_peer_s 结构注释
部分nginx struct剖析
ngx_command_t
nginx回调set(ngx_http_dubbo_pass),并根据
.conf(NGX_HTTP_LOC_CONF_OFFSET)
,设置 cf->ctxoffset 只有和 set 配合才有意义。nginx有很多需要将配置项存放到结构体的需求,所以nginx提供了形如 ngx_conf_set_foo_slot 的函数,用于批量解决此需求。
这些ngx_conf_set_foo_slot需要知道将value放到结构体的哪个位置,因此在ngx_command_t上就须填写offset,由nginx计算位置并存放。
当然你完全可以将offset置为0,但这意味着需要针对每条directive都编写set,将
void * conf
转换成具体的结构体类型,通过a->b
设置。比如nginx提供的一个函数,通过
cmd->offset
获取要set的struct成员并赋值ngx_conf_t
重点说下
void* ctx
ngx_conf_s 在配置解析过程中使用,module type定义了 ctx 存放的类型。上述 ngx_stream_upsync_module 为 stream module,所以解析stream block时,包括 stream upstream 下,ngx_conf_t#ctx总是指向 stream 的 ctx 配置
ngx_stream_conf_ctx_t
ngx_str_t
len的长度不一定包含
\0
,如果ngx_str_t
由字符串常量初始化而来,比如ngx_string("hello")
,sizeof("hello")
为 5+1,而此时len却是5,确保一定是null结尾,方便传给syscall〉 The
len
field holds the string length anddata
holds the string data. The string, held inngx_str_t
, may or may not be null-terminated after thelen
bytes. In most cases it’s not. However, in certain parts of the code (for example, when parsing configuration),ngx_str_t
objects are known to be null-terminated, which simplifies string comparison and makes it easier to pass the strings to syscallsModule如何注册到Phase,参与请求的处理
上文说了 http core module 会在http module初始化中注册到phase,而第三方module则会在
http://nginx.org/en/docs/dev/development_guide.html#http_phases
指令directive
http://nginx.org/en/docs/dev/development_guide.html#config_directives
master发送signal
https://github.com/taikulawo/nginx-debugable/blob/8a30c4b12226c92cd317db614172cc6de2795768/src/os/unix/ngx_process_cycle.c#L139
worker process 接受 master process signal
master, worker signal 处理代码都在一起
worker listen socket 解决listen冲突
Linux Kernel 支持 SO_REUSEPORT,允许多process accept 同一个socket,socket将按照一定的算法传给不同process
内存池
pool并不是说需要内存都从pool里申请,而是在进行一系列操作的时候发现需要频繁申请内存,这时候可以
ngx_create_pool
申请pool,之后这些操作都在此pool中进行。全部操作都完成后,调用
ngx_destroy_pool
就可以。所以 ngx_pfree 并不会释放小内存块比如我们有个逻辑,heap上存了指向其他heap的pointer,如果为了释放这些内存,就需要每个字段都调用free,如果后期涉及到这里的更改,就必须添加上心字段的free,维护成本很高
而如果采用pool,逻辑都从pool里分配,只需要在逻辑开头create pool,在逻辑结束后destroy pool就可以,非常好用
http请求运行流程
https://github.com/willdeeper/tengine/blob/6efc27b8b955201eaad76f76554d0aaa40dac45b/src/http/ngx_http.c#L1729-L1735
https://github.com/willdeeper/tengine/blob/3d65186c980559f4a76f0a48ec19933a76834bfc/src/http/ngx_http_request.c#L332
ngx_http_wait_request_handler 函数中从socket读取request
https://github.com/willdeeper/tengine/blob/3d65186c980559f4a76f0a48ec19933a76834bfc/src/http/ngx_http_request.c#L450
http 调用栈
从socket一次recv可能不是完成的http request,就需要暂存已有buffer,并返回,下一次recv从上一次结束处重新开始
当涉及到 NGX_AGAIN,nginx往往都会创建一个struct,将buf保存起来,并保证此函数重入是正确的
比如下面代码
worker_connections
worker可以处理的最大连接数,包含client和upstream的连接
比如 client => nginx => upstream模型
消耗两个connection
给资源受限的环境准备,现代服务器应尽可能大,比如 10k
整个nginx接受最大连接数为
worker_processes * worker_connections
健康检查
nginx可以编译 health check 模块,定期发送HTTP HEAD,确定connection正常使用
nginx 内置的connection检查
The text was updated successfully, but these errors were encountered: