nginx记录状态码逻辑源码分析
字数 835 2025-08-15 21:32:47

Nginx状态码记录机制源码分析与实现详解

1. 核心记录机制概述

Nginx通过ngx_http_reqstat_log_handler函数记录HTTP状态码,该函数在ngx_http_reqstat_init中被注册到NGX_HTTP_LOG_PHASE阶段。

static ngx_int_t ngx_http_reqstat_init(ngx_conf_t *cf) {
    h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);
    *h = ngx_http_reqstat_log_handler;
}

2. 状态码获取逻辑

状态码获取遵循以下优先级顺序:

  1. 首先检查r->err_status(错误状态)
  2. 其次检查r->headers_out.status(正常响应状态)
  3. 对于HTTP/0.9请求,状态码设为9
  4. 其他情况状态码设为0
static ngx_int_t ngx_http_reqstat_log_handler(ngx_http_request_t *r) {
    if (r->err_status) {
        status = r->err_status;
    } else if (r->headers_out.status) {
        status = r->headers_out.status;
    } else if (r->http_version == NGX_HTTP_VERSION_9) {
        status = 9;
    } else {
        status = 0;
    }
    // 后续处理...
}

3. 状态码分类处理

状态码被分为以下几类进行统计:

3.1 标准HTTP状态码

switch (status) {
    case 500:
        ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_500, 1);
        break;
    case 502:
        ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_502, 1);
        break;
    // 其他状态码处理...
}

3.2 上游服务器状态码

对于有上游服务器的情况,取最后一个连接的状态码:

if (r->upstream_states != NULL && r->upstream_states->nelts > 0) {
    ngx_http_upstream_state_t *state = r->upstream_states->elts;
    status = state[r->upstream_states->nelts - 1].status;
    
    if (status >= 400 && status < 500) {
        ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_UPS_4XX, 1);
    } else if (status >= 500 && status < 600) {
        ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_UPS_5XX, 1);
    } else if (status >= 200 && status < 300) {
        ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_UPS_2XX, 1);
    }
}

4. 上游服务器连接处理

4.1 连接建立过程

void ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u) {
    // 从states数组中获取新的state记录
    u->state = ngx_array_push(r->upstream_states);
    ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));
    
    rc = ngx_event_connect_peer(&u->peer);
    
    // 处理连接繁忙情况
    if (rc == NGX_BUSY) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams");
        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE);
        return;
    }
    
    // 连接成功则发送请求
    ngx_http_upstream_send_request(r, u, 1);
}

4.2 请求发送与状态码处理

static void ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_uint_t do_write) {
    rc = ngx_http_upstream_send_request_body(r, u, do_write);
    
    if (rc == NGX_ERROR) {
        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
        return;
    }
    
    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        ngx_http_upstream_finalize_request(r, u, rc);
        return;
    }
}

5. 健康检查机制

健康检查失败通过ngx_http_upstream_get_round_robin_peer函数判断:

ngx_int_t ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data) {
#if (NGX_HTTP_UPSTREAM_CHECK)
    if (ngx_http_upstream_check_peer_down(peer->check_index)) {
        goto failed;
    }
#endif

failed:
    return NGX_BUSY;
}

6. 上游错误处理

上游错误通过ngx_http_upstream_next函数处理:

static void ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_uint_t ft_type) {
    switch (ft_type) {
        case NGX_HTTP_UPSTREAM_FT_TIMEOUT:
            status = NGX_HTTP_GATEWAY_TIME_OUT;
            break;
        case NGX_HTTP_UPSTREAM_FT_HTTP_500:
            status = NGX_HTTP_INTERNAL_SERVER_ERROR;
            break;
        case NGX_HTTP_UPSTREAM_FT_HTTP_403:
            status = NGX_HTTP_FORBIDDEN;
            break;
        case NGX_HTTP_UPSTREAM_FT_HTTP_404:
            status = NGX_HTTP_NOT_FOUND;
            break;
        default:
            status = NGX_HTTP_BAD_GATEWAY;
    }
    
    if (status) {
        u->state->status = status;
        timeout = u->conf->next_upstream_timeout;
        ngx_http_upstream_finalize_request(r, u, status);
    }
}

