Nginx之Keepalive请求长连接复用率统计

该模块的实现原理比较简单:在NGX_LOG_PHASE阶段加入一个handler,根据请求是否upstream,再结合upstream->peer.cached参数即可判断该请求是否复用长连接。

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <nginx.h>
#include <ngx_files.h>

#define KEEPALIVE_LOG_DEBUG
#define KEEPALIVE_LOG_DEBUG_LEVEL NGX_LOG_NOTICE
#define KEEPALIVE_LOG_MAX_LOG_LEN 1024

typedef struct {
 ngx_log_t *default_log;
}ngx_http_keepalive_log_main_conf_t;

typedef struct {
 ngx_flag_t on;
 ngx_log_t *log;
} ngx_http_keepalive_log_loc_conf_t;

static void* ngx_http_keepalive_log_create_main_conf(ngx_conf_t *cf);
static void* ngx_http_keepalive_log_create_loc_conf(ngx_conf_t *cf);
static char* ngx_http_keepalive_log_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);
static ngx_int_t ngx_http_keepalive_log_init(ngx_conf_t *cf);
static ngx_int_t ngx_http_keepalive_log_init_process(ngx_cycle_t *cycle);
static ngx_int_t ngx_http_keepalive_log_handler(ngx_http_request_t *r);
static void ngx_http_keepalive_log_get_req_info(ngx_http_request_t *r, ngx_str_t *serv, ngx_str_t *hostname, ngx_str_t *ip);
static ngx_log_t *ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name);
static char* ngx_http_keepalive_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);


static ngx_http_module_t ngx_http_keepalive_log_module_ctx = {
    NULL,
    ngx_http_keepalive_log_init,    /* postconfiguration */
   
 ngx_http_keepalive_log_create_main_conf,  /* create main configuration */
    NULL,

    NULL,
    NULL,

    ngx_http_keepalive_log_create_loc_conf,
    ngx_http_keepalive_log_merge_loc_conf   
};

static ngx_command_t ngx_http_keepalive_log_commands[] = {
 {
  ngx_string("keepalive_log"),
  NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LIF_CONF | NGX_CONF_TAKE1,
  ngx_http_keepalive_log_set_log,
  NGX_HTTP_LOC_CONF_OFFSET,
  0,
  NULL
 },
 {
  ngx_string("keepalive_log_enable"),
  NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
  ngx_conf_set_flag_slot,
  NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_keepalive_log_loc_conf_t, on),
  NULL
 },
 ngx_null_command
};

ngx_module_t ngx_http_keepalive_log_module = {
 NGX_MODULE_V1,
 &ngx_http_keepalive_log_module_ctx,
 ngx_http_keepalive_log_commands,
 NGX_HTTP_MODULE,
 NULL,              /* init master */
 NULL,              /* init module */
 ngx_http_keepalive_log_init_process,      /* init process */
 NULL,              /* init thread */
 NULL,              /* exit thread */
 NULL,              /* exit process */
 NULL,              /* exit master */
 NGX_MODULE_V1_PADDING
};

static ngx_int_t
ngx_http_keepalive_log_init_process(ngx_cycle_t *cycle)
{
 ngx_http_keepalive_log_main_conf_t *kmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_keepalive_log_module);
 kmcf->default_log = &cycle->new_log;

 return NGX_OK;
}

static void*
ngx_http_keepalive_log_create_main_conf(ngx_conf_t *cf)
{
 ngx_http_keepalive_log_main_conf_t *kmcf;
 kmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_keepalive_log_main_conf_t));
 if(!kmcf) {
  return NGX_CONF_ERROR;
 }
 kmcf->default_log = NULL;

 return kmcf;
}

static void*
ngx_http_keepalive_log_create_loc_conf(ngx_conf_t *cf)
{
 ngx_http_keepalive_log_loc_conf_t *klcf;

 klcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_keepalive_log_loc_conf_t));
 if(klcf == NULL) {
  return NULL;
 }

 klcf->on = NGX_CONF_UNSET;
 klcf->log = NGX_CONF_UNSET_PTR;
   
    return klcf;
}

