Nginx核心技术

87 阅读6分钟

一、关键文件、目录和命令

NGINX 文件和目录

/etc/nginx

服务器的默认配置根

/etc/nginx/nginx.conf

默认配置文件

/etc/nginx/conf.d/

目录包含默认的HTTP服务器配置文件

/var/log/nginx/

nginx的默认日志位置

PS:以上默认目录和文件均可在安装nginx或者nginx.conf文件修改

二、nginx命令

nginx -h

显示NGINX帮助菜单

nginx -v

显示NGINX版本

nginx -V

显示NGINX版本、build信息和配置参数

nginx -t

测试NGINX配置

nginx -T

测试NGINX配置并将验证后的配置打印到屏幕上。

nginx -s signal

-s 标记向 NGINX master 进程发送信号。您可以发送 stop、quit、reload 和reopen 等信号。stop 信号可立即停止 NGINX 进程。quit 信号会在完成当前正在处理的请求后停止 NGINX 进程。reload 信号可重新加载配置。reopen 信号指示NGINX 重新打开日志文件。

三、获取客户端真实IP

1.配置nginx

首先,我们需要在Nginx的配置文件nginx.conf中添加如下配置

proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

各参数的含义如下所示。

  • Host包含客户端真实的域名和端口号;
  • X-Forwarded-Proto表示客户端真实的协议(http还是https);
  • X-Real-IP表示客户端真实的IP;
  • X-Forwarded-For这个Header和X-Real-IP类似,但它在多层代理时会包含真实客户端及中间每个代理服务器的IP。

2.通过Java方法获取客户端信息

仅仅配置Nginx不能彻底解决问题,那如何才能解决这个问题呢?一种解决方式就是通过Java方法获取客户端信息,例如下面的Java方法。

/***
 * 获取客户端IP地址;这里通过了Nginx获取;X-Real-IP
 */
public static String getClientIP(HttpServletRequest request) {
    String fromSource = "X-Real-IP";
    String ip = request.getHeader("X-Real-IP");
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("X-Forwarded-For");
        fromSource = "X-Forwarded-For";
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("Proxy-Client-IP");
        fromSource = "Proxy-Client-IP";
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("WL-Proxy-Client-IP");
        fromSource = "WL-Proxy-Client-IP";
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getRemoteAddr();
        fromSource = "request.getRemoteAddr";
    }
    return ip;
}

这种方式虽然能够获取客户端的IP地址,但是我总感觉这种方式不太友好,因为既然Servlet API提供了request.getRemoteAddr()方法获取客户端IP,那么无论有没有用反向代理对于代码编写者来说应该是透明的。

接下来,就Tomcat服务器为大家介绍下如何进行配置才能更加友好的获取客户端信息。

3.Tomcat服务器

使用Tomcat作为应用服务器,可以通过配置Tomcat的server.xml文件,在Host元素内最后加入:

<Valve className="org.apache.catalina.valves.RemoteIpValve" />

四、负载均衡

HTTP配置

upstream backend{
    server 10.10.12.45:80 weight=1;
    server app.example.com:80 weight=2;
    server spare.example.com:80 backup;
}
server{
    location / {
        proxy_pass http://backend;
    }
}

HTTP 的 upstream 模块控制着 HTTP 负载均衡。该模块定义了一个目标池 -- 它可以是 Unix 套接字、IP 地址和 DNS(域名服务)记录的任意组合,也可以是它们的混合使用配置。upstream 模块还定义了如何将任一个请求分发给任何上游(upstream)服务器。

负载均衡方式

例如最少连接、最短时间、通用哈希、随机算法或IP哈希

最少连接案例如下

upstream backend{
    least_conn;
    server backend1.example.com;
    server backend2.example.com;
}

各方式详解

轮询

轮询是默认的负载均衡方式,还可以在轮询中加入权重,权重背后的算法只是加权平均值的统计概率。

最少连接

least_conn,顾名思义将当前请求代理到连接数最少的服务器,也会考虑权重

最短时间

least_time,与最少连接算法类似,它将请求代理到当前连接数最少的服务器,但首选平均响应时间最短的。此方法是最复杂的负载均衡算法之一,能够满足高性能 Web 应用的需求。最短时间在最少连接的基础上进行了优化,因为少量连接并不一定意味着最快的响应。使用此算法时,切记要考虑服务请求时间的统计差异。

通用哈希