7. 请求终结处理

void ngx_http_upstream_finalize_request(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_int_t rc) {
    ngx_http_finalize_request(r, rc);
}

void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) {
    if (rc >= NGX_HTTP_SPECIAL_RESPONSE || rc == NGX_HTTP_CREATED || rc == NGX_HTTP_NO_CONTENT) {
        if (rc == NGX_HTTP_CLOSE) {
            ngx_http_terminate_request(r, rc);
            return;
        }
        
        if (r == r->main) {
            if (c->read->timer_set) { ngx_del_timer(c->read); }
            if (c->write->timer_set) { ngx_del_timer(c->write); }
        }
        
        c->read->handler = ngx_http_request_handler;
        c->write->handler = ngx_http_request_handler;
        
        ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc));
        return;
    }
}

8. 特殊响应处理

ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error) {
    r->err_status = error;
    // 错误页返回处理...
}

9. 状态码统计展示

通过特定location展示统计信息:

location /check_req_status {
    req_status_show req_server_status;
}

对应的处理函数:

static ngx_int_t ngx_http_reqstat_show_handler(ngx_http_request_t *r) {
    for (j = 0; j < rlcf->user_select->nelts; j++) {
        if (user[j] < NGX_HTTP_REQSTAT_RSRV) {
            index = user[j];
            b->last = ngx_slprintf(b->last, b->end, "%uA,", 
                *NGX_HTTP_REQSTAT_REQ_FIELD(node, ngx_http_reqstat_fields[index]));
        } else {
            index = user[j] - NGX_HTTP_REQSTAT_RSRV;
            b->last = ngx_slprintf(b->last, b->end, "%uA,", 
                *NGX_HTTP_REQSTAT_REQ_FIELD(node, NGX_HTTP_REQSTAT_EXTRA(index)));
        }
    }
}

10. 状态码字段定义

Nginx定义了详细的状态码统计字段:

off_t ngx_http_reqstat_fields[NGX_HTTP_REQSTAT_RSRV] = {
    NGX_HTTP_REQSTAT_BYTES_IN,        // 输入字节数
    NGX_HTTP_REQSTAT_BYTES_OUT,       // 输出字节数
    NGX_HTTP_REQSTAT_CONN_TOTAL,      // 总连接数
    NGX_HTTP_REQSTAT_REQ_TOTAL,       // 总请求数
    NGX_HTTP_REQSTAT_2XX,             // 2xx状态码
    NGX_HTTP_REQSTAT_3XX,             // 3xx状态码
    NGX_HTTP_REQSTAT_4XX,             // 4xx状态码
    NGX_HTTP_REQSTAT_5XX,             // 5xx状态码
    NGX_HTTP_REQSTAT_OTHER_STATUS,    // 其他状态码
    NGX_HTTP_REQSTAT_RT,              // 响应时间
    NGX_HTTP_REQSTAT_UPS_REQ,         // 上游请求数
    NGX_HTTP_REQSTAT_UPS_RT,          // 上游响应时间
    NGX_HTTP_REQSTAT_UPS_TRIES,       // 上游尝试次数
    // 详细状态码...
    NGX_HTTP_REQSTAT_200,
    NGX_HTTP_REQSTAT_206,
    NGX_HTTP_REQSTAT_302,
    NGX_HTTP_REQSTAT_304,
    NGX_HTTP_REQSTAT_403,
    NGX_HTTP_REQSTAT_404,
    NGX_HTTP_REQSTAT_416,
    NGX_HTTP_REQSTAT_499,
    NGX_HTTP_REQSTAT_500,
    NGX_HTTP_REQSTAT_502,
    NGX_HTTP_REQSTAT_503,
    NGX_HTTP_REQSTAT_504,
    NGX_HTTP_REQSTAT_508,
    NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS,
    // 上游特定状态码...
    NGX_HTTP_REQSTAT_UPS_4XX,
    NGX_HTTP_REQSTAT_UPS_5XX,
    NGX_HTTP_REQSTAT_UPS_LATENCY      // 上游延迟
};

