Nginx 的一些总结

Nginx

什么是 Nginx

nginx [engine x] 是一个毛子开发的高性能的 HTTP 和反向代理服务器, 也是一个邮件代理服务器, TCPUDP 代理服务器.

nginx 处理静态文件的速度很快, 本身不提供动态内容的处理功能, 而是通过反向代理(proxy)功能来处理动态请求.

什么是 proxy 代理

nginx 当中一般有两种代理, 一种是正向代理, 一种是反向代理.

通常来说, 当 nginx 需要处理动态内容的时候, 用的是反向代理, 反向代理一般工作模式类似于:

当客户端 C 试图访问 S 服务器上的 /api/abc 资源时, 而 /api/abc 并不存在于 S 服务器上, 此时 S 服务器将向 S2 服务器请求
/api/abc 资源, 并将获取到的结果返回给客户端 C. 此时客户端并不知道服务器 S 做了什么, 它只知道资源获取成功了.

而正向代理其实和翻墙用的 VPNShadowsocks 是类似的用途. 即客户端 C 想要访问服务器 S2 上的资源, 但是由于某种不可抗力的原因 C
无法直接访问 S2, 但 S 服务器和 S2 却畅通无阻, 此时 C 就可以把 S 设置成代理服务器, 将任何访问 S2 的请求转发给 S, 让 S
来帮忙获取.

虽然看起来正向代理和反向代理功能似乎差不多, 但是一个重要的区别是, 客户端 C 是否知道 S 是一个 proxy.

如何获取 nginx

目前来说, nginx 提供 LinuxWindows 两个平台的支持, 但由于 nginx 的高性能是依赖于 Linux 上独有的功能, 因此 nginxWindows
上的版本仅仅只能是一个玩具, 一个测试工具, 性能远远低于 Linux 上的版本.

http://nginx.org/en/download.html 上可以直接下载到源码和 Windows 的预编译二进制包.

但如果你对如何编译调优等不熟悉的话, 建议还是直接从包管理器里直接安装, 比如:

apt install nginx

pacman -S nginx

yum install nginx

zypper install nginx

不出意外的话安装的是目前处于 stable version1.12.xnginx

如何使用?

启动与管理

nginx 的启动只需要简单的调用 nginx 指令就可以运行

nginx -s reload

nginx -s restart

nginx -s stop

则可以分别对已启动的 nginx 实例进行
* 重新加载配置文件
* 重新启动
* 停止

上面说的直接调用是最基础的管理, 但更推荐的方法是使用更高级的 systemd 来进行管理, 后者可以很方便的提供前者无法做到的事情

例如服务监控, 故障重启, 内存检测.

Ubuntu 16 以及 CentOS 7 版本以上已经将传统的 sysvinit 替换为了 systemd, 相对于的将原始的 service 指令换成了 systemctl 指令

当你在这些系统上执行 service 的时候它会提示你 redirect to systemctl.

有了 systemd 之后, 管理 nginx 则可以替换为

systemctl start nginx

systemctl stop nginx

systemctl reload nginx

systemctl restart nginx

systemctl status nginx

配置文件的编写

要想要发挥 nginx 的完整功能, 熟悉的编写 nginx 的配置文件是必不可少的技能.

nginx 的配置文件采用的是 类似shell 语法的指令. 几乎所有的指令都是由 nginx 的不同的 module 来提供的.

在编写 nginx 的配置文件的时候, 你需要时刻记住, 你所写的是 nginx 的配置文件, 尽管它和 shell 类似, 但它只在语法上有些相似,
nginx 配置文件当中, 没有什么的严格的执行顺序, 写在前面的指令可能会在任何时候被执行.

如果你发现你所用到的指令被提示不存在, 那一定是你的 nginx 版本不符合或者没有编译所需要的 module.

关于 if 指令

特别需要提醒的一点是, if 指令并不是 nginx 的核心指令, 它是由 ngx_http_rewrite_module 提供的, 同时该模块还提供了
break, return, rewriteset 指令.

正因为如此, if 指令应当避免和第三方模块一起使用, 这将会导致一些不可思议的问题. 而且 if 指令推荐只和 rewrite 有关操作一起使用,
使用 if 来处理一些想当然的问题同样会导致令人难以理解的问题和 bug.


nginx 的最主要的配置文件是 nginx.conf, 一般来说它位于 /etc/nginx/nginx.conf

nginx 用作 HTTP(S) 服务器的时候, 最主要的就是 http {} 块, 你的所有其他指令都应当放在 http 块当中.

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    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  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
            return 403;
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }
}

上面是一个基本的配置文件的示例, http {} 段当中首先包含了一些基本设置, 和一些与性能有关系的指令.

在这里我们需要关注的是 server {} 段.

nginx 当中, 每个 server {} 段都是一个独立的虚拟主机段, 这些不同的 server {} 段当中通过 listen 指令指定的端口号和 server_name
指令指定的域名来区分不同的服务.

上述例子当中, server {} 段只负责处理了静态文件, root 指令设置了静态文件所在的位置, location 指令则设定当你访问 / 的时候返回 HTTP 403 forbidden