hash,使用请求或运行时给定的文本、变量或两者的组合定义哈希值。NGINX 能够为当前请求生成哈希值并将其放在服务器上,从而在这些服务器之间分发负载。

随机

random,该方法用于指示 NGINX 从组中随机选择一台服务器,同时考虑服务器的权重。

IP哈希

ip_hash,此方法仅适用于 HTTP。IP 哈希算法使用客户端 IP 地址作为哈希。IP 哈希与通用哈希存在细微的不同,前者使用 IPv4 地址的前三个八进制位或整个IPv6 地址,而后者使用的是远程变量。也会考虑权重

五、健康检查

被动健康检查

upstream backend {
   server backend1.example.com:1234 weight=1 max_fails=2 fail_timeout=60s;
   server backend2.example.com:1234 weight=2 max_fails=2 fail_timeout=60s;
}
​

意思是在fail_timeout时间内失败了max_fails次请求后,则认为该上游服务器不可用,然后将该服务地址踢除掉。fail_timeout时间后会再次将该服务器加入存活列表,进行重试。

Nginx-plus主动健康检查

HTTP方式

http{
    server{
        #...
        location / {
            proxy_pass http://backend;
            health_check interval=2s;
                fails=2
                passes=5
                uri=/
                match=welcome;
        }
    }
    # 状态码是200,内容类型是"text/html"
    # 正文包含"Welcome to nginx!"
    match welcome{
        status 200;
        header Content-type = text/html;
        body ~ "Welcome to nginx!";
    }
}

此处的 HTTP 服务器健康检査配置通过每 2 秒向 UR!“!”发送 HTTP GET 请求来检查上游服务器的健康状况。我们无法为健康检査定义 HTTP 方法,只能执行 GET 请求,因为其他方法可能会更改后端系统的状态。上游服务器只有连续通过五次健康检查才能被认为是健康的。如果它们连续两次未通过检查,则被认定为不健康。上游服务器的响应必须匹配定义的 match 代码块,后者将状态码定义为 200,将请求头的Content-Type 值定义为'texthtm’,同时定义了响应正文中的字符串“Welcome tonginx!”。HTTP match 代码块具有三个指令:status、header 和 body。这三个指令均带有比较标记。

六、Nginx限流配置

配置参数

limit_req_zone只领设置参数

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
  • limit_req_zone定义在http块中,$binary_remote_addr表示保存客户端IP地址的二进制形式。
  • Zone定义IP状态及URL访问频率的共享内存区域。zone=keyword标识区域的名字,以及冒号后面跟区域大小。16000个IP地址的状态信息约1MB,所以示例中区域可以存储160000个IP地址。
  • Rate定义最大请求速率。示例中速率不能超过每秒10个请求

设置限流

location / {
        limit_req zone=mylimit burst=20 nodelay;
        proxy_pass http://real_server;
}
​

burst排队大小,nodelay不限制单个请求间的时间

不限流白名单

geo $limit {
    default              1;
    192.168.2.0/24  0;
}
 
map $limit $limit_key {
    1 $binary_remote_addr;
    0 "";
}
 
limit_req_zone $limit_key zone=mylimit:10m rate=1r/s;
 
location / {
        limit_req zone=mylimit burst=1 nodelay;
        proxy_pass http://real_server;
}
​

上述配置中,192.168.2.0/24网段的IP访问是不限流的,其他限流。

IP后面的数字含义:

  • 24表示子网掩码:255.255.255.0
  • 16表示子网掩码:255.255.0.0
  • 8表示子网掩码:255.0.0.0

七、缓存配置

浏览器配置

静态资源缓存用expire

location ~*  .(jpg|jpeg|png|gif|ico|css|js)$ {
   expires 2d;
}
# d表示天,day

Response Header中添加了Expires和Cache-Control,

静态资源包括(一般缓存)

  • 普通不变的图像,如logo,图标等
  • js、css静态文件
  • 可下载的内容,媒体文件

协商缓存(add_header ETag/Last-Modified value)

  • HTML文件
  • 经常替换的图片
  • 经常修改的js、css文件
  • 基本不变的API接口

不需要缓存

  • 用户隐私等敏感数据
  • 经常改变的api数据接口

Nginx黑名单

一般配置

location / {
    deny  192.168.1.1;
    deny 192.168.1.0/24;
    allow 10.1.1.0/16;
    allow 2001:0db8::/32;
    deny  all;
}
​