static char*
ngx_http_keepalive_log_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
 ngx_http_keepalive_log_loc_conf_t *prev = parent;
 ngx_http_keepalive_log_loc_conf_t *conf = child;

 ngx_conf_merge_value(conf->on, prev->on, 0);
 ngx_conf_merge_ptr_value(conf->log, prev->log, NULL);

 return NGX_CONF_OK;
}

static ngx_int_t
ngx_http_keepalive_log_init(ngx_conf_t *cf)
{
 ngx_http_handler_pt *h;
 ngx_http_core_main_conf_t *cmcf;


 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
 h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);
 if (h == NULL) {
  return NGX_ERROR;
 }
 *h = ngx_http_keepalive_log_handler;

    return NGX_OK;
}

static ngx_int_t
ngx_http_keepalive_log_handler(ngx_http_request_t *r)
{
 ngx_fd_t fd;
 u_char *p;
 u_char msg[KEEPALIVE_LOG_MAX_LOG_LEN];
 ngx_http_keepalive_log_main_conf_t *kmcf;

 
 ngx_http_keepalive_log_loc_conf_t *klcf = ngx_http_get_module_loc_conf(r, ngx_http_keepalive_log_module);
 if(!klcf->on || !klcf->log) {
  return NGX_OK;
 }
 fd= klcf->log->file->fd;

 if (r == r->main) {
  u_char portstr[] = "65535";   
  ngx_str_t serv;
  ngx_str_t hostname;
  ngx_str_t ip;
 
  serv.data = portstr;
  ngx_http_keepalive_log_get_req_info(r, &serv, &hostname, &ip);
 
#ifdef KEEPALIVE_LOG_DEBUG
  kmcf = ngx_http_get_module_main_conf(r, ngx_http_keepalive_log_module);
  ngx_log_error(KEEPALIVE_LOG_DEBUG_LEVEL, kmcf->default_log, 0,
   "%V %V %V", &ip, &serv, &hostname);
#endif
 
  if (!r->upstream) {
   return NGX_OK;
  }

  ngx_uint_t cached = r->upstream->peer.cached ? 1 : 0;
  p = ngx_snprintf(msg, KEEPALIVE_LOG_MAX_LOG_LEN, "%V %V %V %ui\n", ip, serv, hostname, cached);
  (void) ngx_write_fd(fd, msg, p - msg);
 }

    return NGX_OK;
}

static void
ngx_http_keepalive_log_get_req_info(ngx_http_request_t *r, ngx_str_t *serv, ngx_str_t *hostname, ngx_str_t *ip)
{
 ngx_http_core_srv_conf_t *cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

 if(cscf->server_name.len > 0) {
  *serv = cscf->server_name;
 } else {
  ngx_uint_t port;

  port = ntohs(((struct sockaddr_in*)(r->connection->local_sockaddr))->sin_port);
  if(port > 0 && port < 65536) {
   //用端口来代表server
   serv->len = ngx_sprintf(serv->data, "%ui", port) - serv->data;
  } else {
   ngx_str_set(serv, "-");
  }
 }

 if(r->headers_in.host) {
  *hostname = r->headers_in.host->value;
 } else {
  ngx_str_set(hostname, "-");
 }

 *ip = r->connection->addr_text;
}

static ngx_log_t
*ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name)
{
 ngx_log_t *log = ngx_pcalloc(cycle->pool, sizeof(ngx_log_t));
 if(log == NULL) {
  return NULL;
 }

 log->file = ngx_conf_open_file(cycle, name);
 if(log->file == NULL) {
  return NULL;
 }

 return log;
}
 
static char*
ngx_http_keepalive_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
 ngx_http_keepalive_log_loc_conf_t *klcf = conf;
 ngx_str_t *value = NULL;

 if(klcf->log != NGX_CONF_UNSET_PTR) {
  return "is duplicated";
 }

 value = cf->args->elts;
 klcf->log = ngx_log_create(cf->cycle, &value[1]);
 if(klcf->log == NULL) {
  return NGX_CONF_ERROR;
 }

 return NGX_CONF_OK;
}

相关推荐