【Nginx源码分析】Nginx的listen处理流程分析
施洪宝
一. 基础
- nginx源码采用1.15.5
- 后续部分仅讨论http中的listen配置解析以及优化流程
1.1 概述
- 假设nginx http模块的配置如下
http{
server {
listen 127.0.0.1:8000;
server_name www.baidu.com;
root html;
location /{
index index.html;
}
}
server {
listen 10.0.1.1:8000;
server_name www.news.baidu.com;
root html;
location /{
index index.html;
}
}
server {
listen 8000; #相当于0.0.0.0:8000
server_name www.tieba.baidu.com;
root html;
location /{
index index.html;
}
}
server {
listen 127.0.0.1:8000;
server_name www.zhidao.baidu.com;
location / {
root html;
index index.html;
}
}
}
- 端口, 地址, server的关系
- 端口是指一个端口号, 例如上面的8000端口
- 地址是ip+port, 例如127.0.0.1:8000, 10.0.1.1:8000, 0.0.0.0:8000, listen后配置的是一个地址。
- 每个地址可以放到多个server中, 例如上面的127.0.0.1:8000
总而言之, 一个端口可以有多个地址, 每个地址可以有多个server
1.2 存在的问题
- 是否需要在读取完http块中所有的server才能建立监听套接字, 绑定监听地址?
- 是的, 因为允许配置通配地址, 故而必须将http块中的server全部读取完后, 才能知道如何建立监听套接字。
- 一个端口可以对应多个地址, 如何建立监听套接字, 如何绑定地址?
- 通常情况下, 每个地址只能绑定一次(只考虑tcp协议), 这种情况下, 我们只能选择部分地址创建监听套接字, 绑定监听地址。
- 当配置中存在通配地址(0.0.0.0:port)时, 只需要创建一个监听套接字, 绑定这个通配地址即可, 但需要能够依据该监听套接字找到该端口配置的其他地址, 这样当客户端发送请求时, 可以根据客户端请求的地址, 找到对应地址下的相关配置。
- 当配置中不存在通配地址时, 需要对每个地址都创建一个监听套接字, 绑定监听地址。
- 一个地址多个server的情况下, 如何快速找到客户端请求的server?
- 比较合适的方案是通过hash表。
- 为了快速找到客户端请求的server, nginx以server_name为key, 每个server块的配置(可以理解为一个指针, 该指针指向整个server块的配置)为value, 放入到哈希表。
- 由于server_name中可以出现正则匹配等情况, nginx将server_name具体分为4类进行分别处理(www.baidu.com, *baidu.com, www.baidu*, ~*baidu)。
1.3 nginx listen解析的流程
总体而言分为2步,
- 将所有http模块内的配置解析完成, 将listen的相关配置暂存(主要存储监听端口以及监听地址)。
- 根据上一步暂存的监听端口以及监听地址, 创建监听套接字, 绑定监听地址
二. 配置解析
nginx http块解析完成后, 会存储配置文件中配置的监听端口以及监听地址, 其核心结构图如下,
总体而言, 结构可以分为3级, 端口->地址->server
2.1 源码
listen的处理流程:
- ngx_http_core_listen: 读取配置文件配置
- ngx_http_add_listen: 查看之前是否出现过当前监听的端口, 没有则新建, 否则追加
- ngx_http_add_address: 查看之前该端口下是否监听过该地址, 没有则新建, 否则追加。
- ngx_http_add_server: 查看server之前是否出现过, 没有则新建, 否则报错(重复定义)。
三. 创建监听套接字
nginx最终创建的监听套接字及其相关的结构图如下,
- 每个ngx_listening_t结构对应一个监听套接字, 绑定一个监听地址
- 每个ngx_listening_t结构后面需要存储地址信息, 地址可能不止一个, 因为这个监听套接字可能绑定的是通配地址, 这个端口下的其他地址都会放在这个监听套接字下。例如, 1.1节的配置中, 只会创建一个ngx_listening_t结构, 其他地址的配置都会放到这个通配地址下。
- 每个监听地址可能对应多个域名(配置文件中的server_name), 需要将这些域名放到哈希表中, 以供后续使用
总体而言, 结构分为3级, 监听套接字->监听地址->server
3.1 源码
读取完http块后, 需要创建监听套接字绑定监听地址, 处理函数ngx_http_optimize_servers, 该函数的处理流程:
- 遍历所有监听端口, 针对每个监听端口, 执行以下3步
- 对该端口下所有监听地址排序(listen后配置bind的放在前面, 通配地址放在后面)
- 遍历该端口下的所有地址, 将每个地址配置的所有server, 放到该地址的哈希表中。
- 为该端口建立监听套接字, 绑定监听地址。
四. 监听套接字的使用
- 假设此处我们使用epoll作为事件处理模块
- epoll在增加事件时, 用户可以使用epoll_event中的data字段, 当事件发生时, 该字段也会带回。
- nginx中的epoll_event指向的是ngx_connection_t结构, 事件发生时, 调用ngx_connection_t结构中的读写事件, 负责具体处理事件, 参见下图。
//c is ngx_connection_t
rev = c->read;
rev->hadler(rev);
wev = c->write;
wev->handler(wev);
- 每个监听套接字对应一个ngx_connection_t, 该结构的读事件回调函数为ngx_event_accept, 当用户发起tcp握手时, 通过ngx_event_accept接受客户端的连接请求。
- ngx_event_accept会接受客户端请求, 初始化一个新的ngx_connection_t结构, 并将其加入到epoll中进行监听, 最后会调用ngx_connection_t对应的ngx_listening_t的处理函数(http块对应ngx_http_init_connection, mail块ngx_mail_init_connection, stream块对应ngx_stream_init_connection)
五. 总结
- nginx在读取listen相关的配置时, 将结构分为3级, 端口->地址->server, 各级都是一对多的关系。
- nginx在创建监听套接字时, 将结构分为3级, 监听套接字->地址->server, 各级都是一对多的关系。
Nginx源码分析
研读nginx源码
推荐阅读
【转发】来自西红柿(李乐)的《深入理解Go语言
作者:李乐 原文地址:[链接]第一章 Go语言快速入门  第一篇 基本语法  第二讲 数组与切片  第三讲 字符串  第四讲 哈希表MAP  第五讲 结构体与接口 &em...
LNMPRG源码研究赞 1阅读 1k
如何选择适合你的微服务 API 网关:对比 Kong、APISIX、Tyk、Apigee 和其他网关
API 网关并非一个新兴的概念,在十几年前就已经存在了,它的作用主要是作为流量的入口,统一的处理和业务相关的请求,让请求更加安全、快速和准确的得到处理。它有以下传统的功能:
API7_技术团队赞 8阅读 8.6k评论 2
Nginx 配置常用参数,看这一篇就够了
最近在全面学习Nginx,当作笔记了,如有错误,欢迎指出或深入交流。主模块 {代码...} 事件模块 {代码...} http部分 {代码...} 部分参数详细说明server_name {代码...} location {代码...} location表达式类型 {代...
开源到赞 2阅读 1.9k
化虹为桥 - Nginx 如何代理 UDP “连接”
众所周知,UDP 并不像 TCP 那样是基于连接的。但有些时候,我们需要往一个固定的地址发送多个 UDP 来完成一个 UDP 请求。为了保证服务端能够知道这几个 UDP 包构成同一个会话,我们需要在发送 UDP 包时绑定某个端...
spacewander赞 4阅读 1.6k
Nginx unexpected end of file 配置证书遇到问题,如何解决?
通过 letsencrypt 申请证书后,默认服务器安装了 Nginx 1.8 发现,在默认的 /etc/nginx/sites-enabled/default 内容配置 SSL 的 site.com.key 后。重启 Nginx 出现一下错误:
程序员泥瓦匠赞 1阅读 1.2k
mac M1 nginx配置文件位置
mac nginx配置的原位置/usr/local/etc/nginx/nginx.confmac M1 nginx配置位置/usr/local/Homebrew/etc/nginx/nginx.confnginx -t 查看位置
寿兽阅读 3k
(学习到实践)七、mongodb测试,php+nginx负载均衡的部署
从测试容器中匹配搜索得到 mongod.conf.orig,设置可以启动,网上查找配置项反不能启动,原因是配置是yaml格式!好像听说过。百度查询得到:官方配置说明,网站卡得出奇。
沧浪水阅读 2.7k
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。