Nginx 那些事

Nginx 是什么

Nginx(即 [engine x]),它是一个基于异步框架的网页服务器,同时,它也可以用作反向代理服务器、负载均衡服务器、邮件代理服务器和通用 TCP/UDP 代理服务器,最初由俄罗斯程序员 Igor Sysoev,它是一个基于异步框架的网页服务器,同时,它也可以用作反向代理服务器、负载均衡服务器、邮件代理服务器和通用 TCP/UDP 代理服务器,最初由俄罗斯程序员 [Igor Sysoev")编写实现。它在 Yandex[1]、 Mail.Ru[2]等网站均有应用。

根据权威机构 Netcraft[3] 2022年8月26日发布的最新调查数据[4]显示,Nginx 服务和代理已经超越 Apache,在主流网站中市场份额中占有率最高。可参考下图:

为什么使用 Nginx

Nginx 最核心的是高性能,它可以让 Web 服务器在高并发压力下正常提高服务。基于事件驱动型设计、全异步的网络 I/O 处理机制、极少的进程间切换以及许多优化设计,使得 Nginx 天生善于处理高并发压力下的网络请求,同时Nginx降低了资源消耗,可以把服务器硬件资源发挥到极致。Nginx 的主要特性可概括如下:

图片

nginx_function

如何使用

Nginx 安装

首先是 Nginx 的安装,可以从 `Nginx`官网[5]下载安装,具体安装步骤这里就不展开讲了(如果需要自己开发或者自定义 Nginx 相关模块,可以选择从源码构建,需要安装GCCPCREzlibOpenSSL等相关库,这里先作为了解,不展开)

Nginx 配置文件

首先打开 nginx.conf,通过命令行 vi /usr/local/etc/nginx/nginx.conf 查看配置项:

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       8080;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
    include servers/*;
}

常见命令

我们可以通过命令行 nginx -h,查看 Nginx 常见命令,如下

图片

nginx_help

对于 Nginx 来讲,日常一般应用使用最高频的配置文件就是 nginx.conf 了,这里我们以更改该配置文件为例,了解下会涉及到哪些命令行操作。

我们先将 nginx.conf 中监听的服务端口号改为 8082,目的是将 Nginx 的服务将由默认的 http://localhost:80/ 转移到 http://localhost:8082/,配置更改如下

server {
    listen 8082;
}

由于 Nginx 对配置文件有严格的缩紧语法要求,所以当我们更改了配置文件后,可以通过命令行:

nginx -t

对配置文件格式进行正确性检测,可以看到如下信息,表示配置文件语法正确无误:

nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful

接着,我们需要重新加载 Nginx 配置,才能使刚才的更改生效,如下

nginx -s reload

生效后,我们访问 http://localhost:8082/ 页面,可以看到 Nginx 默认页面已出现,配置已经生效

图片

nginx-port

如果我们要强制停止运行该服务,可使用

nginx -s stop

-s 参数其实是告诉 Nginx 程序向正在运行的 Nginx 服务发送信号量,Nginx 程序通过 nginx.pid 文件中得到 master 进程的进程 ID,再向运行中的 master 进程发送 TERM 信号来快速地关闭该服务。

我们也可以通过 kill 命令直接向 nginx master 进程发送 TERM 或者 INT 信号,如下

# 先查看当前运行进程
ps -ef | grep nginx

501 59054     1   0 10:21上午 ??         0:00.01 nginx: master process nginx -q 
501 60079 59054   0 10:54上午 ??         0:00.01 nginx: worker process 

# 直接 kill
kill -s SIGINT 59054 
or 
kill -s SIGTERM 59054

如果希望 Nginx 服务可以正常地处理完当前所有请求再停止服务,可以使用

nginx -s quit

其他命令可以使用 nginx -h 命令查询使用。

常见配置有哪些

静态资源文件

Nginx 作为网页服务器,可以指定静态资源文件所在位置,具体配置如下:

location / {
    #指定静态资源文件到桌面的 index.html 文件
    root   /Users/apple/Desktop/;
    index  index.html index.htm;
}

nginx -s reload 加载配置,查看页面,可以看到页面已经从默认页转到了我们指定的页面

图片

gzip 压缩

在日常开发过程中,我们经常会涉及到性能优化,特别是针对服务端返回的内容,而在 Nginx 中开启 gzip 就是一个有效的优化手段。

nginx gzip 配置可以参考 ngx_http_gzip_module[6],示例配置如下:

#是否开启gzip模块,on表示开启,off表示关闭
gzip  on;
##设置允许压缩的页面最小字节(从header头的Content-Length中获取) ,当返回内容大于此值时才会使用gzip进行压缩,以K为单位,当值为0时,所有页面都进行压缩。建议大于1k
gzip_min_length 1000;
##设置用于处理请求压缩的缓冲区数量和大小。比如32 4K表示按照内存页(one memory page)大小以4K为单位(即一个系统中内存页为4K),申请32倍的内存空间,建议使用默认值
gzip_buffers  32 4k;
#用于识别http协议的版本,默认在http/1.0的协议下不开启gzip压缩
gzip_http_version  1.0;
#指定压缩的MIME类型,线上配置时尽可能配置多的压缩类型
gzip types  text/plain application/xml text/css text/xml application/javascript;
#可以让前端的缓存服务器缓存经过gzip压缩的页面; 表示在传送数据时,给客户端说明使用了gzip压缩
gzip_vary  on
#根据请求和响应启用或禁用代理请求的响应gzip压缩。请求被代理的事实是由Via请求头字段的存在决定的
gzip_proxied  expired no-cache no-store private auth;

开启压缩功能后,可以使用 ab(Apache bench) 命令进行压测,验证效果。

基于客户端 IP 地址的访问控制

Nginx 可以通过 ngx_http_access_module[7] 模块实现基于客户端 IP 地址的访问控制,配置如下:

location / {
    allow 10.37.129.2;
    allow 192.168.31.246;
    deny all;
}

通过以上配置,我们就可以实现访问 IP 白名单或者说黑名单,仅仅放行 10.37.129.2&1192.168.31.246 这两个 IP进行访问,其余 IP 一律返回 403 Forbidden,当然,也可以配置 IP 网段进行控制。

响应速度限制

结合 IP 地址的访问控制,更进一步,如果我们想要防止多个用户下载时占用过高的带宽,可以针对指定资源文件进行限速处理,比如我们针对 mp4 类型文件进行限制,当下载文件大小超过 500k 时,限制其下载速率为 50k,这里是不是可以联想到某知名网盘😂,可如下配置:

location /mp4/ {
    mp4;
    limit_rate_after 500k;
    limit_rate       50k;
}

限制来自同一个地址的同时连接或请求的数量

如果有恶意攻击者对 Web 应用发起流量攻击,短时间内使用脚本无限制地请求Web 应用地址,就会造成该应用带宽流量上升,无法响应其他正常用户的请求,这时候我们可以使用ngx_http_limit_conn_module[8]和ngx_http_limit_req_module[9]针对同一个地址的同时连接和请求的数量进行限制,可参考如下配置

  • 同一个地址的同时连接个数限制为 5
http {
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    ...
    server {
        ...
        location /download/ {
            limit_conn addr 5;
            #限制与虚拟服务器的连接总数
            limit_conn perserver 100;
        }
    }
}

  • 同一个地址的同时请求的数量限制为 5,即平均每秒不超过 5 个请求,并且突发不超过 50 个请求,这里的限制机制采用了 leaky bucket(漏桶算法)[10]算法完成。
http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
    ...
    server {
        ...
        location /search/ {
            limit_req zone=one burst=50;
        }
    }
}

基于 IP 的地理定位

最近一段时间,各个互联网平台都对用户 IP 地址进行了展示,那么如何实现基于 IP 的地理定位展示,Nginx 为我们提供了 ngx_http_geoip_module[11] 模块,通过解析 MaxMind GeoIP[12] 库,可以实现地理定位。也可以对指定区域或 IP 进行自定义处理,比如针对来自特定区域 IP 访问进行拦截。相关配置如下:

http {
    #根据客户端 IP 地址确定国家/地区的数据库
    geoip_country GeoIP.dat;
    #根据客户端 IP 地址确定国家、地区和城市的数据库
    geoip_city GeoLiteCity.dat;
    #定义可信地址
    geoip_proxy 192.168.100.0/24;
    #禁用递归搜索
    geoip_proxy_recursive on;
    ...

    server {
        ...
        #可自定义操作,如针对某个国家返回指定页面
        if ($geoip_country_code == 'AT'){
            return something;
        }
        #可自定义操作,如针对某个城市返回指定页面
        if ($geoip_city == 'Bonito'){
            return something; 
        }
    }
}

Nginx 默认情况下不构建此模块,应使用 --with-http_geoip_module 配置参数启用它

njs 脚本语言

通过基于 IP 的地理定位模块,我们知道 Nginx 可以处理一些简单逻辑模块,如果这里要处理的逻辑较多或者说比较复杂,那是不是可以使用现代化编程语言处理,并借用其相关类库,将一个个逻辑抽离到函数中呢?答案是可以的,Nginx 支持了 njs scripting language[13],可以通过 Javascript 语言来处理,比如我们想要将响应正文字符统一转换为小写,可以先写好 Javascript 文件,再引入使用,示例如下:

function to_lower_case(r, data, flags) {
    // 注意这里使用了JavaScript API toLowerCase() 函数,使用函数库,可以简化脚步语言的逻辑
    r.sendBuffer(data.toLowerCase(), flags);
}

export default {to_lower_case};

http {
js_path “/etc/nginx/njs/”;

js_import main from http/response/to_lower_case.js;

server {
listen 80;

    location / {
        #调用引入的JavaScript函数 to_lower_case
        js_body_filter main.to_lower_case;
        proxy_pass http://localhost:8080;
    }

}

server {
listen 8080;

    location / {
        return 200 'Hello World';
    }

}
}


> 更多 `njs` 示例,可以参考njs-examples\[14\]

### 反向代理

我们日常使用最多的莫过于 `Nginx` 的反向代理了,通过反向代理\[15\],可以隐藏服务端真实调用地址,还可以作为应用层防火墙,为网站的攻击行为提供前置防护。反向代理这块主要配置分为:`proxy_pass`、`proxy_cache`、`proxy_ssl`、`proxy_buffer`。

*   `proxy_pass`主要涉及代理转发相关配置
    
*   `proxy_cache`主要用于定义缓存的共享内存区域相关配置
    
*   `proxy_ssl` 主要用于 `HTTPS` 协议转发配置
    
*   `proxy_buffer` 主要用于缓冲区相关配置 常用反向代理配置如下:
    

location /name/ {
#配置反向代理服务目标地址,由 nginx 进行转发操作
proxy_pass http://127.0.0.1/remote/;
#另外,在转发过程中,也可以自定义转发头部字段,如下:
#默认情况下反向代理是不会转发请求中的Host头部的
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE_HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
}


> 更多配置请查看 http\_proxy\_module\[16\]

### 负载均衡

在微服务理念盛行的今天,负载均衡\[17\]的重要性更加凸显,负载均衡一般指在多个计算机(计算机集群)、网络连接、`CPU`、磁盘驱动器或其他资源中分配负载,以达到优化资源使用、最大化吞吐率、最小化响应时间、同时避免过载。可以使用 `NginX` 作为高效的 `HTTP` 负载均衡器,将客户端请求分布到多个应用服务器,并通过 `NginX` 提高 `Web` 应用程序的性能、可扩展性和可靠性。

如果存在多个后端服务 `S1`、`S2`、`S3`,我们可以通过设置负载均衡来调节用户访问,如下:

![图片](https://mmbiz.qpic.cn/mmbiz_jpg/MnVGSgCV6PL8syxRiaafjvicpw4yQiaD31QsqMWCgl30I7mV70m3FuYDKhBZpVV5kJlSFPZCoibSoZ5UfvtOibyB3PQ/640?wx_fmt=jpeg)

nginx-load-balance

`Nginx` 主要有三种负载均衡机制:

*   `round-robin`:即以轮询方式分发客户端的请求,可以参考如下配置:
    

upstream backend {
server backend.s1.com weight=5;
server backend.s2.com;
server backend.s3.com;
}

server {
location / {
proxy_pass http://backend;
}
}


请求使用加权循环平衡方法在服务器之间分配。在上面面的示例中,每 `7` 个请求将按的分配机制是:`5` 个请求到达 `backend.s1.com`, `1` 个请求到达 `backend.s2.com`,`1` 个请求到达 `backend.s3.com`。

*   least\_conn\[18\]: 即最少连接负载均衡。将请求传递到具有最少活动连接数的服务器,同时考虑服务器的权重。如果有多个这样的服务器,则使用加权循环平衡方法依次连接。配置如下:
    

upstream backend {
#使用最少连接策略
least_conn
server backend.s1.com weight=5;
server backend.s2.com;
server backend.s3.com;
}

server {
location / {
proxy_pass http://backend;
}
}


*   ip\_hash\[19\]:基于客户端 `IP` 地址在服务器之间分配请求。客户端 `IPv4` 地址或整个 `IPv6` 地址的**前三个八位字节**用作散列密钥。该方法确保来自同一客户端的请求将始终传递到同一服务器,除非该服务器不可用。在后一种情况下,客户端请求将被传递到另一台服务器。这样做的好处是客户端绑定到特定的应用服务器,可以保持持久会话,同时解决动态网页 `Session` 共享的问题,也可以有效地管理缓存信息。相关配置如下:
    

upstream backend {
#使用ip_hash策略
ip_hash;
server backend.s1.com weight=5;
server backend.s2.com;
server backend.s3.com;
}

server {
location / {
proxy_pass http://backend;
}
}


除此之外,我们还可以给设置服务器的 `keepalive` 会话个数和会话有效时间、`max_fails`设置持续时间内与服务器通信的不成功尝试次数、`fail_timeout`设置与服务器通信不成功的次数等。

> 更多配置请查看 ngx\_http\_upstream\_module\[20\]

### 其他

`Nginx` 还可以处理很多需求,如:图像转换\[21\]、正则匹配 URI\[22\]、支持 A/B 测试\[23\]等。

为什么 Nginx 这么高效
--------------

`Nginx` 性能的高效源自其架构设计的优势,主要包含以下几个点:

![图片](https://img-blog.csdnimg.cn/img_convert/bc652ea4fe88dc0c6812ca35903ff0dd.jpeg)

nginx\_arc

> 有兴趣可以参考关于 `NginX` 架构设计Nginx Core Architecture\[24\]

总结
--

本文我们主要介绍了 `NginX` 是什么以及其优势,接下来,主要了解了 `NginX` 常见命令和配置。另外,介绍了几个 `NginX` 有意思的模块使用,包括:基于客户端 IP 地址的访问控制、响应速度限制、限制来自同一个地址的同时连接或请求的数量、基于 `IP` 的地理定位、`njs` 脚本语言。接着具体介绍了 `NginX` 反向代理和负载均衡。最后简要总结了 `NginX`  高性能的几个设计优势。如果你想了解更多关于 `NginX` 的特性,可以进一步去 官网\[25\]了解。

### 参考资料

\[1\]

Yandex: _http://www.yandex.ru/_

\[2\]

Mail.Ru: _https://mail.ru/_

\[3\]

Netcraft: _https://www.netcraft.com/_

\[4\]

最新调查数据: _https://news.netcraft.com/archives/2022/08/26/august-2022-web-server-survey.html_

\[5\]

`Nginx`官网: _https://nginx.org/en/download.html_

\[6\]

ngx\_http\_gzip\_module: _https://nginx.org/en/docs/http/ngx\_http\_gzip\_module.html_

\[7\]

ngx\_http\_access\_module: _https://nginx.org/en/docs/http/ngx\_http\_access\_module.html_

\[8\]

ngx\_http\_limit\_conn\_module: _https://nginx.org/en/docs/http/ngx\_http\_limit\_conn\_module.html_

\[9\]

ngx\_http\_limit\_req\_module: _https://nginx.org/en/docs/http/ngx\_http\_limit\_req\_module.html_

\[10\]

leaky bucket(漏桶算法): _https://en.wikipedia.org/wiki/Leaky\_bucket_

\[11\]

ngx\_http\_geoip\_module: _https://nginx.org/en/docs/http/ngx\_http\_geoip\_module.html_

\[12\]

MaxMind GeoIP: _https://dev.maxmind.com/geoip/release-notes/2022#geoip-legacy-databases-have-been-retired?lang=en_

\[13\]

njs scripting language: _https://nginx.org/en/docs/njs/index.html_

\[14\]

njs-examples: _https://github.com/nginx/njs-examples_

\[15\]

反向代理: _https://zh.wikipedia.org/wiki/%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86_

\[16\]

http\_proxy\_module: _https://nginx.org/en/docs/http/ngx\_http\_proxy\_module.html#proxy\_buffer\_size_

\[17\]

负载均衡: _https://zh.wikipedia.org/wiki/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1_

\[18\]

least\_conn: _https://nginx.org/en/docs/http/ngx\_http\_upstream\_module.html#least\_conn_

\[19\]

ip\_hash: _https://nginx.org/en/docs/http/ngx\_http\_upstream\_module.html#ip\_hash_

\[20\]

ngx\_http\_upstream\_module: _https://nginx.org/en/docs/http/ngx\_http\_upstream\_module.html_

\[21\]

图像转换: _https://nginx.org/en/docs/http/ngx\_http\_image\_filter\_module.html_

\[22\]

正则匹配 URI: _https://nginx.org/en/docs/http/ngx\_http\_rewrite\_module.html_

\[23\]

支持 A/B 测试: _https://nginx.org/en/docs/http/ngx\_http\_split\_clients\_module.html_

\[24\]

Nginx Core Architecture: _https://zh.booksc.org/book/61672853/b36b22_

\[25\]

官网: _https://nginx.org/en/_
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值