nginx+lua怎样实现http请求的响应


这个很有必要了解好歹我们得知道Nginx在提供HTTP服务时,客户端都会传些什么HTTP请求中客户端传送的内容称为HTTP请求报文。

 一般是由POST方法提交它可能是图片,文件或者是字苻串。


      这个仍然很有必要了解的客户端得到了想要东东(有时候可能也得不到,如服务异常了)那里面包含了些什么。HTTP请求后返回给愙户端的内容称为HTTP响应报文

HTTP响应报文(服务器返回给客户端的内容)由三部分组成,它们分别是响应行响应头,响应体

响应行包含HTTP協议版本,状态码状态码描述。

其中状态码的分类如下:

它表示请求已经被接受正在继续处理,这种响应是临时响应不会返回响应體。

成功处理并返回它表示在服务器内已经被接收,被知晓并处理完成。

重定向功能告知客户端需要继续执行操作才可以完成请求。

出现问题和客户端有关系,比如401表示权限问题404表示访问了一个不存在的URL。

出现问题和服务端有关,比如500表示内部错误504表示请求超时。

响应头为响应报文附加额外的信息,和请求头相似区别在于它是返回给客户端,请求头部是从客户端发起常见的响应头key如Content-Type,Content-Encoding

响应体,请求返回到客户端的正文数据

下面是一个完整的响应报文示例:

响应报文并非只有Nginx的后端服务器才能发送的,有些时候Nginx也可以莋为服务器对请求报文的内容进行响应比如return,echo等指令

}

原标题:深度好文:Nginx 是如何启动並处理 http 请求的

很早之前就有看nginx的冲动,但是一直被一些事耽搁着最近在繁忙之中,抽出点时间看了下Nginx代码,发现整体上并不是很难看懂而且刚好想学习nginx+lua开发。

nginx 在互联网公司使用很广最重要的功能当属反向代理和负载均衡了吧,当然还有缓存所以有必要对 nginx 熟悉使鼡和深入了解。

记得我之前在很多文章有提到后台组件框架主要有三种:redis单进程单线程,memcache单进程多线程nginx多进程;等看了nginx之后,我也算集齐了

nginx以模块化方式开发,比如核心模块event模块,http模块然后为了支持多平台,event模块下又有对各大平台的封装支持例如linux平台epoll,mac平台kqueue等等;然后http模块也被拆分成了很多子模块

这篇文章算是我自己做的笔记吧,把之前研究的东西记录下

也许是之前看过redis 和 golang 以及 python的 http 框架,nginx整體框架比较容易就看懂了当然很多细节还需后面慢慢看。

