nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解。与网络有关的配置命令主要有两个:listen和sever_name。listen命令设置nginx监听地址,对于IP协议,这个地址就是address和port,对于UNIX域套接字协议,这个地址就是path,一条listen指令只能指定一个address或者port,address也可以是主机名,比如:
listen 127.0.0.1:8000;
listen 127.0.0.1;
listen 8000;
listen *:8000;
listen localhost:8000;
listen [::]:8000;
listen [fe80::1];
listen unix:/var/run/nginx.sock;
server_name指令列出虚拟主机的所有主机名,如server_name example.com *.example.com www.example.*,用空格分割。
typedef struct {
ngx_int_t family;
in_port_t port;
ngx_array_t addrs; /* array of ngx_http_conf_addr_t */
} ngx_http_conf_port_t;
typedef struct {
ngx_http_listen_opt_t opt;
ngx_hash_t hash;
ngx_hash_wildcard_t *wc_head;
ngx_hash_wildcard_t *wc_tail;
ngx_http_core_srv_conf_t *default_server;
ngx_array_t servers; /* array of ngx_http_core_srv_conf_t */
} ngx_http_conf_addr_t;
typedef struct {
/* array of the ngx_http_server_name_t, "server_name" directive */
ngx_array_t server_names;
/* server ctx */
ngx_http_conf_ctx_t *ctx;
ngx_str_t server_name;
size_t connection_pool_size;
size_t request_pool_size;
size_t client_header_buffer_size;
ngx_bufs_t large_client_header_buffers;
ngx_msec_t client_header_timeout;
ngx_flag_t ignore_invalid_headers;
ngx_flag_t merge_slashes;
ngx_flag_t underscores_in_headers;
unsigned listen:1;
ngx_http_core_loc_conf_t **named_locations;
} ngx_http_core_srv_conf_t;
typedef struct {
union {
struct sockaddr sockaddr;
struct sockaddr_in sockaddr_in;
u_char sockaddr_data[NGX_SOCKADDRLEN];
} u;
socklen_t socklen;
unsigned set:1; // bind/backlog/…
unsigned default_server:1; // default_server
unsigned bind:1; // bind/backlog/…
unsigned wildcard:1;
#if (NGX_HTTP_SSL)
unsigned ssl:1;
#endif
#if (NGX_HTTP_SPDY)
unsigned spdy:1; // spdy
#endif
unsigned so_keepalive:2;
unsigned proxy_protocol:1; // proxy_protocol
int backlog; // backlog
int rcvbuf; // rcvbuf
int sndbuf; // sndbuf
。。。
u_char addr[NGX_SOCKADDR_STRLEN + 1]; // 0.0.0.0:8000
} ngx_http_listen_opt_t;
struct ngx_listening_s {
ngx_socket_t fd;
struct sockaddr *sockaddr;
socklen_t socklen; /* size of sockaddr */
size_t addr_text_max_len;
ngx_str_t addr_text;
int type;
int backlog;
int rcvbuf;
int sndbuf;
ngx_connection_handler_pt handler;
void *servers; /* array of ngx_http_port_t, for example */
。。。
ngx_listening_t *previous;
ngx_connection_t *connection;
unsigned open:1;
unsigned remain:1;
unsigned ignore:1;
unsigned bound:1; /* already bound */
unsigned inherited:1; /* inherited from previous process */
unsigned nonblocking_accept:1;
unsigned listen:1;
unsigned nonblocking:1;
unsigned shared:1; /* shared between threads or processes */
unsigned addr_ntop:1;
unsigned keepalive:2;
};
typedef struct {
/* ngx_http_in_addr_t or ngx_http_in6_addr_t */
void *addrs;
ngx_uint_t naddrs;
} ngx_http_port_t;
typedef struct {
in_addr_t addr;
ngx_http_addr_conf_t conf;
} ngx_http_in_addr_t;
struct ngx_http_addr_conf_s {
/* the default server configuration for this address:port */
ngx_http_core_srv_conf_t *default_server;
ngx_http_virtual_names_t *virtual_names;
#if (NGX_HTTP_SSL)
unsigned ssl:1;
#endif
#if (NGX_HTTP_SPDY)
unsigned spdy:1;
#endif
unsigned proxy_protocol:1;
};
函数功能:解析server_name指令。
static char *
ngx_http_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_srv_conf_t *cscf = conf;
u_char ch;
ngx_str_t *value;
ngx_uint_t i;
ngx_http_server_name_t *sn;
value = cf->args->elts;
for (i = 1; i < cf->args->nelts; i++) {
ch = value[i].data[0];
if ((ch == '*' && (value[i].len < 3 || value[i].data[1] != '.'))
|| (ch == '.' && value[i].len < 2))
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"server name \"%V\" is invalid", &value;[i]);
return NGX_CONF_ERROR;
}
if (ngx_strchr(value[i].data, '/')) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"server name \"%V\" has suspicious symbols",
&value;[i]);
}
sn = ngx_array_push(&cscf->server_names);
if (sn == NULL) {
return NGX_CONF_ERROR;
}
sn->server = cscf;
if (ngx_strcasecmp(value[i].data, (u_char *) "$hostname") == 0) {
sn->name = cf->cycle->hostname;
} else {
sn->name = value[i];
}
if (value[i].data[0] != '~') {
ngx_strlow(sn->name.data, sn->name.data, sn->name.len);
continue;
}
。。。
}
return NGX_CONF_OK;
}
函数功能:解析listen指令。
static char *
ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_srv_conf_t *cscf = conf;
ngx_str_t *value, size;
ngx_url_t u;
ngx_uint_t n;
ngx_http_listen_opt_t lsopt;
cscf->listen = 1;
value = cf->args->elts;
ngx_memzero(&u;, sizeof(ngx_url_t));
u.url = value[1];
u.listen = 1;
u.default_port = 80;
//解析listen命令后面的参数,ip:port
if (ngx_parse_url(cf->pool, &u;) != NGX_OK) {
if (u.err) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"%s in \"%V\" of the \"listen\" directive",
u.err, &u.url;);
}
return NGX_CONF_ERROR;
}
ngx_memzero(&lsopt;, sizeof(ngx_http_listen_opt_t));
ngx_memcpy(&lsopt.u.sockaddr;, u.sockaddr, u.socklen);
lsopt.socklen = u.socklen;
lsopt.backlog = NGX_LISTEN_BACKLOG;
lsopt.rcvbuf = -1;
lsopt.sndbuf = -1;
lsopt.wildcard = u.wildcard; //listen 80
(void) ngx_sock_ntop(&lsopt.u.sockaddr;, lsopt.socklen, lsopt.addr,
NGX_SOCKADDR_STRLEN, 1);
for (n = 2; n < cf->args->nelts; n++) {
if (ngx_strcmp(value[n].data, "default_server") == 0
|| ngx_strcmp(value[n].data, "default") == 0)
{
lsopt.default_server = 1;
continue;
}
….
if (ngx_strncmp(value[n].data, "backlog=", 8) == 0) {
lsopt.backlog = ngx_atoi(value[n].data + 8, value[n].len - 8);
lsopt.set = 1;
lsopt.bind = 1;
if (lsopt.backlog == NGX_ERROR || lsopt.backlog == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid backlog \"%V\"", &value;[n]);
return NGX_CONF_ERROR;
}
continue;
}
if (ngx_strcmp(value[n].data, "ssl") == 0) {
#if (NGX_HTTP_SSL)
lsopt.ssl = 1;
continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the \"ssl\" parameter requires "
"ngx_http_ssl_module");
return NGX_CONF_ERROR;
#endif
}
if (ngx_strcmp(value[n].data, "spdy") == 0) {
#if (NGX_HTTP_SPDY)
lsopt.spdy = 1;
continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the \"spdy\" parameter requires "
"ngx_http_spdy_module");
return NGX_CONF_ERROR;
#endif
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value;[n]);
return NGX_CONF_ERROR;
}
//将解析到的虚拟主机的地址信息加入到监听列表中
if (ngx_http_add_listen(cf, cscf, &lsopt;) == NGX_OK) {
return NGX_CONF_OK;
}
return NGX_CONF_ERROR;
}
函数功能:以port为粒度,添加addr(即1.1.1.1:80、2.2.2.2:80和80,会放在相同的cmcf->port[i]中)。
ngx_int_t
ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
ngx_http_listen_opt_t *lsopt)
{
in_port_t p;
ngx_uint_t i;
struct sockaddr *sa;
struct sockaddr_in *sin;
ngx_http_conf_port_t *port;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
if (cmcf->ports == NULL) { //全局
cmcf->ports = ngx_array_create(cf->temp_pool, 2,
sizeof(ngx_http_conf_port_t));
if (cmcf->ports == NULL) {
return NGX_ERROR;
}
}
sa = &lsopt-;>u.sockaddr;
switch (sa->sa_family) {
…
default: /* AF_INET */
sin = &lsopt-;>u.sockaddr_in;
p = sin->sin_port;
break;
}
port = cmcf->ports->elts;
for (i = 0; i < cmcf->ports->nelts; i++) {
if (p != port[i].port || sa->sa_family != port[i].family) {
continue;
}
//相同的port共用相同的address
return ngx_http_add_addresses(cf, cscf, &port;[i], lsopt);
}
port = ngx_array_push(cmcf->ports);
if (port == NULL) {
return NGX_ERROR;
}
port->family = sa->sa_family;
port->port = p;
port->addrs.elts = NULL;
return ngx_http_add_address(cf, cscf, port, lsopt);
}
函数功能:相同的port,相同的addr,则ngx_http_add_server(如listen 8080,server_name hello和server_name world),相同的port,不同的addr,则ngx_http_add_address(如listen 1.1.1.1:80和2.2.2.2:80)。
static ngx_int_t
ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt)
{
u_char *p;
size_t len, off;
ngx_uint_t i, default_server;
struct sockaddr *sa;
ngx_http_conf_addr_t *addr;
#if (NGX_HTTP_SSL)
ngx_uint_t ssl;
#endif
#if (NGX_HTTP_SPDY)
ngx_uint_t spdy;
#endif
sa = &lsopt-;>u.sockaddr;
switch (sa->sa_family) {
default: /* AF_INET */
off = offsetof(struct sockaddr_in, sin_addr); // 4
len = 4;
break;
}
p = lsopt->u.sockaddr_data + off; //u_char
addr = port->addrs.elts;
for (i = 0; i < port->addrs.nelts; i++) {
if (ngx_memcmp(p, addr[i].opt.u.sockaddr_data + off, len) != 0) { //比较sin_addr
continue;
}
//相同的addr:port,不同的server_name
if (ngx_http_add_server(cf, cscf, &addr;[i]) != NGX_OK) {
return NGX_ERROR;
}
/* preserve default_server bit during listen options overwriting */
default_server = addr[i].opt.default_server;
#if (NGX_HTTP_SSL)
ssl = lsopt->ssl || addr[i].opt.ssl;
#endif
#if (NGX_HTTP_SPDY)
spdy = lsopt->spdy || addr[i].opt.spdy;
#endif
if (lsopt->set) {
if (addr[i].opt.set) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"duplicate listen options for %s", addr[i].opt.addr);
return NGX_ERROR;
}
addr[i].opt = *lsopt;
}
/* check the duplicate "default" server for this address:port */
if (lsopt->default_server) {
if (default_server) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"a duplicate default server for %s", addr[i].opt.addr);
return NGX_ERROR;
}
default_server = 1;
addr[i].default_server = cscf;
}
addr[i].opt.default_server = default_server;
#if (NGX_HTTP_SSL)
addr[i].opt.ssl = ssl;
#endif
#if (NGX_HTTP_SPDY)
addr[i].opt.spdy = spdy;
#endif
return NGX_OK;
}
/* add the address to the addresses list that bound to this port */
return ngx_http_add_address(cf, cscf, port, lsopt);
}
static ngx_int_t
ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt)
{
ngx_http_conf_addr_t *addr;
if (port->addrs.elts == NULL) {
if (ngx_array_init(&port-;>addrs, cf->temp_pool, 4,
sizeof(ngx_http_conf_addr_t))
!= NGX_OK)
{
return NGX_ERROR;
}
}
addr = ngx_array_push(&port-;>addrs);
if (addr == NULL) {
return NGX_ERROR;
}
addr->opt = *lsopt;
addr->hash.buckets = NULL;
addr->hash.size = 0;
addr->wc_head = NULL;
addr->wc_tail = NULL;
#if (NGX_PCRE)
addr->nregex = 0;
addr->regex = NULL;
#endif
addr->default_server = cscf;
addr->servers.elts = NULL;
return ngx_http_add_server(cf, cscf, addr);
}
函数功能:将新的虚拟主机信息加入到这个地址的虚拟主机列表中。
static ngx_int_t
ngx_http_add_server(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
ngx_http_conf_addr_t *addr)
{
ngx_uint_t i;
ngx_http_core_srv_conf_t **server;
if (addr->servers.elts == NULL) {
if (ngx_array_init(&addr-;>servers, cf->temp_pool, 4,
sizeof(ngx_http_core_srv_conf_t *))
!= NGX_OK)
{
return NGX_ERROR;
}
} else {
server = addr->servers.elts;
for (i = 0; i < addr->servers.nelts; i++) { //相同的server,相同的listen
if (server[i] == cscf) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"a duplicate listen %s", addr->opt.addr);
return NGX_ERROR;
}
}
}
server = ngx_array_push(&addr-;>servers);
if (server == NULL) {
return NGX_ERROR;
}
*server = cscf;
return NGX_OK;
}
函数功能:ngx_http_block的最后一步。
static ngx_int_t
ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
ngx_array_t *ports)
{
ngx_uint_t p, a;
ngx_http_conf_port_t *port;
ngx_http_conf_addr_t *addr;
if (ports == NULL) {
return NGX_OK;
}
port = ports->elts;
for (p = 0; p < ports->nelts; p++) {
//比如listen 80和listen 127.0.0.1:80
//排序原则:通配符如listen 80和listen*:80,会排在最后面,listen 1.1.1.1:80 backlog=512等会排在最前面
ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
sizeof(ngx_http_conf_addr_t), ngx_http_cmp_conf_addrs);
addr = port[p].addrs.elts;
for (a = 0; a < port[p].addrs.nelts; a++) {
if (addr[a].servers.nelts > 1 //相同的addr:port,不同的server_name
#if (NGX_PCRE)
|| addr[a].default_server->captures
#endif
)
{
if (ngx_http_server_names(cf, cmcf, &addr;[a]) != NGX_OK) {
return NGX_ERROR;
}
}
}
if (ngx_http_init_listening(cf, &port;[p]) != NGX_OK) { //一个port,一个listening
return NGX_ERROR;
}
}
return NGX_OK;
}
函数功能:相同的addr:port的优化处理。
static ngx_int_t
ngx_http_server_names(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
ngx_http_conf_addr_t *addr)
{
ngx_int_t rc;
ngx_uint_t n, s;
ngx_hash_init_t hash;
ngx_hash_keys_arrays_t ha;
ngx_http_server_name_t *name;
ngx_http_core_srv_conf_t **cscfp;
ngx_memzero(&ha;, sizeof(ngx_hash_keys_arrays_t));
ha.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
if (ha.temp_pool == NULL) {
return NGX_ERROR;
}
ha.pool = cf->pool;
if (ngx_hash_keys_array_init(&ha;, NGX_HASH_LARGE) != NGX_OK) {
goto failed;
}
cscfp = addr->servers.elts;
for (s = 0; s < addr->servers.nelts; s++) {
name = cscfp[s]->server_names.elts; //一个server可以有多个server_name
for (n = 0; n < cscfp[s]->server_names.nelts; n++) {
rc = ngx_hash_add_key(&ha;, &name;[n].name, name[n].server,
NGX_HASH_WILDCARD_KEY);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"invalid server name or wildcard \"%V\" on %s",
&name;[n].name, addr->opt.addr);
return NGX_ERROR;
}
if (rc == NGX_BUSY) {
ngx_log_error(NGX_LOG_WARN, cf->log, 0,
"conflicting server name \"%V\" on %s, ignored",
&name;[n].name, addr->opt.addr);
}
}
}
hash.key = ngx_hash_key_lc;
hash.max_size = cmcf->server_names_hash_max_size;
hash.bucket_size = cmcf->server_names_hash_bucket_size;
hash.name = "server_names_hash";
hash.pool = cf->pool;
if (ha.keys.nelts) { //无通配
hash.hash = &addr-;>hash;
hash.temp_pool = NULL;
if (ngx_hash_init(&hash;, ha.keys.elts, ha.keys.nelts) != NGX_OK) {
goto failed;
}
}
if (ha.dns_wc_head.nelts) { //前缀通配
ngx_qsort(ha.dns_wc_head.elts, (size_t) ha.dns_wc_head.nelts,
sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);
hash.hash = NULL;
hash.temp_pool = ha.temp_pool;
if (ngx_hash_wildcard_init(&hash;, ha.dns_wc_head.elts,
ha.dns_wc_head.nelts)
!= NGX_OK)
{
goto failed;
}
addr->wc_head = (ngx_hash_wildcard_t *) hash.hash;
}
if (ha.dns_wc_tail.nelts) { //后缀通配
ngx_qsort(ha.dns_wc_tail.elts, (size_t) ha.dns_wc_tail.nelts,
sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);
hash.hash = NULL;
hash.temp_pool = ha.temp_pool;
if (ngx_hash_wildcard_init(&hash;, ha.dns_wc_tail.elts,
ha.dns_wc_tail.nelts)
!= NGX_OK)
{
goto failed;
}
addr->wc_tail = (ngx_hash_wildcard_t *) hash.hash;
}
ngx_destroy_pool(ha.temp_pool);
return NGX_OK;
failed:
ngx_destroy_pool(ha.temp_pool);
return NGX_ERROR;
}
static ngx_int_t
ngx_http_cmp_conf_addrs(const void *one, const void *two)
{
ngx_http_conf_addr_t *first, *second;
first = (ngx_http_conf_addr_t *) one;
second = (ngx_http_conf_addr_t *) two;
//通配符必须是最后一个,返回1,则表示要交换顺序
if (first->opt.wildcard) {
/* a wildcard address must be the last resort, shift it to the end */
return 1;
}
//通配符必须是最后一个,不需要换位置
if (second->opt.wildcard) {
/* a wildcard address must be the last resort, shift it to the end */
return -1;
}
//设置如backlog的,需要放在前面
if (first->opt.bind && !second->opt.bind) {
/* shift explicit bind()ed addresses to the start */
return -1;
}
//设置如backlog的,需要放在前面
if (!first->opt.bind && second->opt.bind) {
/* shift explicit bind()ed addresses to the start */
return 1;
}
/* do not sort by default */
return 0;
}
函数功能:用新的结构(cycle-> listening)存储这些关系。
static ngx_int_t
ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port)
{
ngx_uint_t i, last, bind_wildcard;
ngx_listening_t *ls;
ngx_http_port_t *hport;
ngx_http_conf_addr_t *addr;
addr = port->addrs.elts;
last = port->addrs.nelts; //相同的port,不同的addr个数
if (addr[last - 1].opt.wildcard) { //通配符需要放在最后一个,设置backlog的需要放在前面
addr[last - 1].opt.bind = 1;
bind_wildcard = 1;
} else {
bind_wildcard = 0;
}
i = 0;
while (i < last) {
if (bind_wildcard && !addr[i].opt.bind) {
i++;
continue;
}
//这个函数里面将会创建,并且初始化listen结构,这个listen已经存放在cycle结构的listen数组中
ls = ngx_http_add_listening(cf, &addr;[i]); //port->addrs.elts
if (ls == NULL) {
return NGX_ERROR;
}
hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t));
if (hport == NULL) {
return NGX_ERROR;
}
ls->servers = hport;
if (i == last - 1) {
hport->naddrs = last; //将*:port和没有显式bind的address:port放在同一个listen中,如listen 80和listen 1.1.1.1:80可以放在一个listening中
} else {
hport->naddrs = 1;
//重新赋值为0,因为最前面可能是listen 2.2.2.2:80 backlog=512这样的
i = 0;
}
switch (ls->sockaddr->sa_family) {
default: /* AF_INET */
if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) {
return NGX_ERROR;
}
break;
}
addr++;
last--;
}
return NGX_OK;
}