OpenResty最佳实践笔记(2)

转自OpenResty最佳实践

Nginx 新手起步

Nginx优点

  • 处理响应请求很快
  • 高并发连接
  • 低的内存消耗

    10000 个非活跃的 HTTP Keep-Alive 连接在 Nginx 中仅消耗 2.5MB 的内存

  • 具有很高的可靠性
  • 高扩展性

    完全是由多个不同功能、不同层次、不同类型且耦合度极低的模块组成。这种设计造就了 Nginx 庞大的第三方模块

  • 热部署

    master 管理进程与 worker 工作进程的分离设计,使得 Nginx 具有热部署的功能 可以在 7 × 24 小时不间断服务的前提下,升级 Nginx 的可执行文件。 也可以在不停止服务的情况下修改配置文件,更换日志文件等功能。

  • 自由的 BSD 许可协议

    BSD 许可协议不只是允许用户免费使用 Nginx,也允许用户修改 Nginx 源码,还允许用户用于商业用途

使用 Nginx

  • 安装

1 获取 Nginx

2 解压缩 nginx-xx.tar.gz 包

3 进入解压缩目录,执行 ./configure

4 make & make install

若安装时找不到上述依赖模块,使用 --with-openssl=<openssl_dir>、
--with-pcre=<pcre_dir>、--with-zlib=<zlib_dir> 指定依赖的模块目录
  • 配置
配置目录 conf 下有以下配置文件

.
├── fastcgi.conf
├── fastcgi_params
├── koi-utf
├── koi-win
├── mime.types
├── nginx.conf
├── scgi_params
├── uwsgi_params
└── win-utf

除了 nginx.conf,其余配置文件,一般只需要使用默认提供即可

nginx.conf

worker_process      # 表示工作进程的数量,一般设置为cpu的核数

worker_connections  # 表示每个工作进程的最大连接数

server{}            # 块定义了虚拟主机

    listen          # 监听端口

    server_name     # 监听域名

    location {}     # 是用来为匹配的 URI 进行配置,URI 即语法中的“/uri/”

    location /{}    # 匹配任何查询,因为所有请求都以 / 开头

        root        # 指定对应uri的资源查找路径,这里html为相对路径,完整路径为
                    # /opt/nginx-1.7.7/html/

        index       # 指定首页index文件的名称,可以配置多个,以空格分开。如有多
                    # 个,按配置顺序查找。




location 匹配规则

语法规则

location [=|~|~*|^~] /uri/ { … }
模式 含义
location = /uri = 表示精确匹配,只有完全匹配上才能生效
location ^~ /uri ^ ^~ 开头对URL路径进行前缀匹配,并且在正则之前。
location ~ pattern 开头表示区分大小写的正则匹配
location ~* pattern 开头表示不区分大小写的正则匹配
location / 通用匹配,任何未匹配到其它location的请求都会匹配到,相当于switch中的default
location /uri 不带任何修饰符,也表示前缀匹配,但是在正则匹配之后

多个 location 配置的情况下匹配顺序:

首先精确匹配 =
其次前缀匹配 ^~
其次是按文件中顺序的正则匹配
然后匹配不带任何修饰的前缀匹配。
最后是交给 / 通用匹配
当有匹配成功时候,停止匹配,按当前匹配规则处理请求

匹配实例

location = / {
   echo "规则A";
}
location = /login {
   echo "规则B";
}
location ^~ /static/ {
   echo "规则C";
}
location ^~ /static/files {
    echo "规则X";
}
location ~ \.(gif|jpg|png|js|css)$ {
   echo "规则D";
}
location ~* \.png$ {
   echo "规则E";
}
location /img {
    echo "规则Y";
}
location / {
   echo "规则F";
}

访问根目录 /,比如 http://localhost/ 将匹配 规则A

访问 http://localhost/login 将匹配 规则B,http://localhost/register 则匹配 规则F

访问 http://localhost/static/a.html 将匹配 规则C

访问 http://localhost/static/files/a.exe 将匹配 规则X,虽然 规则C 也能匹配到,但因为最大匹配原则,最终选中了 规则X。你可以测试下,去掉规则 X ,则当前 URL 会匹配上 规则C。