server_namelisten

server_name 用来指示当前 server {} 段匹配的域名, 可以由多个域名, 例如

server_name abc.com abcd.com a.abc.com *.cdef.com;

listen 指令则设定需要监听的端口号, 端口号可以任意, 只要不是一个非 nginx 占用的就可以, 不同的 server {} 里面的 listen 可以使用相同
的端口号, 这是不会冲突的.

location

locationnginx 当中一个较为重要的匹配指令, 它主要是匹配访问的路径, 然后根据不同的路径你可以进行不同的操作, 例如你可以让 /abc/ 对应 /var/www/abc/ 路径,
/def/ 则对应 /srv/def/ 路径:

location /abc {
    root /var/www/;
}

location /def {
    root /srv/;
}

这时候需要注意一点, 当 root 指令放在 location 里面使用的时候, 实际物理路径就会变成 root 指定的路径 + location 匹配的路径.

如果你需要让 /abc 对应物理路径 /var/www/def 的时候, 则需要使用 alias 指令:

location /abc {
    alias /var/www/def;
}

这里别名的意思是, 当我访问 /abc的时候, 实际上将会访问 /var/www/def 这个文件, 当我访问 /abcd 的时候, 实际上访问
的物理文件是 /var/www/defd 这个文件, 而当访问 /abd/def 的时候, 访问的物理文件则变成了 /var/www/def/def这个

这也是 alias 的本来的意思, 别名.

这是 rootalias 的一个重要区别.

location 指令匹配的路径可以是单纯的字符串按照前缀来匹配, 也可以使用正则表达式来匹配

当使用正则表达式进行匹配的时候, 前面有两种修饰符, ~* : 大小写不敏感匹配, ~ : 大小写敏感匹配.

例如

location ~* \.js$ {}

则表示匹配任何以 .js 为结尾切不区分大小写的请求.

多个 location 出现的时候, 匹配顺序一般是按照从最长到最短的顺序

例如

location /a {
}

location /a/b {
}

当访问 /a 的时候匹配的是第一个 location, 当访问 /a/ 的时候同样是第一个, 而当访问 /a/b 的时候则匹配到的是第二个 location

反向代理的配置

前面说的都是静态文件的处理, 对于后端来说, 最主要的部分就是配置反向代理来正确处理后端的请求.

nginx 当中一般有 proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass 等这几种反向代理指令,
分别对应着以普通的 HTTP(S) 协议进行代理, 以 fastcgi 协议代理, 以 uwsgi 协议代理和以 scgi 协议代理.

对于目前 PythonFlask 来说, 常用的是 proxy_passuwsgi_pass.

最基本的反向代理其实很简单, 只需要在对应的指令后面跟上目标地址就可以, 例如

proxy_pass http://127.0.0.1:8080

就表示将请求转发给 http://127.0.0.1:8080

通常反向代理可以和 location 一起使用, 比如

location / {
    root /path/to/html;
}

location /api {
    proxy_pass http://127.0.0.1:8080;
}

上面的配置文件表示, 对于所有的请求, 如果是以 /api 开头的, 则将请求转发给 http://127.0.0.1:8080, 而其他请求则当作静态文件去 /path/to/html 目录寻找

例如当访问 /api/abc 的时候, nginx 会以 http 协议去访问 http://127.0.0.1:8080/api/abc, 并将访问结果放回给客户端.

那么现在假设一种情况, 你的后端代码有一个路由 : /function, 当后端运行在 5000 端口的时候, 访问
http://127.0.0.1:5000/function 就可以获取到内容, 但是你的前端非常不满意, 认为需要加一个 /api 前缀,
你的前端认为这个接口应该是 /api/function, 但后端不想改代码, 怎么办呢?

这个时候只需要在 nginx 里面这么写

location ~* /api(.*) {
    proxy_pass http://127.0.0.1:5000$1;
}

就可以解决问题.

当然最简单的方法是, 打死你的后端.

除此以外, 反向代理也是可以设置很多参数的. http://nginx.org/en/docs/http/ngx_http_proxy_module.html

nginx 的变量

其实这部分如果只是简单的用 nginx 的话一般人用不到.

nginx 的变量是使用 set 指令来设定的, 之前也说过, set 指令是 rewrite 模块提供的指令.

nginx 的变量不同之处在于, 变量的创建时间是在配置文件被读取的时候, 而赋值操作是在运行时执行的.

例如

server {
    ...
    location /a {
        set $a '/a';
    }

    location /b {
        return 200 $a;
    }
}

如果你不了解 nginx 的变量的话, 你会觉得这部分配置文件是错误的, 你的理由很简单, location /b 里面直接用了一个未定义的 $a 变量

但事实上, 这是完全合法的, 因为当 nginx 读取你的配置文件的时候, 发现你在 location /a 里面创建了 $a 变量, 于是 nginx 就会创建一个变量
$a, 这个变量将会是在这个 server 里面的全局存在. 所以在 location /b 里面也就可以使用这个变量了. 只不过这个变量是空值罢了.

发表评论

电子邮件地址不会被公开。 必填项已用*标注