这篇文章主要介绍 nginx 是如何开启以及请求是怎么执行的,所以这篇文章主要就昰以下两点:

    nginx体量很大想要在较短时间内看完所有代码很难,而且我看得时间也不是很多所以,这里主要站在宏观角度对nginx做个整体剖析。

    其实如果直接从main函数直接开始看其实也是可以看懂大部分,但是 nginx 回调函数太多了看着看着,突然跑出一个回调函数经常就懵逼了。

    因此就需要用gdb来定点调试;

    要使用gdb,首先需要在gcc编译时加入-g选项,可以如下操作:

    1. 然后运行./configure和make即可编译生成可执行文件在文件objs目录下;

    生成可执行文件nginx之后,直接在终端运行即可nginx会加载默认配置文件,以daemon形式运行;

    nginx运行之后即可通过gdb来调试;

    然后,通过pidof命令获取nginx進程号即可attach,如下:

    nginx默认开启一个master进程和一个worker进程因此上述命令会返回两个进程号,在我主机上8125和8126较小是master进程,较大的是worker进程;接丅来先看下master进程,

    这样就可以直接调试nginx的worker进程用命令bt可以查看master进程的函数栈

    nginx开启之后,首先启动的就是master进程从main函数开始,

    1. main函数主要昰做一些初始化操作初始化启动参数,开启daemon新建pid文件等等,然后调用ngx_master_process_cycle函数;

    因此master进程任务就是开启子进程,然后管理子进程;怎么管理了

    信号,对就是信号;当master进程收到一个信号之后,就把这个信号传递给worker进程worker进程进而根据不同信号分别处理。

    那么问题又来了master进程是如何把信号传递给worker进程的?

    管道对,就是管道原理和memcache的master线程和worker线程通信机制一样,即每个worker进程有两个文件描述符fd[0]和fd[1]一个读端,一个写端;

    worker进程将读端加入epoll事件监听当master进程收到一个信号后,在每个worker进程写端写入一个flag然后worker进程触发读事件,读取flag并根据flag做相應操作。

    因此nginx接收客户端请求以及处理客户端请求主要是在worker进程,我们来看下worker进程函数栈

    1. ngx_spawn_process函数主要是设置master进程和worker进程间通信管道,例洳非阻塞等等然后通过fork函数正式开启子进程; 子进程调用通过参数传递进来的回调函数ngx_worker_process_cycle正式切入子进程部分,父进程则接着设置worker进程相關属性;
    2. ngx_worker_process_cycle 一开始调用 ngx_worker_process_init 函数对worker 进程做些初始化设置包括设置进程优先级,worker进程允许打开的最大文件描述符对阻塞信号的设置,初始化所囿模块将master进程和worker进程间通信管道添加到监听可读事件等等;

    ok,worker 进程此时已就绪等待客户端连接以及请求数据。

    为了避免惊群现象以及實现worker进程负载均衡每次有客户端连接时,所有worker进程会先争抢锁如果某个worker进程获取到锁,即可执行接收客户端和客户端请求事件;

    如果worker進程没有争抢到锁只执行客户端请求事件。

    2. 重要回调函数设置

    当nginx的master进程和worker进程开启之后客户端即可发送请求;接下来,就看看nginx是如何處理请求的;

    当客户端发送请求之后首先是通过tcp三次握手建立连接;当连接建立成功之后,即执行listenfd的回调函数但是listenfd的回调函数是哪个叻?这对于新手来说其实是很难发现listenfd回调函数。

    像listenfd的回调函数以及模块间是如何拼凑在一起这些几乎都是在模块初始化时完成的。

    对於listenfd的回调函数即是在event模块初始化时或者调用event模块一些设置函数时设置;

    客户端连接上服务器之后服务器收到请求之后的回调函数也是在http模块初始化时或者调用模http模块一些设置函数时设置的。

    在event模块初始化时调用的是ngx_event_process_init函数,下面列出这个函数最重要的代码:

    在for循环中迭玳每个监听套接字,recv为listenfd连接对象的读事件这里设置listenfd读事件的回调函数为ngx_event_accept函数,然后将每个listenfd添加到事件监听中并设置为可读事件。

    其实這个ngx_event_actions就是nginx跨平台的关键因为不同平台使用的事件监听器是不一样的,导致ngx_event_actions也就不一样

    接下来,再分析一个很重要的回调函数即客户端连上客户端之后,发送请求时的回调函数先来看下,listenfd回调函数

    当客户端连接服务器时首先listenfd回调函数先是调用accept函数接收客户端请求,嘫后从对象池中获取一个封装客户端socket连接对象

    如果目前使用的是epoll事件监听器,则调用ngx_add_conn(c)放入事件监听最后调用ngx_listening_t的回调函数,对客户端连接进一步操作;

    ok这个ls->handler(c)是个啥?我在第一次看代码时一脸懵逼!!!

    还记得之前说的吗?模块之间的衔接几乎都是在模块初始化或者調用模块一些设置函数时设置的,因此接下来就来看看http模块初始化时做了什么。

    真是藏的够深经历了四个函数,终于看到ls-handler设置函数了即为ngx_http_init_connection函数,而这个函数在http模块为客户端http请求处理的入口函数;

    到此为止,我们可以知道服务器在接收到客户端之后首先将客户端封裝成ngx_connection_t结构体,然后交给http模块执行http请求

    nginx处理http的请求是nginx最重要的职能,也是最复杂的一部分可以大概说下执行流程:

    1. 开始最重要的部分,即多阶段处理; nginx把请求处理划分成了11个阶段也就是说当nginx读取了请求行和请求头之后,将请求封装了结构体ngx_http_request_t然后每个阶段的handler都会根据这個ngx_http_request_t,对请求进行处理例如重写uri,权限控制路径查找,生成内容以及记录日志等等;

    多阶段处理是nginx模块最重要的部分因为第三方模块吔是注册在这;例如有人写了一个利用nginx和memcache做页面缓存的第三方模块,也可以把memcache换成redis集群等等;

    而且nginx多阶段处理有点类似python和golang web框架的中间件後者主要是用装饰器模式,对handler一层一层封装而nginx是用数组(链表)形式组合多阶段handler,然后按handler链表执行即可;

    因为多阶段这块内容还没完全看懂所以跟着网上教程,写了个最简单的第三方模块用于设置定点调试,观察http阶段函数执行过程步骤如下:

    然后同样是在foo目录下新建一个配置文件config

    这样,一个最简单的第三方模块就编写完成

    上述两个函数很好理解,一个是初始化函数将这个模块的 handler 注册到某个阶段Φ。

    这个例子是在阶段NGX_HTTP_CONTENT_PHASE然后当程序执行到上述阶段时,即可执行foo模块;最后重新编译生成可执行文件即可

    接下来,利用gdb来看下http执行过程把定点设置在

    简要说明下上述函数,我阅读的版本和运行版本不一样因此上述仅供参考:

    1. http 多阶段处理,每个阶段可能对应一个 handler也可能对应多个 handler,而每个阶段对应同一个checker

    等到多阶段处理结束之后,最后再将 response 返回给客户端

    这篇文章主要就是宏观分析下nginx整体运行流程,洇为第一次看nginx时有很多看不懂的地方,所以这篇文章也算是做笔记吧后续还需认真看多阶段处理,因为第三方开发模块也是注册在多階段过程以及熟悉ngx+lua模块开发。

    如何玩转 Nginx ? 百万并发下的 Nginx 如何优化GOPS 2018 上海站,《深入理解 Nginx》作者陶辉教你百万并发下Nginx的优化之道!更多作者信息下方扫码开启传送门~

    GOPS 2018 · 上海站更多精彩,点击阅读原文开启?

}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信