Nginx的phase初始化

本文深入探讨Nginx中的Phase机制,详细介绍了11个Phase的定义与作用,包括读取请求、重写规则、权限验证等。同时,解析了Phase如何被初始化并组织成链表形式,以便按顺序调用。

在前面的文章,我们知道当Nginx获取到连接后,会首先对连接的请求进行处理,例如读取请求的uri,参数等,然后会调用ngx_http_core_run_phases函数对于该请求跑一遍phase,当所有的phase都跑完了以后,那么这个请求也就处理完成了。

好,我们首先来看一下Nginx的phase的定义吧:

//Nginx定义的11个phase
typedef enum {
    NGX_HTTP_POST_READ_PHASE = 0,    //读取请求的phase

    NGX_HTTP_SERVER_REWRITE_PHASE,  //这个阶段主要是处理全局的(server block)的rewrite

    NGX_HTTP_FIND_CONFIG_PHASE,   /* 根据uri查找location */  
    
    NGX_HTTP_REWRITE_PHASE,   /* localtion级别的rewrite */  
    
    NGX_HTTP_POST_REWRITE_PHASE,   /* server、location级别的rewrite都是在这个phase进行收尾工作的 */  

    NGX_HTTP_PREACCESS_PHASE,  /* 粗粒度的access */  

    NGX_HTTP_ACCESS_PHASE,   /* 细粒度的access,比如权限验证、存取控制 */  
    
    NGX_HTTP_POST_ACCESS_PHASE,   /* 根据上述两个phase得到access code进行操作 */  

    NGX_HTTP_TRY_FILES_PHASE,    /* 实现try_files指令 */  
    
    NGX_HTTP_CONTENT_PHASE,    /* 生成http响应 */  
   
    NGX_HTTP_LOG_PHASE    /* log模块 */  
} ngx_http_phases;
然后我们在ngx_http_core_main_conf_t结构中可以看到如下的定义:
   /** 
     * 所有的phase的数组,其中每个元素是该phase上注册的handler的数组。 
     */  
    ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];
phases静态数组保存了所有的http类型的模块定义的phase处理函数,这里我们看一下ngx_http_phase_t结构:
//phase的定义
typedef struct {
    ngx_array_t                handlers;  //处理函数
} ngx_http_phase_t;
其实说白了每一个phase就对应一个数组,用来保存该phase的所有处理函数。我们首先来看phases数组的是如何初始化的,在http命令的回调函数ngx_http_block中我们可以看到如下代码:
    if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {  //因为nginx把http分化成为11个phase,每一个phase都可以注册handler,ngx_http_core_main_conf_t里面的phases所有的phase handler数组,ngx_http_init_phases函数完成每一个phase的handler的初始化
        return NGX_CONF_ERROR;
    }
调用了ngx_http_init_phases函数来初始化phases数组。该函数其实很简单,因为phases数组中每一个元素都有一个数组用来保存该phase的处理函数,ngx_http_init_phases函数就是调用ngx_array_init来初始化这些数组的。

嗯,接下来我们来看一下在ngx_http_core_main_conf_t中的另外一个比较重要的域:

	/** 
	  * 包含所有phase,以及注册的phase handler,这些handler在处理http请求时, 
	  * 会被依次调用,通过ngx_http_phase_handler_t的next字段串联起来组成一个 
	  * 链表。 
	  */  
    ngx_http_phase_engine_t    phase_engine;
这个域将会包含一个动态数组,用来存储所有的phase的handler,并且将它们组成类似于链表的方式,这样额可以方便以后phase按照顺寻来调用。我们首先来看看ngx_http_phase_engine_t结构的定义吧:
typedef struct {
    ngx_http_phase_handler_t  *handlers;   //它是一个动态数组,用来保存所有的ngx_http_phase_handler_t结构
	   /** 
     * server rewrite阶段的handler在ngx_http_core_main_conf_t->phase_engine.handlers数组中的下标 
     */  
    ngx_uint_t                 server_rewrite_index;

	    /** 
     * rewrite阶段的handler在ngx_http_core_main_conf_t->phase_engine.handlers数组中的下标 
     */  
    ngx_uint_t                 location_rewrite_index;
} ngx_http_phase_engine_t;
handler域是一个动态数组,它将会用来保存所有phase的handler,但是这里保存的元素稍微变了一下,并不是单纯的函数了,而是一个ngx_http_phase_handler_t结构,接下来我们来看看这个结构的定义:
struct ngx_http_phase_handler_s {
    ngx_http_phase_handler_pt  checker;   //当前handler的checker函数,同一phase的所有handlerchecker都是统一的
    ngx_http_handler_pt        handler; //处理函数
    ngx_uint_t                 next;    //下一个phase的第一个handler在phase_engine的handlers数组中的下标,用这种方式将phase链成了一个链表,可以用来按照phase的顺序来调用handler
};
这里checker函数在同一个phase的所有handler里都是一样的,而且最终handler函数的调用也是在checker函数中完成的,handler就是对应的函数,next是下一个phase的第一个handler在phase_engine的handlers数组中下表,nginx就是利用这种方式将phase变成了类似于链表的结构,方便调用。

