Nginx简介
NGINX 是一种高性能的反向代理服务器、负载均衡器和 HTTP 缓存服务器。它的设计初衷是为了应对高并发和低资源消耗,尤其适合处理大量的短连接请求。NGINX 的高效性能来自其事件驱动架构和异步非阻塞的处理方式。
Nginx工作原理
1.事件驱动模型
Nginx使用的是一种基于事件的异步非阻塞模型,也就是它能够在处理客户端请求时,不会因为等待某个操作(如I/O操作)而阻塞整个进程。相比传统的基于线程或进程的模型(如Apache),这种方式极大地提高了资源利用率。
事件循环:Nginx采用事件驱动机制管理连接,当有事件发生时,它会被通知并执行相应的操作。例如,一个新的客户端连接会触发accept事件,数据的读取会触发read事件。
异步非阻塞:在等待I/O操作时(如从磁盘读取文件或向客户端发送数据),不会阻塞其他操作。即使正在处理大量请求,单个请求的延迟也会尽可能保持较低。
2.Master-Worker多进程架构
Nginx采用了Master-Worker进程模型,Master进程负责管理Worker进程(包括启动、停止和监控等),而Worker进程负责处理实际的客户端请求。
Master进程:负责配置加载、管理Worker进程等。
Worker进程:负责处理客户端的连接。每个Worker进程独立处理请求,并且它们之间不共享任何状态。通过这种设计,NGINX能够在不锁住资源的情况下处理大量并发请求。
3.反向代理和负载均衡
Nginx主要用于反向代理服务器,即它会接收客户端的请求,转发到后端服务器,并将后端服务器的响应返回给客户端。这使得NGINX成为一个强大的负载均衡器,可以将流量分配给多个后端服务器,以实现更高的可用性和扩展性。
反向代理:Nginx接受客户端请求,将请求转发给后端服务器,并返回后端服务器的响应。
负载均衡算法:它支持多种负载均衡策略,比如轮询(RoundRobin)、权重轮询(Weighted Round Robin)、IP 哈希(IP Hash)、最少连接(LeastConnections)以及第三方模块提供的Fair、URL Hash等。
4.高并发处理
Nginx由于其事件驱动模型和多进程架构,特别适合处理高并发连接。例如,在高并发场景下,Nginx可以在一个工作进程中处理成千上万的连接,而不会因为线程或进程上下文切换导致性能下降。
5.静态资源缓存
Nginx还可以作为一个静态内容服务器,特别擅长处理静态资源如图片、CSS、JavaScript文件的缓存。通过缓存策略,它可以极大地减少后端服务器的负载,并提升整体的响应速度。
6.模块化设计
Nginx支持高度模块化,用户可以根据需求选择或编写自定义的模块。例如,使用SSL模块处理HTTPS请求,或者使用Gzip模块进行压缩等。
Nginx执行流程
在 NGINX 中,处理客户端请求时,它的执行顺序主要遵循以下几个阶段。NGINX 的执行流程是基于事件驱动的,因此它对请求的处理遵循模块化的方式。每个请求会经过一系列预定义的阶段,确保请求按照正确的顺序被处理。
1.接收请求
NGINX通过其事件驱动的架构接收到客户端请求。此时,主进程负责接收连接,并将请求分配给一个可用的工作进程(Worker)。请求被交给Worker后,进入请求处理流程。
2.选择服务器块(server block)
NGINX首先会在配置文件中根据客户端请求的主机名和端口号,查找最合适的server块来处理请求。如果请求中包含了匹配的server_name和listen端口,NGINX将确定使用哪个server块来继续处理请求。
>按照listen指令和server_name指令来匹配。
>如果没有明确匹配的server_name,则使用default的server块。
3.选择位置块(location block)
在确定了合适的server块后,NGINX接下来会根据请求的URI路径,查找最匹配的location块。location匹配返回顺序如下(重要):
精确匹配
如果使用=符号开头的location,表示精确匹配整个URI。
这种匹配优先级最高,如果找到,则立即使用该location,并停止进一步的匹配。
location = / {
处理根路径的请求
}
带^~的前缀匹配
^~表示如果某个前缀匹配成功,不再进行正则表达式匹配,即优先匹配前缀。
这是用来指示NGINX优先使用前缀匹配而不是正则匹配的方式。
location ^~ /static/ {
匹配所有以/static/开头的URI,并停止继续查找正则表达式匹配
}
正则表达式匹配
正则表达式匹配用于更加复杂的匹配规则。正则匹配在前缀匹配之后进行。
~用于大小写敏感的正则表达式匹配。
~*用于大小写不敏感的正则表达式匹配。
正则表达式从上到下匹配,不同的正则表达式在Nginx配置文件中的出现顺序,会影响匹配结果。
location ~ \.php$ {
匹配所有以.php结尾的请求 (大小写敏感)
}
location ~* \.(jpg|jpeg|png)$ {
匹配所有以.jpg, .jpeg, .png结尾的请求 (大小写不敏感)
}
前缀匹配(常规匹配)
NGINX默认情况下按前缀匹配。即URI以某个字符串开头,匹配定义的location块。
NGINX会找到最长的前缀匹配,所以常规匹配在****Nginx 配置文件中没有先后顺序要求。
location /images/ {
处理以 /images/ 开头的所有请求,不包括/images/1/
}
location /images/1/ {
处理以/images/1/开头的所有请求
}
通用匹配
如果没有其他location块匹配到URI,/会作为兜底匹配项。这是最通用的匹配,表示匹配所有请求。
location / {
匹配所有没有被其他 location 块处理的请求
}
4.处理重写规则(rewrite directives)
如果在server或location块中有rewrite指令,Nginx将在此时执行重写规则,改变URI并可能重新选择一个合适的location。重写规则允许修改请求的URI或跳转到其他处理方式。
5.处理访问控制
在处理完重写后,NGINX会检查任何访问控制相关的配置,比如IP限制、允许/禁止访问的规则(allow和deny指令)。如果请求被拒绝,NGINX会返回相应的错误响应。
6.处理代理或静态文件
根据location块的配置,NGINX可能会执行不同的操作,如:
静态文件处理:如果请求的是静态文件,NGINX会直接从文件系统中读取并返回该文件。
反向代理:如果proxy_pass配置存在,NGINX会将请求转发给后端服务器,并将后端的响应返回给客户端。
FastCGI/uwsgi等应用层处理:如果配置了FastCGI、uwsgi等,NGINX会将请求转发给这些处理器。
7.执行过滤器
NGINX可能会对响应进行多种过滤处理,比如:压缩响应数据(如启用了gzip)、添加或修改响应头信息、其他响应内容的处理。这些过滤器按照一定的顺序执行,确保响应在返回客户端之前被适当修改。
8.发送响应
在完成请求处理后,NGINX将结果返回给客户端。响应可以是:来自后端服务器的动态内容;从缓存或文件系统中获取的静态内容;错误页面(如404或500)等。
9.记录日志
最后,Nginx会根据配置将请求的处理结果记录到访问日志中(如access_log和error_log),便于后续分析和排错。
Nginx常用配置
1.反向代理负载均衡
轮询(Round Robin),默认算法,将请求依次分发到每台服务器
upstream backend {
server backend1.example.com;
server backend2.example.com;
}
server {
location /api {
proxy_pass http://backend/api ;
}
}
权重轮询(Weighted Round Robin),根据权重分配请求,权重越高,分配的请求越多
upstream backend {
server backend1.example.com weight=3;
server backend2.example.com weight=1;
}
server {
location /api {
proxy_pass http://backend/api ;
}
}
IP 哈希(IP Hash),根据客户端的IP地址进行哈希计算,将请求分发到固定的服务器上
upstream backend {
ip_hash;
server backend1.example.com;
server backend2.example.com;
}
server {
location /api {
proxy_pass http://backend/api ;
}
}
最少连接数(Least Connections),将请求分发到当前连接数最少的服务器上
upstream backend {
least_conn;
server backend1.example.com;
server backend2.example.com;
}
server {
location /api {
proxy_pass http://backend/api ;
}
}
Fair,第三方模块,智能负载均衡算法,根据后端服务器的响应时间和负载情况来分配请求。与传统的轮询算法不同,Fair策略会优先将请求分配给响应时间较短、负载较轻的服务器,从而提高整体系统的性能和响应速度
upstream backend {
fair;
server backend1.example.com;
server backend2.example.com;
}
server {
location /api {
proxy_pass http://backend/api ;
}
}
URL Hash,第三方模块,通过对请求的URL进行哈希计算,将请求分配到特定的服务器上。这种策略特别适用于需要提高缓存命中率的场景
upstream backend {
hash $request_uri;
server backend1.example.com;
server backend2.example.com;
}
server {
location /api {
proxy_pass http://backend/api ;
}
}
2.日志输出
输出格式
Nginx的日志输出为JSON格式是一个常见的需求,因为JSON格式的日志便于日志管理工具(如 ELK Stack)解析和分析。为了让Nginx以JSON格式输出日志,可以按照以下步骤配置:
备份并编辑Nginx配置文件(通常是 /etc/nginx/nginx.conf
)
修改 log_format
指令,将其格式化为 JSON 格式
http {
log_format json_combined escape=json '{'
'"time_local": "$time_local",'
'"remote_addr": "$remote_addr",'
'"remote_user": "$remote_user",'
'"request": "$request",'
'"status": "$status",'
'"body_bytes_sent": "$body_bytes_sent",'
'"http_referer": "$http_referer",'
'"http_user_agent": "$http_user_agent",'
'"http_x_forwarded_for": "$http_x_forwarded_for",'
'"request_time": "$request_time",'
'"upstream_response_time": "$upstream_response_time",'
'"upstream_addr": "$upstream_addr",'
'"upstream_status": "$upstream_status",'
'"host": "$host"'
'}';
access_log /var/log/nginx/access.log json_combined;
}
输出字段
Nginx默认的日志输出字段太少,不便于排查问题,流量分析等,我们可以添加输出的字段,例如:
$host:请求主机头字段,否则为服务器名称
$upstream_status:记录上游服务器返回的HTTP状态码
$upstream_addr:记录处理请求的上游服务器的地址
$upstream_response_time:记录从上游服务器接收到响应的时间
$http_x_forwarded_for: 客户端的真实ip(非常有用,根据返回的ip层级数量,可以筛选安全攻击事件)
$server_addr:服务器地址,在完成一次系统调用后可以确定这个值
$server_name:服务器名称
$server_port:请求到达服务器的端口号
$request_uri:包含请求参数的原始URI
$http_user_agent:客户端agent信息
$request_body:记录POST请求的body内容,这对于调试和监控非常有帮助
3.重写功能
rewrite
Nginx的rewrite指令用于基于正则表达式修改请求的URI。它可以在server
、location
或if
块中使用,并支持多种标志(flags)来控制重写行为,以下是一些关键知识点和配置示例:
rewrite [pattern] [replacement] [flags];
pattern:要匹配的URI模式可以使用正则表达式。
replacement:重写后的URI。
flags:控制重写行为的标志如last、break、redirect、permanent等。
其中几种flags的说明如下:
last :停止处理当前的重写规则,并重新发起一个新的请求来匹配新的location。这意味着重写后的URL会被重新解析并匹配新的location块。
break :停止处理当前的重写规则,但不会重新发起新的请求。这意味着重写后的URL不会被重新解析,只会在当前的location块内继续处理。
redirect :返回302临时重定向。
permanent:返回301永久重定向。
两个示例
location /old-url/ {
rewrite ^/old-url/(.*)$ /new-url/$1 permanent;
}
#将所有以
/
old-url开头的请求重写为/
new-url开头,并返回301永久重定向
if ($http_user_agent ~* "iPhone") {rewrite ^(.*)$ /mobile$1 break;
}
#iPhone用户的请求进行重写为/mobile开头
return
Nginx的return指令用于立即返回一个HTTP状态码和可选的响应体。它可以在server
或location
块中使用,常用于简单的重定向或返回特定的状态码:
server {
listen 80;
server_name example.com;
return 301 https://server_namerequest_uri;
}
#将所有HTTP请求重定向到HTTPS,并返回301永久重定向
缓存控制
浏览器缓存控制
浏览器缓存可以通过设置HTTP头部字段来实现,主要包括强缓存和协商缓存
强缓存指的是浏览器在缓存有效期内直接从缓存中读取资源,而不与服务器进行任何通信。可以通过以下指令配置:
location / {
expires 30d; # 设置缓存时间为30天
add_header Cache-Control "public, max-age=2592000"; # 30天的秒数
}
协商缓存指的是浏览器在每次请求资源时,都会向服务器确认资源是否更新。常用的HTTP头部字段包括Last-Modified和ETag
location / {
add_header Last-Modified $date_gmt;
add_header ETag $request_uri;
if_modified_since exact;
etag on;
}
代理缓存控制
Nginx还可以作为反向代理服务器缓存后端服务器的响应。首先,定义缓存的路径和一些参数:
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;
#/data/nginx/cache
:缓存文件存储的路径。
#levels=1:2
:缓存目录的层级结构,防止单个目录中包含过多文件。
#keys_zone=my_cache:10m
:定义一个共享内存区域,用于存储缓存键和元数据。
#max_size=10g
:缓存的最大大小。
#inactive=60m
:未被访问的缓存项在60分钟后会被删除。
#use_temp_path=off
:将缓存文件直接写入最终目录,避免不必要的数据复制
然后,在需要缓存的location块中启用代理缓存:
server {
listen 80;
server_name example.com;
location / {
proxy_cache my_cache;
proxy_pass http://backend_server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
}
}
#proxy_cache my_cache
:启用之前定义的缓存区域。
#proxy_pass http://backend_server
:将请求转发到后端服务器。
#proxy_set_header
:设置一些头部信息。
#proxy_cache_valid
:定义不同响应状态码的缓存时间。
#proxy_cache_use_stale
:在后端服务器出现错误或超时时,使用过期的缓存内容。
访问控制
Nginx提供了强大的访问控制功能,可以通过 allow
和deny
指令来基于IP地址进行访问控制。通过这些指令,你可以允许或禁止特定IP地址或IP段访问你的Nginx服务器资源。allow
和 deny
可以在http
、server
或location
块中使用,以下是几个示例
location /admin {
deny all; # 禁止所有 IP 访问
allow 192.168.1.1; # 允许特定 IP 访问
}
location / {allow all; # 允许所有 IP 访问
deny 192.168.1.100; # 禁止特定 IP 访问
}
http {allow 192.168.1.0/24; # 全局允许指定 IP 段访问
deny all; # 全局禁止其他 IP 访问
}