2.4 用HTTP核心模块配置一个静态Web服务器

静态Web服务器的主要功能由ngx_http_core_module模块(HTTP框架的主要成员)实现,当然,一个完整的静态Web服务器还有许多功能是由其他的HTTP模块实现的。本节主要讨论如何配置一个包含基本功能的静态Web服务器,文中会完整地说明ngx_http_core_module模块提供的配置项及变量的用法,但不会过多说明其他HTTP模块的配置项。在阅读完本节内容后,读者应当可以通过简单的查询相关模块(如ngx_http_gzip_filter_module、ngx_http_image_filter_module等)的配置项说明,方便地在nginx.conf配置文件中加入新的配置项,从而实现更多的Web服务器功能。

除了2.3节提到的基本配置项外,一个典型的静态Web服务器还会包含多个server块和location块,例如:


http{

gzip on;

upstream{

……

}……

server{

listen localhost:80;

……

location/webstatic{

if……{

……

}

root/opt/webresource;

……

}

location~*.(jpg|jpeg|png|jpe|gif)${

……

}

}

server{

……

}

}


所有的HTTP配置项都必须直属于http块、server块、location块、upstream块或if块等(HTTP配置项自然必须全部在http{}块之内,这里的“直属于”是指配置项直接所属的大括号对应的配置块),同时,在描述每个配置项的功能时,会说明它可以在上述的哪个块中存在,因为有些配置项可以任意地出现在某一个块中,而有些配置项只能出现在特定的块中,在第4章介绍自定义配置项的读取时,相信读者就会体会到这种设计思路。

Nginx为配置一个完整的静态Web服务器提供了非常多的功能,下面会把这些配置项分为以下8类进行详述:虚拟主机与请求的分发、文件路径的定义、内存及磁盘资源的分配、网络连接的设置、MIME类型的设置、对客户端请求的限制、文件操作的优化、对客户端请求的特殊处理。这种划分只是为了帮助大家从功能上理解这些配置项。

在这之后会列出ngx_http_core_module模块提供的变量,以及简单说明它们的意义。

2.4.1 虚拟主机与请求的分发

由于IP地址的数量有限,因此经常存在多个主机域名对应着同一个IP地址的情况,这时在nginx.conf中就可以按照server_name(对应用户请求中的主机域名)并通过server块来定义虚拟主机,每个server块就是一个虚拟主机,它只处理与之相对应的主机域名请求。这样,一台服务器上的Nginx就能以不同的方式处理访问不同主机域名的HTTP请求了。

(1)监听端口

语法:listen address:port[default(deprecated in 0.8.21)|default_server|[backlog=num|rcvbuf=size|sndbuf=size|accept_filter=filter|deferred|bind|ipv6only=[on|off]|ssl]];

默认:listen 80;

配置块:server

listen参数决定Nginx服务如何监听端口。在listen后可以只加IP地址、端口或主机名,非常灵活,例如:


listen 127.0.0.1:8000;

listen 127.0.0.1;#注意:不加端口时,默认监听80端口

listen 8000;

listen*:8000;

listen localhost:8000;


如果服务器使用IPv6地址,那么可以这样使用:


listen[:]:8000;

listen[fe80:1];

listen[:a8c9:1234]:80;


在地址和端口后,还可以加上其他参数,例如:


listen 443 default_server ssl;

listen 127.0.0.1 default_server accept_filter=dataready backlog=1024;


下面说明listen可用参数的意义。

❑default:将所在的server块作为整个Web服务的默认server块。如果没有设置这个参数,那么将会以在nginx.conf中找到的第一个server块作为默认server块。为什么需要默认虚拟主机呢?当一个请求无法匹配配置文件中的所有主机域名时,就会选用默认的虚拟主机(在11.3节介绍默认主机的使用)。

❑default_server:同上。

❑backlog=num:表示TCP中backlog队列的大小。默认为-1,表示不予设置。在TCP建立三次握手过程中,进程还没有开始处理监听句柄,这时backlog队列将会放置这些新连接。可如果backlog队列已满,还有新的客户端试图通过三次握手建立TCP连接,这时客户端将会建立连接失败。

❑rcvbuf=size:设置监听句柄的SO_RCVBUF参数。

❑sndbuf=size:设置监听句柄的SO_SNDBUF参数。

❑accept_filter:设置accept过滤器,只对FreeBSD操作系统有用。