嗯,接下来我们来看一下phase_engine的初始化,在http命令回调函数中会找到如下代码:

    if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {   //初始化phase_engine
        return NGX_CONF_ERROR;
    }
调用了ngx_http_init_phase_handlers函数来初始化phase_engine,好了,接下来我们来看看ngx_http_init_phase_handlers函数吧:
//将ngx_http_core_main_conf_t的phases数组中的handler信息转化为ngx_http_phase_handler_t结构,并将他们保存在phase_engine.handlers数组中
static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
    ngx_int_t                   j;
    ngx_uint_t                  i, n;
    ngx_uint_t                  find_config_index, use_rewrite, use_access;
    ngx_http_handler_pt        *h;
    ngx_http_phase_handler_t   *ph;
    ngx_http_phase_handler_pt   checker;d

    cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;
    cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;
    find_config_index = 0;
    use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;    /* ngx_http_rewrite在http module的postconfiguration回调函数中添加REWRITE阶段的处理函数 */  
    use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;  /* ngx_http_access在http module的postconfiguration回调函数中添加ACCESS阶段的处理函数 */  

//n用来统计所有的phase 的handler的数量
    n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {   //遍历phase数组中的所有的phase,然后将它们handler的数量加起来
        n += cmcf->phases[i].handlers.nelts;
    }

//嗯,这里还加了一个空指针
    ph = ngx_pcalloc(cf->pool,
                     n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));   //这里说白了是为phase_engine.handlers动态数组分配内存空间,因为最后phase_engine.handlers会指向ph,用来保存所有的ngx_http_phase_handler_t
    if (ph == NULL) {
        return NGX_ERROR;
    }

    cmcf->phase_engine.handlers = ph;  //将phase_engine.handlers指向刚刚的ph
    n = 0;


	/* 
		* 初始化phase handler,保存checker和next字段 
		* continue的都是不能添加handler的phase 
		*/	
    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        h = cmcf->phases[i].handlers.elts;

        switch (i) {

        case NGX_HTTP_SERVER_REWRITE_PHASE:
            if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.server_rewrite_index = n;  //记住NGX_HTTP_SERVER_REWRITE_PHASE的第一phase的handler的下标
            }
            checker = ngx_http_core_rewrite_phase;   //NGX_HTTP_SERVER_REWRITE_PHASE的checker

            break;

        case NGX_HTTP_FIND_CONFIG_PHASE:
            find_config_index = n;

            ph->checker = ngx_http_core_find_config_phase;
            n++;
            ph++;

            continue;

        case NGX_HTTP_REWRITE_PHASE:
            if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.location_rewrite_index = n;  //记住NGX_HTTP_REWRITE_PHASE第一个handler的下标
            }
            checker = ngx_http_core_rewrite_phase;

            break;

        case NGX_HTTP_POST_REWRITE_PHASE:
            if (use_rewrite) {
                ph->checker = ngx_http_core_post_rewrite_phase;
                ph->next = find_config_index;   //它的next比较怪,指向前面的,这里主要是为了处理uri重写的情况
                n++;
                ph++;
            }

            continue;

        case NGX_HTTP_ACCESS_PHASE:
            checker = ngx_http_core_access_phase;
            n++;
            break;

        case NGX_HTTP_POST_ACCESS_PHASE:
            if (use_access) {
                ph->checker = ngx_http_core_post_access_phase;
                ph->next = n;
                ph++;
            }

            continue;

        case NGX_HTTP_TRY_FILES_PHASE:
            if (cmcf->try_files) {
                ph->checker = ngx_http_core_try_files_phase;
                n++;
                ph++;
            }

            continue;

        case NGX_HTTP_CONTENT_PHASE:
            checker = ngx_http_core_content_phase;
            break;

        default:
            checker = ngx_http_core_generic_phase;  //其余phase的checker函数是ngx_http_core_generic_phase
        }

        n += cmcf->phases[i].handlers.nelts;  //这里n将表示下一个phase开始的第一个handler的在phase_engine.handlers数组中的下标

        for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--) {
            ph->checker = checker;   //为当前ngx_http_phase_handler_t的checker函数赋值,在同一个phase中,checker是统一的,handler不一样
            ph->handler = h[j];   //为handler赋值
            ph->next = n;  // 指向下一个phase的第一个handler,nginx用这种方式将他们串成了一个链
            ph++;
        }
    }

    return NGX_OK;
}
其实该函数还是很简单的,基本上上面的注释都已经说的很清楚了。而且能够很明白nginx是如何将phase的handler组织成为类似于链表的机构方便phase的顺序调用,而且可以知道同一个phase中,其checker函数都是一样的。不过这里有个地方需要注意,就是NGX_HTTP_POST_REWRITE_PHASE部分的下一个phase指向的是NGX_HTTP_FIND_CONFIG_PHASE,这里可能是因为需要一些额外的处理吧。另外可以知道有的phase是只有一个handler的,例如NGX_HTTP_POST_REWRITE_PHASE,有的phase则可以有很多handler。

