nginx实践总结
前言
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。由俄罗斯的程序设计师Igor Sysoev所开发,供俄国大型的入口网站及搜索引擎Rambler使用。 其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好。
安装
具体安装步骤参考 nginx安装教程
需要先安装编译工具和pcre,最后安装nginx。
常用配置
nginx配置模块的官方文档
主配置文件
主配置文件默认在 {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 "${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。
理解了上面的处理流程,便可以游刃有余的配置反向代理,应对各种各样的业务转发需求。
下面整理下常用的业务场景:
- 某个接口转发到新的应用集群
常用指数 : ★★★★★
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/;
}
...
}
- 将某个接口转发至另一个接口
常用指数 : ★★★★★
server {
...
location ~ /service/mp3/{
rewrite ^/service/mp3/(.*)\.action /service/mp4/${1} last;
}
location ~ /service/mp4/{
proxy_pass http://app-cluster/;
}
...
}
- 某个接口根据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}
}
}
...
}
- 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"}';
}
}
...
}
- 禁用接口
常用指数 : ★★★★
server {
...
location ~ /service/mp3/{
deny all;
}
...
}
- 某接口禁用某外部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
参考资料
网友评论