nginx实践总结

Posted by ACRusher on July 27, 2016

nginx实践总结

前言

Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。由俄罗斯的程序设计师Igor Sysoev所开发,供俄国大型的入口网站及搜索引擎Rambler使用。 其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好。

安装

具体安装步骤参考 nginx安装教程 需要先安装编译工具和pcre,最后安装nginx。

常用配置

nginx配置模块的官方文档

http://nginx.org/en/docs/

主配置文件

主配置文件默认在 {nginx_install_dir}/conf/nginx.conf.

由于nignx高性能,单机qps可达3-5万,一般情况下都是多个业务应用复用一个nginx集群。nginx主配置文件只能有一个,在nginx启动时通过 -conf 参数指定,故nginx配置需要支持多个应用的配置变动互不影响。nginx有include指令,完美的支持这一需求。

下面的一个主配置示例:

user	                nobody;

#由于nginx单线程,这儿启动4个worker来提高多核cpu利用率, worker数量尽量保持跟服务器的cpu数量一致
worker_processes        4;
# 指定每个worker对应的cpu(可以指定多个) 如 1010 代表指定cpu1 和 cpu3
worker_cpu_affinity     0001 0010 0100 1000;
# 指定同时打开文件数的上线,注意linux一切皆文件,包括socket
worker_rlimit_nofile    102400;
# 全局默认log
error_log  logs/error.log;
# 保持nginx master的pid
pid logs/nginx.pid;

# 多路io复用模型 主要包括 select poll epoll (这是三个linux 系统调用),由于epoll性能最好,线上一般使用epoll
events {
    use                 epoll;
    # 跟最大文件数保持一致即可
    worker_connections  102400;
}

# http代理模块
http {
    #配置http 访问控制的, 参考: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
    add_header Access-Control-Allow-Origin  $http_origin;
    add_header Access-Control-Allow-Credentials  true;
    include             mime.types;
    default_type        text/html;

    log_format          main  '"$remote_addr" "-" "$remote_user" "$time_local" "$request" '
                              '"$status" "$body_bytes_sent" "$http_referer" '
                              '"$http_user_agent" "$http_x_forwarded_for" "$request_time" "$upstream_response_time" "$upstream_addr"';
    
    log_format      pingback  '$remote_addr - $remote_user [$time_local] "$request" '
                              '$status $body_bytes_sent "$request_body" "$http_referer" '
                              '"$http_user_agent" "$http_x_forwarded_for" "$request_time" "$upstream_response_time"';

    #默认的access log
    access_log  logs/access.log  main;
    sendfile            on;
#    tcp_nopush          on;
    # 禁用tcp delay 防止响应延迟
    tcp_nodelay         on;
    # 使用tcp 长连接时,最长的keep alive时间(秒)
    keepalive_timeout   10;

    # 下面的配置名字已经写的很清楚了
    client_header_buffer_size     128k;
    large_client_header_buffers   4 256k;
    client_header_timeout         10;
    client_body_timeout           10;
    send_timeout                  10;
    client_max_body_size 50m;
    client_body_buffer_size  512k;
    proxy_connect_timeout    30;
    proxy_read_timeout       30;
    proxy_send_timeout       30;
    proxy_buffer_size        16k;
    proxy_buffers            4 64k;
    proxy_busy_buffers_size 128k;
    proxy_temp_file_write_size 128k;

    limit_req_log_level info;
    
    # 所有通过nginx代理的http请求,都会设置的header,主要为了获取客户端的真实ip
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    #这儿做应用隔离,每个应用单独维护自己的配置文件
    include etc/app1.conf;
    include etc/app2.conf;
    include etc/app3.conf;

  
    # 配置默认的server 主要为了监控nginx状态使用,curl -v "http://localhost/nginx_status"
    server {
        listen       80;
        server_name  localhost;

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

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        location /nginx_status {
            stub_status on;
            access_log  off;
        }
    }
}

子应用配置文件

下面是一个简单的例子 app1.conf

    
    upstream app1.com {
	server 10.10.10.01:8080;
	server 10.10.10.02:8080;
	server 10.10.10.03:8080;
	# 每个nginx worker线程,最多持有的 app1.com集群 的长连接数量
	keepalive 256;
    }
 
    server {
        listen       80;
        #设置需要处理的host,只有http请求url的host匹配才会处理请求
        server_name  app1.com pre.app1.com dev.app1.com;
        #配置当前server的默认access log,注意main为日志格式引用,在主配置里有main的格式
        access_log  /data/logs/nginx/app1.access.log  main;
        
        #按照url 正则匹配
       location ~ (/service/test.action)|(/service/buy.action){
            # 只要http 1.1 才支持长连接,下面两个必须配置才能支持长连接
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            # 下面几个header,这儿其实冗余了,因为主配置文件已经配置
            proxy_set_header Host $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_pass   http://app1.com;
        }
        # 这个配置可以拦截所有的action请求, 关于location匹配的优先级,完全匹配>正则匹配,当都是正则匹配时,匹配长度长的优先
        location ~ \.action$ {
            proxy_pass   http://app1.com;
        }
        location /img {
            proxy_pass   http://cdn.com;
        }
        location / {
            root   html;
            index  index.html index.htm;
        }

        error_page  404          http://app1.com/notfound.html;
        error_page  500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        location @missing {
            internal;
            proxy_pass   http://app1.com;
        }
    }