11. 时间统计机制

对于Nginx 1.9.2及以上版本,使用更精确的时间统计结构:

typedef struct {
    ngx_uint_t status;            // 状态码
    ngx_msec_t response_time;     // 响应时间
    ngx_msec_t connect_time;      // 连接时间
    ngx_msec_t header_time;       // 首包时间
    ngx_msec_t queue_time;        // 队列时间
    off_t response_length;        // 响应长度
    off_t bytes_received;         // 接收字节数
    off_t bytes_sent;             // 发送字节数
    ngx_str_t *peer;              // 对端信息
} ngx_http_upstream_state_t;

时间记录在请求处理过程中进行:

u->start_time = ngx_current_msec;
u->state->response_time = (ngx_msec_t) -1;
u->state->connect_time = (ngx_msec_t) -1;
u->state->header_time = (ngx_msec_t) -1;

首包时间在头部处理时记录:

static void ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u) {
    rc = u->process_header(r);
    u->state->header_time = ngx_current_msec - u->start_time;
}

总结

Nginx的状态码记录机制是一个多层次、多阶段的复杂系统,主要特点包括:

  1. 状态码获取有明确的优先级顺序
  2. 对标准HTTP状态码和上游状态码分别处理
  3. 详细记录各种时间指标和性能数据
  4. 通过模块化设计支持灵活的统计展示
  5. 与上游服务器健康检查机制紧密集成
  6. 对错误处理有完整的流程和状态码映射

理解这些机制对于Nginx性能调优、故障排查和监控系统建设都有重要意义。

Nginx状态码记录机制源码分析与实现详解 1. 核心记录机制概述 Nginx通过 ngx_http_reqstat_log_handler 函数记录HTTP状态码,该函数在 ngx_http_reqstat_init 中被注册到NGX_ HTTP_ LOG_ PHASE阶段。 2. 状态码获取逻辑 状态码获取遵循以下优先级顺序: 首先检查 r->err_status (错误状态) 其次检查 r->headers_out.status (正常响应状态) 对于HTTP/0.9请求,状态码设为9 其他情况状态码设为0 3. 状态码分类处理 状态码被分为以下几类进行统计: 3.1 标准HTTP状态码 3.2 上游服务器状态码 对于有上游服务器的情况,取最后一个连接的状态码: 4. 上游服务器连接处理 4.1 连接建立过程 4.2 请求发送与状态码处理 5. 健康检查机制 健康检查失败通过 ngx_http_upstream_get_round_robin_peer 函数判断: 6. 上游错误处理 上游错误通过 ngx_http_upstream_next 函数处理: 7. 请求终结处理 8. 特殊响应处理 9. 状态码统计展示 通过特定location展示统计信息: 对应的处理函数: 10. 状态码字段定义 Nginx定义了详细的状态码统计字段: 11. 时间统计机制 对于Nginx 1.9.2及以上版本,使用更精确的时间统计结构: 时间记录在请求处理过程中进行: 首包时间在头部处理时记录: 总结 Nginx的状态码记录机制是一个多层次、多阶段的复杂系统,主要特点包括: 状态码获取有明确的优先级顺序 对标准HTTP状态码和上游状态码分别处理 详细记录各种时间指标和性能数据 通过模块化设计支持灵活的统计展示 与上游服务器健康检查机制紧密集成 对错误处理有完整的流程和状态码映射 理解这些机制对于Nginx性能调优、故障排查和监控系统建设都有重要意义。