访问 http://localhost/a.gif, http://localhost/b.jpg 将匹配 规则D 和 规则 E ,但是 规则 D 顺序优先,规则 E 不起作用,而 http://localhost/static/c.png 则优先匹配到 规则 C

访问 http://localhost/a.PNG 则匹配 规则 E ,而不会匹配 规则 D ,因为 规则 E 不区分大小写。

访问 http://localhost/img/a.gif 会匹配上 规则D,虽然 规则Y 也可以匹配上,但是因为正则匹配优先,而忽略了 规则Y。

访问 http://localhost/img/a.tiff 会匹配上 规则Y。

访问 http://localhost/category/id/1111 则最终匹配到规则 F 

    因为以上规则都不匹配,这个时候应该是 Nginx 转发请求给后端应用服务器,比如 FastCGI(php),tomcat(jsp)
    Nginx 作为反向代理服务器存在。

前缀匹配,如果有包含关系时,按最大匹配原则进行匹配。

    location /dir01 与 location /dir01/dir02,如有请求 http://localhost/dir01/dir02/file 将最终匹配到 location /dir01/dir02

location实际应用

# 直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说。
# 这里是直接转发给后端应用服务器了,也可以是一个静态首页
# 第一个必选规则
location = / {
    proxy_pass http://tomcat:8080/index
}

# 第二个必选规则是处理静态文件请求,这是 nginx 作为 http 服务器的强项
# 有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用
location ^~ /static/ {
    root /webroot/static/;
}
location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
    root /webroot/res/;
}

# 第三个规则就是通用规则,用来转发动态请求到后端应用服务器
# 非静态文件请求就默认是动态请求,自己根据实际把握
# 毕竟目前的一些框架的流行,带.php、.jsp后缀的情况很少了
location / {
    proxy_pass http://tomcat:8080/
}

rewrite 语法

last – 基本上都用这个 flag
break – 中止 rewrite,不再继续匹配
redirect – 返回临时重定向的 HTTP 状态 302
permanent – 返回永久重定向的 HTTP 状态 301

可以用来判断的表达式

-f 和 !-f 用来判断是否存在文件
-d 和 !-d 用来判断是否存在目录
-e 和 !-e 用来判断是否存在文件或目录
-x 和 !-x 用来判断文件是否可执行

可以用作判断的全局变量

例:http://localhost:88/test1/test2/test.php?k=v
$host:localhost
$server_port:88
$request_uri:/test1/test2/test.php?k=v
$document_uri:/test1/test2/test.php
$document_root:D:\nginx/html
$request_filename:D:\nginx/html/test1/test2/test.php

redirect 语法


server {
    listen 80;
    server_name start.igrow.cn;
    index index.html index.php;
    root html;
    if ($http_host !~ "^star\.igrow\.cn$") {
        rewrite ^(.*) http://star.igrow.cn$1 redirect;
    }
}

防盗链

location ~* \.(gif|jpg|swf)$ {
    valid_referers none blocked start.igrow.cn sta.igrow.cn;
    if ($invalid_referer) {
       rewrite ^/ http://$host/logo.png;
    }
}

根据文件类型设置过期时间

location ~* \.(js|css|jpg|jpeg|gif|png|swf)$ {
    if (-f $request_filename) {
        expires 1h;
        break;
    }
}
禁止访问

禁止访问某个目录

location ~* \.(txt|doc)${
    root /data/www/wwwroot/linuxtone/test;
    deny all;
}

Nginx 静态文件服务

http {
    # 这个将为打开文件指定缓存,默认是没有启用的,max 指定缓存数量,
    # 建议和打开文件数一致,inactive 是指经过多长时间文件没被请求后删除缓存。
    open_file_cache max=204800 inactive=20s;

    # open_file_cache 指令中的inactive 参数时间内文件的最少使用次数,
    # 如果超过这个数字,文件描述符一直是在缓存中打开的,如上例,如果有一个
    # 文件在inactive 时间内一次没被使用,它将被移除。
    open_file_cache_min_uses 1;

    # 这个是指多长时间检查一次缓存的有效信息
    open_file_cache_valid 30s;

    # 默认情况下,Nginx的gzip压缩是关闭的, gzip压缩功能就是可以让你节省不
    # 少带宽,但是会增加服务器CPU的开销哦,Nginx默认只对text/html进行压缩 ,
    # 如果要对html之外的内容进行压缩传输,我们需要手动来设置。
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_http_version 1.0;
    gzip_comp_level 2;
    gzip_types text/plain application/x-javascript text/css application/xml;

    server {
            listen       80;
            server_name www.test.com;
            charset utf-8;
            root   /data/www.test.com;
            index  index.html index.htm;
           }
}