为了便于理解,插播一段nginx配置的语法:

nginx支持的关键字: set , if , location , rewrite, break

  • set 使用语法 : set $your_var_name “0” 或者 set $your_var_name “${host}”
  • if 使用语法 : if ( ... ) { }
  • location 使用语法 : location (~|~*|!~|!~*) /test/ { } 其中 /test 是要匹配的正则表达式
  • rewrite 使用语法 : rewrite expr1 expr2 flag , 其中 expr1为正则表达式 , expr2 为重写的url(可以带http也可以不带),flag为last、break、permanent、redirect
  • break 使用语法 : break ; 可单独使用。

上面几个关键字可以出现在 server 模块的任意地方,一般是组合使用,nginx的匹配规则流程:

    1. 预处理,查找server模块的所有rewrite语句,按顺序找出第一个匹配的rewirte语句,只执行这一个语句
    2. location 循环处理
        2.1 找出所有 location的直接匹配,匹配长度最长的优先
        2.2 如果2.1匹配的location , 有设置跳过正则匹配,则跳到步骤2.3,否则按出现顺序找到第一个正则匹配的location
        2.3 location匹配结果分析
            2.3.1 如果2.1 和 2.2 匹配到的location为零,则跳出location循环处理
            2.3.2 如果2.1 和 2.2 匹配到的location为2个,则只保留2.2的正则匹配结果
            2.3.3 此时只剩下一个location匹配,执行location内部处理 ,
            2.3.4 如果2.3.3中执行了rewrite,并且没有中断循环,则从2.1重新开始循环 ,否则停止循环

上面是nginx处理location的流程,2.1处理过程中如有匹配到 locaton ^~ /some-url 可以设置跳过正则匹配location, location 完全匹配默认也会跳过正则匹配。

location内部处理逻辑: 按顺序执行每个 rewrite 语句,如果rewrite 设置了 flag,则终止当前location的rewrite语句执行并返回flag。flag通常情况下为 last或break,也可以为permanet(301)、redirect(302),这两种必须为完整的url。如果location内部匹配了非终止性(flag不是break或permanent)的rewrite语句,则proxy_pass不会执行,nginx继续重新匹配location。

理解了上面的处理流程,便可以游刃有余的配置反向代理,应对各种各样的业务转发需求。

下面整理下常用的业务场景:

1. 某个接口转发到新的应用集群

常用指数 : ★★★★★

upstream app-new {
    server 10.10.10.1:8080 weight=10;
    server 10.10.10.2:8080 weight=10;
    server 10.10.10.3:8080 weight=10;
}
server {
    ... 
    location ~ /service/interface.action$ {
        proxy_pass http://app-new/;
    }
    ...
}

2. 将某个接口转发至另一个接口

常用指数 : ★★★★★

    server {
        ... 
        location ~ /service/mp3/{
            rewrite ^/service/mp3/(.*)\.action /service/mp4/${1} last;
        }
        location ~ /service/mp4/{
            proxy_pass http://app-cluster/;
        }
        ...
    }

3. 某个接口根据cookie设置默认参数

常用指数 : ★★★★

    server {
    ... 
    location ~ /service/mp3/{
      if ( $http_cookie ~* "NAME=([0-9a-d].*)") {
          set $user_name "${1}";
          # nginx 在rewrite时会把原始 query_string拼接回去,如果这不是你想要的,可以在末尾添加?阻止这种行为
          # 即:rewrite ^/(.*)$   /${1}?user_name=${user_name}?
          rewrite ^/(.*)$   /${1}?user_name=${user_name}
      }
    }
    ... 
    }

4. nginx限流

常用指数 : ★★★★

    server {
    ... 
    location ~ /service/mp3/{
      # 这儿只是个示例,根据cookie将部分用户直接跳转到默认限流页面
      # 如果是json接口 也可以直接返回json数据: return 200 '{"code":"A00001","message":"limited"}'
      if ( $http_cookie ~* "NAME=([0-9a-d].*)") {
          rewrite ^/(.*)$   http://server.com/default.html permanent;
      }
      if ( $http_cookie ~* "AGE=([0-9a-d].*)") {
          return 200 '{"code":"A00001","message":"limited"}';
      }
    }
    ... 
    }

5. 禁用接口

常用指数 : ★★★★

    server {
    ... 
    location ~ /service/mp3/{
      deny all;
    }
    ... 
    }

6. 某接口禁用某外部ip的调用

常用指数 : ★★★★

    server {
    ... 
    location ~ /service/mp3/{
      deny 10.10.10.1;
    }
    ... 
    }

附一下nginx常用的内置变量

  • $http_user_agent UA
  • $http_cookie coockie
  • $request_method POST或者GET
  • $args 查询参数
  • $http_host host

总结

nginx作为当前最流行的http和反向代理服务器,源于其强大的性能、可靠性以及灵活的配置。nginx官方提供了非常多的配置模块,能满足大部分情况下的业务需求。当然,如果想自己定义一些特性,可以通过lua脚本来定制化nginx的行为。

nginx需要配置多个worker来充分发挥多核cpu的优势,另外如果应用的的qps压力比较大,推荐在nginx和后台服务器集群之间使用长连接,避免频繁的tcp握手和挥手。


Creative Commons License
This work is licensed under a CC A-S 4.0 International License.