好了,phase的初始化就讲的差不多了,下一篇文章讲讲是如何调用这些phase来处理http请求的吧。

<think>我们遇到的问题是master节点初始化报错后如何重新初始化。在Kubernetes中,使用kubeadm初始化主节点时,如果失败,需要清理环境后再重新初始化。 步骤包括: 1. 重置kubeadm 2. 清理相关文件和目录 3. 清理CNI配置 4. 清理iptables规则 5. 重新初始化 注意:重新初始化前,确保所有节点(如果有其他节点)已经重置并清理干净。 下面给出详细步骤:</think>### Kubernetes Master节点重新初始化指南 #### 完整重置与重新初始化流程 1. **安全清理现有环境** ```bash # 1.1 重置kubeadm安装 sudo kubeadm reset --force # 1.2 清理网络接口 sudo ip link delete cni0 sudo ip link delete flannel.1 # 1.3 删除残留配置文件 sudo rm -rf /etc/cni/net.d /etc/kubernetes/ /var/lib/kubelet/ /var/lib/etcd/ # 1.4 清理iptables规则 sudo iptables -F && sudo iptables -t nat -F && sudo iptables -t mangle -F ``` 2. **重新初始化Master节点** ```bash # 2.1 执行初始化命令(替换实际参数) sudo kubeadm init \ --pod-network-cidr=10.244.0.0/16 \ --apiserver-advertise-address=192.168.1.100 \ --control-plane-endpoint=cluster-endpoint:6443 # 2.2 配置kubectl访问 mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config ``` 3. **安装网络插件(以Flannel为例)** ```bash # 3.1 应用Flannel网络 kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml # 3.2 验证网络组件 kubectl get pods -n kube-system ``` #### 关键故障排查点 1. **证书问题处理** ```bash # 检查证书有效期 openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates # 重新生成证书 sudo kubeadm init phase certs all ``` 2. **etcd数据损坏修复** ```bash # 备份并清理etcd数据 sudo mv /var/lib/etcd/member /var/lib/etcd/member.bak # 重新创建etcd sudo kubeadm init phase etcd local ``` 3. **kubelet服务重置** ```bash # 停止并重置kubelet sudo systemctl stop kubelet sudo rm /var/lib/kubelet/config.yaml # 重新生成配置 sudo kubeadm init phase kubelet-start ``` #### 验证初始化成功 ```bash # 检查节点状态 kubectl get nodes -o wide # 测试核心组件 kubectl get pods -n kube-system # 部署测试应用 kubectl create deployment test --image=nginx kubectl expose deployment test --port=80 --type=NodePort ``` #### 预防初始化失败的建议 ```yaml # kubeadm-config.yaml 示例 apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration nodeRegistration: criSocket: "unix:///var/run/containerd/containerd.sock" --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: "1.28.0" networking: podSubnet: "10.244.0.0/16" controlPlaneEndpoint: "cluster-endpoint:6443" ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值