实现和调整应用栈(application stack)的缓存提高终端用户的体验

  • 文件缓存

    一个 web 缓存坐落于客户端和原始服务器(origin server)中间,它保留了所有可见内容的拷贝。 如果一个客户端请求的内容在缓存中存储,则可以直接在缓存中获得该内容而不需要与服务器通信。 这样一来,由于 web 缓存距离客户端“更近”,就可以提高响应性能,并更有效率的使用应用服务器, 因为服务器不用每次请求都进行页面生成工作。

在浏览器和应用服务器之间,存在多种潜在缓存,如:客户端浏览器缓存、中间缓存、内容分发网络(CDN)和服务器上的负载平衡和反向代理。

缓存,仅在反向代理和负载均衡的层面,就对性能提高有很大的帮助

  • 安装和配置基础缓存

proxy_cache_path 用来设置缓存的路径和配置

proxy_cache 用来启用缓存

proxy_cache_path/path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m
use_temp_path=off;

server {
    ...
    location / {
        proxy_cache my_cache;
        proxy_pass http://my_upstream;
    }

}

proxy_cache_path

用于缓存的本地磁盘目录是 /path/to/cache/

levels 在 /path/to/cache/ 设置了一个两级层次结构的目录。将大量的文件放置在单个目录中会导致文件访问缓慢,
       所以针对大多数部署,我们推荐使用两级目录层次结构。如果 levels 参数没有配置,则 Nginx 会将所有的文件放到同一个目录中。

keys_zone 设置一个共享内存区,该内存区用于存储缓存键和元数据,有些类似计时器的用途。
          将键的拷贝放入内存可以使 Nginx 在不检索磁盘的情况下快速决定一个请求是 HIT 还是 MISS,
          这样大大提高了检索速度。一个 1MB 的内存空间可以存储大约 8000 个 key,
          那么上面配置的 10MB 内存空间可以存储差不多 80000 个 key。

max_size 设置了缓存的上限(在上面的例子中是 10G)。这是一个可选项;如果不指定具体值,
         那就是允许缓存不断增长,占用所有可用的磁盘空间。当缓存达到这个上限,
         处理器便调用 cache manager 来移除最近最少被使用的文件,这样把缓存的空间降低至这个限制之下。

inactive 指定了项目在不被访问的情况下能够在内存中保持的时间。在上面的例子中,
         如果一个文件在 60 分钟之内没有被请求,则缓存管理将会自动将其在内存中删除,不管该文件是否过期。
         该参数默认值为 10 分钟(10m)。注意,非活动内容有别于过期内容。
         Nginx 不会自动删除由缓存控制头部指定的过期内容(本例中 Cache-Control:max-age=120)。
         过期内容只有在 inactive 指定时间内没有被访问的情况下才会被删除。如果过期内容被访问了,
         那么 Nginx 就会将其从原服务器上刷新,并更新对应的 inactive 计时器。

Nginx 最初会将注定写入缓存的文件先放入一个临时存储区域,use_temp_path=off 命令指示 Nginx 将在缓存这些文件时将它们写入同一个目录下。
      我们强烈建议你将参数设置为 off 来避免在文件系统中不必要的数据拷贝。
  • 缓存微调
proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m
use_temp_path=off;
server {
    ...
    location / {
        proxy_cache my_cache;
        proxy_cache_revalidate on;
        proxy_cache_min_uses 3;
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        proxy_cache_lock on;
        proxy_pass http://my_upstream;
    }
}

proxy_cache_revalidate 指示 Nginx 在刷新来自服务器的内容时使用 GET 请求。如果客户端的请求项已经被缓存过了,但是在缓存控制头部中定义为过期,
                       那么 Nginx 就会在 GET 请求中包含 If-Modified-Since 字段,发送至服务器端。这项配置可以节约带宽,
                       因为对于 Nginx 已经缓存过的文件,服务器只会在该文件请求头中 Last-Modified 记录的时间内被修改时才将全部文件一起发送。
                       
