之前两篇文章已经说明了过程,今天稍微把过程说细一点,毕竟知其然还要知其所以然嘛,整个调用的逻辑是怎完整的呢?
其实上两篇文章看似简单的将nginx处理一个请求的过程说出来了,但实际过程一点也不简单,一个连接处理的过程,主要是复杂在准备阶段(也就是各种回调函数的挂载,上下文的准备,从各种池内申请资源等等),一个http连接的准备过程分为两个部分,一个是http部分,一个event部分。
得益于nginx的模块化设计,用nginx做模块化开发很方便,但是也带来了代码复杂,难以读懂的问题,首先看一下http模块是怎么准备的,回忆下上一篇文章我们提到的真正处理http请求的函数(或者说是handler,句柄)是ngx_http_wait_request_handler对吧。
rev->handler = ngx_http_wait_request_handler;
以这个函数为例,我们来看看http模块是怎么将这个handler挂载好的,如下图
ngx_http_block()则就是一个典型的nginx中的module函数了,当整个模块加载的时候就会调用这个函数(后续讲nginx模块开发的时候我会细讲调用过程,这里就先把它记住吧)。
挂好了handler,什么时候去调用这个handler呢?这就要靠nginx的event模块了,event模块实现的功能就是将用来accept的fd注册到epoll中,等有client的请求到来,生成一个新的connfd,然后从连接池里拿出一个连接,将这个连接初始化(也就是把刚刚我们的读写事件的回调等东西写进连接中),然后一并注册到epoll中去,这样,只要这个connfd就绪,就可以根据fd的读写状态调用其相应读写事件的handler了。来看下event模块是怎么初始化然后监听用来accept_fd的:
绕来绕去,不知道你昏没有,我这个讲的人都有点昏了,IO复用加回调机制确实有够反人类的,为此go,python等引入了coroutine(协程)的概念,就是想取消掉这种反人类的东西,这就是后话了(不过最近看了c语言实现的一个协程,比回调还要恶心的多。。。。)