❑deferred:在设置该参数后,若用户发起建立连接请求,并且完成了TCP的三次握手,内核也不会为了这次的连接调度worker进程来处理,只有用户真的发送请求数据时(内核已经在网卡中收到请求数据包),内核才会唤醒worker进程处理这个连接。这个参数适用于大并发的情况下,它减轻了worker进程的负担。当请求数据来临时,worker进程才会开始处理这个连接。只有确认上面所说的应用场景符合自己的业务需求时,才可以使用deferred配置。

❑bind:绑定当前端口/地址对,如127.0.0.1:8000。只有同时对一个端口监听多个地址时才会生效。

❑ssl:在当前监听的端口上建立的连接必须基于SSL协议。

(2)主机名称

语法:server_name name[……];

默认:server_name"";

配置块:server

server_name后可以跟多个主机名称,如server_name www.testweb.com、download.testweb.com;。

在开始处理一个HTTP请求时,Nginx会取出header头中的Host,与每个server中的server_name进行匹配,以此决定到底由哪一个server块来处理这个请求。有可能一个Host与多个server块中的server_name都匹配,这时就会根据匹配优先级来选择实际处理的server块。server_name与Host的匹配优先级如下:

1)首先选择所有字符串完全匹配的server_name,如www.testweb.com。

2)其次选择通配符在前面的server_name,如*.testweb.com。

3)再次选择通配符在后面的server_name,如www.testweb.*。

4)最后选择使用正则表达式才匹配的server_name,如~^.testweb.com$。

实际上,这个规则正是7.7节中介绍的带通配符散列表的实现依据,同时,在10.4节也介绍了虚拟主机配置的管理。如果Host与所有的server_name都不匹配,这时将会按下列顺序选择处理的server块。

1)优先选择在listen配置项后加入[default|default_server]的server块。

2)找到匹配listen端口的第一个server块。

如果server_name后跟着空字符串(如server_name"";),那么表示匹配没有Host这个HTTP头部的请求。

注意 Nginx正是使用server_name配置项针对特定Host域名的请求提供不同的服务,以此实现虚拟主机功能。

(3)server_names_hash_bucket_size

语法:server_names_hash_bucket_size size;

默认:server_names_hash_bucket_size 32|64|128;

配置块:http、server、location

为了提高快速寻找到相应server name的能力,Nginx使用散列表来存储server name。server_names_hash_bucket_size设置了每个散列桶占用的内存大小。

(4)server_names_hash_max_size

语法:server_names_hash_max_size size;

默认:server_names_hash_max_size 512;

配置块:http、server、location

server_names_hash_max_size会影响散列表的冲突率。server_names_hash_max_size越大,消耗的内存就越多,但散列key的冲突率则会降低,检索速度也更快。server_names_hash_max_size越小,消耗的内存就越小,但散列key的冲突率可能增高。

(5)重定向主机名称的处理

语法:server_name_in_redirect on|off;

默认:server_name_in_redirect on;

配置块:http、server或者location

该配置需要配合server_name使用。在使用on打开时,表示在重定向请求时会使用server_name里配置的第一个主机名代替原先请求中的Host头部,而使用off关闭时,表示在重定向请求时使用请求本身的Host头部。

(6)location

语法:location[=|~|~*|^~|@]/uri/{……}

配置块:server

location会尝试根据用户请求中的URI来匹配上面的/uri表达式,如果可以匹配,就选择location{}块中的配置来处理用户请求。当然,匹配方式是多样的,下面介绍location的匹配规则。

1)=表示把URI作为字符串,以便与参数中的uri做完全匹配。例如:


location=/{

只有当用户请求是/时,才会使用该location下的配置

……

}


2)~表示匹配URI时是字母大小写敏感的。

3)~*表示匹配URI时忽略字母大小写问题。

4)^~表示匹配URI时只需要其前半部分与uri参数匹配即可。例如:


location^~/images/{

以/images/开始的请求都会匹配上

……

}


5)@表示仅用于Nginx服务内部请求之间的重定向,带有@的location不直接处理用户请求。

当然,在uri参数里是可以用正则表达式的,例如:


location~*.(gif|jpg|jpeg)${

匹配以.gif、.jpg、.jpeg结尾的请求

……

}


注意,location是有顺序的,当一个请求有可能匹配多个location时,实际上这个请求会被第一个location处理。

在以上各种匹配方式中,都只能表达为“如果匹配……则……”。如果需要表达“如果不匹配……则……”,就很难直接做到。有一种解决方法是在最后一个location中使用/作为参数,它会匹配所有的HTTP请求,这样就可以表示如果不能匹配前面的所有location,则由“/”这个location处理。例如:


location/{

/可以匹配所有请求

……

}