proxy_cache_min_uses 该指令设置同一链接请求达到几次即被缓存,默认值为 1 。当缓存不断被填满时,这项设置便十分有用,
                     因为这确保了只有那些被经常访问的内容会被缓存。

proxy_cache_use_stale 中的 updating 参数告知 Nginx 在客户端请求的项目的更新正在原服务器中下载时发送旧内容,
                      而不是向服务器转发重复的请求。第一个请求陈旧文件的用户不得不等待文件在原服务器中更新完毕。
                      陈旧的文件会返回给随后的请求直到更新后的文件被全部下载。

当 proxy_cache_lock 被启用时,当多个客户端请求一个缓存中不存在的文件(或称之为一个 MISS),
                    只有这些请求中的第一个被允许发送至服务器。
                    其他请求在第一个请求得到满意结果之后在缓存中得到文件。
                    如果不启用 proxy_cache_lock,则所有在缓存中找不到文件的请求都会直接与服务器通信

日志

Nginx 日志主要有两种:access_log(访问日志)error_log(错误日志)

access_log 访问日志

通过 access_log 你可以得到用户地域来源、跳转来源、使用终端、某个 URL 访问量等相关信息

log_format myformat '$remote_addr  $status  $time_local';
access_log logs/access.log  myformat;

log_format name string
    name 表示格式名称,
    string 表示定义的格式字符串
    
access_log path [format_name [buffer=size | off]]
    path 表示访问日志存放路径
    format_name 表示访问日志格式名称
    buffer 表示缓存大小
    off 表示关闭访问日志
字段 作用
$remote_addr与$http_x_forwarded_for 记录客户端IP地址
$remote_user 记录客户端用户名称
$request 记录请求的URI和HTTP协议
$status 记录请求状态
$body_bytes_sent 发送给客户端的字节数,不包括响应头的大小
$bytes_sent 发送给客户端的总字节数
$connection 连接的序列号
$connection_requests 当前通过一个连接获得的请求数量
$msec 日志写入时间。单位为秒,精度是毫秒
$pipe 如果请求是通过HTTP流水线(pipelined)发送,pipe值为“p”,否则为“.”
$http_referer 记录从哪个页面链接访问过来的
$http_user_agent 记录客户端浏览器相关信息
$request_length 请求的长度(包括请求行,请求头和请求正文)
$request_time 请求处理时间,单位为秒,精度毫秒
$time_iso8601 ISO8601标准格式下的本地时间
$time_local 记录访问时间与时区

注意:

log_format 配置必须放在 http 内  否则会出现警告

Nginx 进程设置的用户和组必须对日志路径有创建文件的权限,否则会报错

error_log 错误日志

error_log 主要记录客户端访问 Nginx 出错时的日志,格式不支持自定义

error_log path [level]
     path 表示错误日志存放路径
     level 表示错误日志等级
 
日志等级包括 debug、info、notice、warn、error、crit、alert、emerg
从左至右,日志详细程度逐级递减,即 debug 最详细,emerg 最少,默认为 error
注意:error_log off 并不能关闭错误日志记录,此时日志信息会被写入到文件名为 off 的文件
Linux 系统把存储位置设置为空设备


error_log /dev/null;

http {
    # ...
}

反向代理

  • 反向代理(Reverse Proxy)

是指用代理服务器来接受 internet 上的连接请求, 然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端, 此时代理服务器对外就表现为一个反向代理服务器

nginx.conf 配置示例:

worker_processes 1;

pid logs/nginx.pid;
error_log logs/error.log warn;

events {
    worker_connections 3000;
}

http {
    include mime.types;
    server_tokens off;

    ## 下面配置反向代理的参数
    server {
        listen    8866;

        ## 1. 用户访问 http://ip:port,则反向代理到 https://github.com
        location / {
            proxy_pass  https://github.com;
            proxy_redirect     off;
            proxy_set_header   Host             $host;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        }

        ## 2.用户访问 http://ip:port/README.md,则反向代理到
        ##   https://github.com/.../README.md
        location /README.md {
            proxy_set_header  X-Real-IP  $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass https://github.com/moonbingbing/openresty-best-practices/blob/master/README.md;
        }
    }
}
  • 正向代理

