游客发表
翻到《nginx底层设计与源码分析》的层设第三章,我盯着`ngx_http_process_request_line`函数看了快半小时。计源这本书最狠的码分地方在于,它不跟你绕弯子,层设直接拿`src/http/ngx_http_request.c`里的计源代码说事。比如处理HTTP请求行那段,码分nginx用了一个状态机来逐字节解析,层设`r->state`在`ngx_http_parse_request_line`里来回跳转,计源从`sw_start`一路走到`sw_check_uri`。码分这种写法在应用层代码里少见,层设但在nginx里遍地都是计源——它压根不信任任何外部输入,每个字节都得自己过一遍。码分
书里配的层设调用图挺实用。我照着画了一遍`ngx_http_wait_request_handler`到`ngx_http_process_request`的计源路径,发现从epoll拿到可读事件到真正开始处理请求,码分中间隔了至少四层回调。nginx的模块化不是嘴上说说,`ngx_http_top_request_body_filter`这种指针数组串起来的过滤器链,读源码时容易迷路,但作者把每个filter的注册时机标出来了,对着看能省不少时间。
内存池那块讲得比官方文档细。`ngx_palloc`和`ngx_pnalloc`的区别,书里直接拿`ngx_http_alloc_large_header_buffer`当例子——大header缓冲区不够时,nginx会重新分配一块,旧数据用`ngx_memcpy`搬过去。我拿gdb在测试环境断点跟过一次,确实跟书里写的一样,`r->header_in`指向新内存后,`r->header_end`的偏移量要重算。这种细节,光看代码注释根本注意不到。
作者对upstream模块的分析有点意思。他拿`ngx_http_upstream_init_round_robin`开刀,说轮询均衡的权重计算实际是在`ngx_http_upstream_get_round_robin_peer`里做的。我翻回源码对照,发现`rrp->current`的递增逻辑确实藏在那,而且当权重不同时,`n`和`total`的比值决定了选哪个peer。书里没提的是,如果后端全挂了,`ngx_http_upstream_get_round_robin_peer`会返回`NGX_BUSY`,然后触发`ngx_http_upstream_next`重试——这个分支在线上环境出问题时特别有用。
缓存部分写得稍微糙了点。`ngx_http_file_cache_open`里那个`ngx_shmtx_lock`的争用场景,书里只提了一句“可能阻塞”,实际压测时如果缓存key冲突多,worker进程会卡在自旋锁上。我拿perf top看过,`ngx_shmtx_lock`的CPU占比能到8%左右。这点作者没展开,算个小遗憾。
最后几章讲事件模块,`ngx_epoll_process_events`的`nevents`上限设置,书里建议按worker_connections的1/2来调。我试过在4核8G的机器上把`nevents`从512拉到1024,QPS没明显变化,但`epoll_wait`的返回耗时从0.3ms降到了0.2ms。这种微调,没源码底子根本不敢动。
整本书的代码片段都标了行号,对着GitHub上的nginx-1.24.0源码看,基本能对上。唯一要吐槽的是,有些结构体定义在头文件里,书里只给了成员名没给完整定义,得自己翻`ngx_http_request.h`。不过话说回来,能对着源码逐行拆nginx的书本来就不多,这本算把关键路径都覆盖了。
友情链接