正向代理就像一个跳板,例如一个用户访问不了某网站(例如 www.google.com), 但是他能访问一个代理服务器,这个代理服务器能访问 www.google.com,于是用户可以先连上代理服务器, 告诉它需要访问的内容,代理服务器去取回来返回给用户。

例如一些常见的翻墙工具游戏代理就是利用正向代理的原理工作的,我们需要在这些正向代理工具上配置服务器的 IP 地址等信息。

负载均衡

负载均衡(Load balancing)是一种计算机网络技术,用来在多个计算机(计算机集群)、网络连接、CPU、磁盘驱动器或其他资源中分配负载, 以达到最佳化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的。

upstream 负载均衡概要

upstream test.net{
    ip_hash;
    server 192.168.10.13:80;
    server 192.168.10.14:80  down;
    server 192.168.10.15:8009  max_fails=3  fail_timeout=20s;
    server 192.168.10.16:8080;
}
server {
    location / {
        proxy_pass  http://test.net;
    }
}

upstream 是 Nginx 的 HTTP Upstream 模块,这个模块通过一个简单的调度算法来实现客户端 IP后端服务器的负载均衡。 在上面的设定中,通过 upstream 指令指定了一个负载均衡器的名称 test.net。这个名称可以任意指定,在后面需要用到的地方直接调用即可

upstream 支持的状态参数

down:
    表示当前的 server 暂时不参与负载均衡。

backup:
    预留的备份机器。
    当其他所有的非 backup 机器出现故障或者忙的时候,才会请求 backup 机器,因此这台机器的压力最轻。

max_fails:
    允许请求失败的次数,默认为 1 。
    当超过最大次数时,返回 proxy_next_upstream 模块定义的错误。

fail_timeout:
    在经历了 max_fails 次失败后,暂停服务的时间。
    max_fails 可以和 fail_timeout 一起使用。

注意: 当负载调度算法为 ip_hash 时,后端服务器在负载均衡调度中的状态不能是 backup

upstream 支持的负载均衡算法

1 轮询(默认):
    每个请求按时间顺序逐一分配到不同的后端服务器
    如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。
    Weight 指定轮询权, 值越大,分配到的访问机率越高,主要用于后端每个服务器性能不均的情况下。

2 ip_hash:
    每个请求按访问 IP 的 hash 结果分配,这样来自同一个 IP 的访客固定访问一个后端服务器,
    有效解决了动态网页存在的 session 共享问题。

3 fair:
    这是比上面两个更加智能的负载均衡算法。
    此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,
    也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。
    
    Nginx 本身是不支持 fair 的,如果需要使用这种调度算法,必须下载 Nginx 的 upstream_fair 模块。

4 url_hash:
    此方法按访问 url 的 hash 结果来分配请求,使每个 url 定向到同一个后端服务器,
    可以进一步提高后端缓存服务器的效率。
    
    Nginx 本身是不支持 url_hash 的,如果需要使用这种调度算法,必须安装 Nginx 的 hash 软件包。
    
5 least_conn:
    最少连接负载均衡算法,简单来说就是每次选择的后端都是当前最少连接的一个 server(这个最少连接不是共享的,是每个 worker 都有自己的一个数组进行记录后端 server 的连接数)。
    
6 hash:
    这个 hash 模块又支持两种模式 hash, 一种是普通的 hash, 另一种是一致性 hash(consistent)。

配置负载均衡

upstream 是定义在 server{ } 之外的,不能定义在 server{ } 内部。 定义好 upstream 之后,用 proxy_pass 引用一下即可

upstream webservers {
    server 192.168.18.201 weight=1;
    server 192.168.18.202 weight=1;
    server 192.168.18.202 backup;

}
server {
    listen       80;
    server_name  localhost;
    #charset koi8-r;
    #access_log  logs/host.access.log  main;
    location / {
        proxy_pass      http://webservers;
        proxy_set_header  X-Real-IP  $remote_addr;
    }
}
Buy me a 肥仔水!