Nginx

Nginx

IO 多路复用技术。

CPU 亲和:每个进程绑定一个核心,减少Cache Miss。

Sendfile:Httpd要将静态文件从内核空间传输到用户空间再转义到内核空间Socket。而Sendfile只在内核中传输,实现零拷贝。对于静态网站速度快。

压测工具:

1
2
# 一共 20000 个,每次并发 10 个
ab -n 20000 -c 10 url

IO 多路复用

单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力。Linux 系统下的IO操作分为:阻塞IO非阻塞IOIO多路复用信号驱动IO异步IO。其中IO多路复用又分为EpollSelectPoll。Windows下则使用IOCP技术。

Select Epoll:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* According to POSIX.1-2001, POSIX.1-2008 */
#include <sys/select.h>

/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

/*
功能:等待一个或多个事件,在有一个或多个事件发生或经历一段指定的时间后才唤醒。

参数:
fd_set 文件描述符组
readfds, writefds, exceptfds 为 NULL 表示不启用

返回值:就绪描述符的数目,超时返回0,出错返回-1
*/
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);

// pselect和select大体上是一样的,但有一些细节上的区别。
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timespec *timeout,
const sigset_t *sigmask);

void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

Select 有数量限制,而Epoll没有限制。Select阻塞等待记录就绪的文件描述符,并上报应用程序。Nginx采用Epoll。

1
2
3
4
#include <sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

Nginx 目录

日志轮转
/etc/logrotate.d/nginx

配置文件

  • /etc/nginx
  • /etc/nginx/nginx.conf 启动载入
  • /etc/nginx/conf.d
  • /etc/nginx/conf.d/default.conf 旧版默认载入的网站
  • /etc/nginx/sites-enabled/default 新版默认载入的网站

CGI 配置

  • /etc/nginx/fastcgi_params PHP
  • /etc/nginx/uwsgi_params
  • /etc/nginx/scgi_params

编码转化映射

  • /etc/nginx/koi-utf
  • /etc/nginx/koi-wih
  • /etc/nginx/win-utf

Mime Types

  • /etc/nginx/mime.types

模块

  • /usr/lib64/nginx/modules
  • /etc/nginx/modules

命令

  • /usr/sbin/nginx
  • /usr/sbin/nginx-debug

帮助

  • /usr/share/doc/nginx-*

缓存

  • /var/cache/nginx

日志

  • /var/log/nginx

Nginx 变量

主要分为三类:HTTP请求响应变量,Nginx内置变量,自定义变量

1
2
3
4
5
6
7
8
9
10
11
12
13
# 内置变量,参考 log_format
$remote_addr
$remote_user
$time_local
$request
$status

# HTTP请求响应变量
$http_user_agent
$http_HEADER 请求头
$send_http_HEADER 响应头

# 自定义变量 使用 Lua

Nginx 目录匹配

location 匹配:

  • = 精确匹配,一旦匹配成功,停止搜索
  • ^~ 前缀匹配,一旦匹配成功,停止搜索
  • ~ 正则匹配,区分大小写
  • ~* 正则匹配,不区分大小写

Rewrite:可以实现对url重写和重定向。使用正则表达式。用于访问跳转,SEO优化,后台维护,流量转发,伪静态。该功能依赖于 rewrite 模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
location [=|~|~*|^~|@] /uri/ {
...
}

locaton / {
rewrite ^(.*)$ /test/$1 break;
}

locaton /test {
root /var/www/html;
# 如无法访问,则调用 @java_page
try_files $uri @java_page;
}

location @java_page {
proxy_pass http://127.0.0.1:8080;
}

# root和alias的区别:
locaton /code/ {
root /var/www/html/;
# 寻找目录 /var/www/html/code/*
}

location /code {
alias /var/www/html;
# 寻找目录 /var/www/html/*
}

if($http_user_agent ~* Chrome){
rewrite ~(.*)$ /chrome/$1 break;
}

rewrite ^(.*)$ /pages/maintain.html break;

location ~ \.jsp$ {
proxy_pass http://mystream;
include proxy_params;
}

常用正则表达式:

  • ^ 匹配开始
  • $ 匹配结束
  • ~ 区分大小写
  • ~* 不区分大小写
  • ! 不匹配
  • . 匹配除换行符之外的字符
  • ? 0/1 次匹配
  • + >=1 次匹配
  • * 任意次匹配
  • | 或操作
  • \d 匹配数字
  • {n} 重复n次
  • {n,} 重复n次或更多次
  • [c] 匹配字符c
  • [a-z] 匹配a-z
  • \ 转义字符
  • () 提取内容,放到 $1 $2 中

Nginx 配置文件

/etc/nginx/nginx.conf

1
2
3
# -t 配置文件检查 -c 配置文件路径
nginx -t -c /etc/nginx/nginx.conf
nginx -s reload -c /etc/nginx/nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

error_log /var/log/nginx/error.log warn;

events {
worker_connections 768;
}

http {
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;

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

# 模块
log_format main '$header_user_agent' '$remote_addr - $remote_user'

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
server {
listen 80;
listen [::]:80;
# listen 80 ssl default_server;


server_name example.com;

root /var/www/example.com;
index index.html;

# 一个server可以有多个location
location / {
root /var/www/example.com;
index index.html index.htm;
try_files $uri $uri/ =404;
}

error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www/example.com;
}

location ~ \.php$ {
proxy_pass http://127.0.0.1;
}
}

Nginx 模块

分为:官方模块,第三方模块。

  • log_format 日志格式
  • stub_status Nginx 状态信息
  • random_index 返回随机页面
  • sub 替换HTTP内容
  • limit_conn 限制连接数量
  • limit_req 限制请求数量
  • access 基于IP的访问控制
  • auth_basic 用户登录控制
  • sendfile sendfile方式,加快读取速率
  • tcp_nopush 提高传输效率
  • tcp_nodelay 提高实时率
  • gzip 压缩
  • add_header 添加头字段
  • valid_referers 修改referer
  • proxy_pass 代理
  • upstream 负载均衡
  • rewrite 匹配url

模块:log_format

格式化日志输出,支持error.log access.log

语法规则:

1
2
3
Syntax: log_format name [escape=default|json] string ...;
Defualt: log_format combined "...";
Context: http; # 表示只能配置在http模块下

例如:

1
2
3
4
5
6
http {
...
# log_format 按照main的格式要求输出日志
# 输出的变量包括 $header_user_agent $remote_addr $remote_user 等
log_format main '$header_user_agent' '$remote_addr - $remote_user'
}

模块:stub_status

显示Nginx当前连接的信息。

语法规则:

1
2
3
Syntax: stub_status;
Defualt: --;
Context: server, location;

例如:

1
2
3
4
5
6
7
server {
...
location /status {
# 输出Nginx状态
stub_status;
}
}

输出说明:

  1. 当前Nginx活跃的连接数;
  2. 握手总次数,总连接数,总请求数;
  3. 读写等的连接数。

模块:random_index

目录中随机选取一个文件作为主页,但是隐藏文件不会被选中。

语法规则:

1
2
3
Syntax: random_index on|off;
Defualt: random_index off;
Context: location;

例如:

1
2
3
4
5
6
7
8
server {
...
location / {
# 随机选择主页
random_index on;
root /var/www/html/html_set
}
}

模块:sub

用于替换HTTP内容。

语法规则(替换字符串):

1
2
3
Syntax: sub_filter old_string new_string;
Defualt: --;
Context: http, server, location;

语法规则(判断页面是否有更新):

1
2
3
Syntax: sub_filter_last_modified on|off;
Defualt: sub_filter_last_modified off;
Context: http, server, location;

语法规则(匹配字符串,只匹配第一个,还是匹配所有):

1
2
3
Syntax: sub_filter_once on|off;
Defualt: sub_filter_once off;
Context: http, server, location;

例如:

1
2
3
4
5
6
7
8
server {
...
location / {
root /var/www/html/html_set
index index.html
sub_filter 'server' 'first_server'
}
}

模块:limit_conn

限制连接频率(完成三次握手后,建立连接)。

语法规则:

1
2
3
4
5
6
Syntax: limit_conn_zone key zone=name:size;
Defualt: --;
Context: http;
# key 表示依据的变量,例如依据 $remote_addr 进行限制
# zone 空间名字
# size 空间大小

语法规则:

1
2
3
4
5
Syntax: limit_conn zone number;
Defualt: --;
Context: http, server, location;
# 根据上一条定义的 zone 使用
# number 限制的个数

例如:

1
2
3
4
5
6
7
8
9
10
11
http {
limit_conn_zone $binanry_remote_addr zone=my_zone:1m;
...
server {
...
location / {
...
limit_conn my_zone 3;
}
}
}

模块:limit_req

限制请求频率(一次连接可以有多次请求,如果开启Keep-alive)。

语法规则:

1
2
3
4
5
6
7
Syntax: limit_req_zone key zone=name:size rate=rate;
Defualt: --;
Context: http;
# key 表示依据的变量,例如依据 $remote_addr 进行限制
# zone 空间名字
# size 空间大小
# rate 以秒为单位的请求速率

语法规则:

1
2
3
4
Syntax: limit_conn zone=name [burst=number] [nodelay];
Defualt: --;
Context: http, server, location;
# burst 请求个数:对客户端限制速率,延迟返回

例如:

1
2
3
4
5
6
7
8
9
10
11
http {
limit_req_zone $binanry_remote_addr zone=my_zone:1m rate=1r/s;
...
server {
...
location / {
...
limit_req my_zone 3;
}
}
}

模块:access

基于IP $remote_addr的访问控制,不推荐使用,因为在使用CDN情况下会失效。如果使用

  • 可以尝试使用http_x_forward_for,但是不安全。
  • 可以使用geo模块。
  • 可以自定义HTTP变量传递。

语法规则:

1
2
3
Syntax: allow address|CIDR|unix:|all;
Defualt: --;
Context: http, server, location, limit_except;

语法规则:

1
2
3
Syntax: deny address|CIDR|unix:|all;
Defualt: --;
Context: http, server, location, limit_except;

例如:

1
2
3
4
5
6
7
8
server {
...
location ~ ^/admin.html {
...
deny ...;
allow all;
}
}

模块:auth_basic

基于用户的信任登录。

语法规则:

1
2
3
Syntax: auth_basic string|off;
Defualt: auth_basic off;
Context: http, server, location, limit_except;

语法规则(认证用的用户信息文件):

1
2
3
4
5
Syntax: auth_basic_user_file file;
Defualt: --;
Context: http, server, location, limit_except;
# 认证文件格式
# 用户名:密码:注释

例如:

1
2
3
4
5
6
7
8
server {
...
location ~ ^/admin.html {
...
auth_basic "Input your password.";
auth_basic_user_file /etc/nginx/auth/auth_a;
}
}

模块:sendfile

用于快速请求文件返回到客户端,其原理是利用sendfile技术,让内核直接读取文件,并通过网络发送出去,省去与用户空间的数据传输。

语法规则:

1
2
3
Syntax: sendfile on|off;
Defualt: sendfile off;
Context: http, server, location, if in location;

模块:tcp_nopush

在sendfile开启的情况下,提高网络包传输效率。多个数据包整合发送。

语法规则:

1
2
3
Syntax: tcp_nopush on|off;
Defualt: tcp_nopush off;
Context: http, server, location;

模块:tcp_nodelay

在keepalive开启的情况下,提高网络实时性。

语法规则:

1
2
3
Syntax: tcp_nodelay on|off;
Defualt: tcp_nodelay off;
Context: http, server, location;

模块:gzip

压缩传输。其他模块包含:

  • http_gzip_static_module 使用预压缩直接传输
  • http_gunzip_module 不支持gzip时,使用gunzip

语法规则:

1
2
3
Syntax: gzip on|off;
Defualt: gzip off;
Context: http, server, location, if in location;

语法规则(压缩比):

1
2
3
Syntax: gzip_comp_level level;
Defualt: gzip_comp_level 1;
Context: http, server, location;

语法规则(版本):

1
2
3
Syntax: gzip_http_version 1.0|1.1;
Defualt: gzip_http_version 1.1;
Context: http, server, location;

语法规则(文件类型):

1
2
3
Syntax: gzip_type type;
Defualt: --;
Context: http, server, location;

例如:

1
2
3
4
5
6
7
8
9
10
server {
...
location ~ .*\.(jpg|gif|png)$ {
gzip_on;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_type text/plain application/javascript text/css image/jpeg image/gif image/png;
root /var/www/html/static;
}
}

MIME type 列表参考地址

模块:add_header

添加头。

语法规则:

1
2
3
Syntax: add_header name value [always];
Defualt: --;
Context: http, server, location, if in location;

HTTP Headers 参考文档

模块:valid_referers

用于防盗链。

语法规则:

1
2
3
Syntax: valid_referes none|blocked|server_names|string ...;
Defualt: --;
Context: server, location;

模块:proxy_pass

用于代理。可以实现正向代理,反向代理,负载均衡等。

语法规则:

1
2
3
Syntax: proxy_pass uri;
Defualt: --;
Context: location, if in location, limit_except;

语法规则(头信息):

1
2
3
Syntax: proxy_set_header field value;
Defualt: proxy_set_header Host $proxy_host; proxy_set_header Connection close;
Context: http, server, location;

语法规则(跳转重定向):

1
2
3
Syntax: proxy_redirect default|off|(redirect replacement);
Defualt: proxy_redirect default;
Context: http, server, location;

语法规则(超时):

1
2
3
Syntax: proxy_connect_timeout time;
Defualt: proxy_connect_timeout 60s;
Context: http, server, location;

语法规则(缓冲区,一次性加载完毕再转发):

1
2
3
Syntax: proxy_buffering on|off;
Defualt: proxy_buffering off;
Context: http, server, location;

参考文档

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
server {
...
location / {
# 代理地址,如果后端返回301,可能需要改一下
proxy_pass http://localhost:8080;
proxy_redirect default;

# 修改客户端头信息
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;

# 超时设置
proxy_connect_timeout 30;
proxy_send_timeout 60;
proxy_read_timeout 60;

# 缓冲区大小
proxy_buffering on;
proxy_buffer_size 32k;
proxy_buffers 4 128k;
proxy_busy_buffers_size 256k;
proxy_max_temp_file_size 256k;
}
}

模块:upstream

负载均衡。

语法规则:

1
2
3
Syntax: upstream name {...};
Defualt: --;
Context: http;

其他选项:

  • down 暂不参与负载均衡
  • backup 作为备份服务器
  • max_fails 允许失败次数
  • fail_timeout 达到max_fails后服务暂停时间
  • max_conns 最大连接数

调度算法:

  • 轮询
  • 加权轮询
  • ip_hash
  • least_conn
  • url_hash
  • hash关键数值 hash自定义key

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
http {
...
upstream mystream1 {
server xx.xx.xx.xx:8001 weight=5;
server xx.xx.xx.xx:8002;
server unix:/tep/backend;
server xx.xx.xx.xx:8003 backup;
server xx.xx.xx.xx:8004 backup;
}
upstream mystream2 {
ip_hash;
server xx.xx.xx.xx:8001;
server xx.xx.xx.xx:8002;
}
upstream mystream3 {
hash $request_uri;
server xx.xx.xx.xx:8001;
server xx.xx.xx.xx:8002;
}
}

模块:rewrite

用于匹配URL。

语法规则:

1
2
3
Syntax: rewrite regex replacement [flag];
Defualt: --;
Context: server, location, if;

flag可以是:

  • break 停止rewrite匹配,直接在root指定的目录下查找,未找到则返回404
  • last 停止rewrite匹配,新建一个请求,再请求一次服务器
  • redirect 返回302临时重定向
  • permanent 返回301永久重定向,只要用户有缓存就会重定向
1
2
3
4
root /var/www/html
location ~ ^/last {
rewrite ^/last /test/ last;
}

优先级为:

  1. http
  2. server
  3. location
  4. location 内部的规则

用于防盗链。通过前后端加密传输防止盗链。例如客户端下载文件,服务端会生成带md5串和时间戳的链接返回给客户端,客户端再请求下载地址。

语法规则:

1
2
3
Syntax: security_link expression;
Defualt: --;
Context: http, server, location;

语法规则:

1
2
3
Syntax: security_link_md5 expression;
Defualt: --;
Context: http, server, location;

例如:

1
2
3
4
location / {
security_link $arg_md5, $arg_expires;
security_link_md5 "$security_link_expires$uri";
}

模块:geoip

(默认不安装)

解析IP的地域。基于IP地址匹配MaxMind GEOIP二进制文件得到地域信息。

包括的变量有:

  • $geoip_country_code
  • $geoip_country_name
  • $geoip_city

例如:

1
2
3
4
5
6
7
8
9
10
11
12
http {
geoip_country /etc/nginx/geoip/GeoIP.dat;
geoio_city /etc/nginx/geoip/GeoLiteCity.dat;
server {
...
location / {
if ($geoip_country_code != CN){
return 403;
}
}
}
}

配置案例

访问控制

原理:利用http_x_forward_for限制来访客户端。

1
2
3
4
5
6
7
8
9
10
server {
...
location / {
if($http_x_forward_for !~* "^xx\.xx\.xx\.xx") {
return 403;
}
root /var/www/html;
index index.html index.htm;
}
}

使用跨域限制

原理:在HTTP头部使用Access-Control-Allow-Origin,限制前端跨域访问的域。

1
2
3
4
5
6
7
8
server {
...
location ~ .*\.(htm|html)$ {
add_header Access-Control-Allow-Origin http://localhost;
add_header Access-Control-Allow-Method GET, POST, PUT, DELETE, OPTIONS;
root /var/www/html;
}
}

防盗链

原理:在HTTP头部使用Referer,防止盗链。

1
2
3
4
5
6
7
8
9
10
server {
...
location ~ .*\.(htm|html)$ {
valid_referers none blocked xx.xx.xx.xx;
if($invalid_referer){ // 非0,表示被限制了
return 403;
}
root /var/www/html;
}
}

代理

可以代理HTTP,HTTPS,邮件,RTMP等,既可以正向代理,也可以反向代理。

反向代理:

1
2
3
4
5
6
server {
...
location / {
proxy_pass http://localhost:8080;
}
}

正向代理:

1
2
3
4
5
6
7
server {
...
resolver 8.8.8.8;
location / {
proxy_pass http://$http_host$request_uri;
}
}

然后配置浏览器配置代理服务器。

负载均衡

按照范围分为:GSLB(全国范围),SLB(地域范围)。

按照OSI模型分类分为:4层负载均衡(TCP/IP),7层负载均衡(应用层)。

配置 Upstream:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
http {
...
upstream mystream {
server xx.xx.xx.xx:8001;
server xx.xx.xx.xx:8002;
server xx.xx.xx.xx:8003;
}

server {
...
location / {
proxy_pass http://mystream;
include proxy_params;
}
}
}

HTTPS

首先生成CA证书:

  1. 生成密钥;
  2. 根据密钥生成证书签名的请求文件CSR;
  3. 将密钥,CSR打包发送给签名机构,进行CA签名,得到CA证书。
    1
    2
    3
    4
    5
    6
    # 生成密钥
    openssl genrsa -out first.key 1024
    # 生成请求文件
    openssl req -new -key first.key -out first.csr
    # 自签名
    openssl x509 -req -days 3650 -in first.csr -signkey first.key -out first.crt

也可以直接到腾讯云上申请免费SSL认证。申请通过后,服务器直接返回CSR请求文件CRT证书私钥

配置Nginx SSL服务。

1
2
3
4
5
6
server {
listen 443;
ssl on;
ssl_certificate /etc/nginx/ssl_key/first.crt;
ssl_certificate_key /etc/nginx/ssl_key/first.key;
}

Tomcat

1
2
3
4
5
6
7
8
9
10
11
12
13
http {
...
upstream java_api {
server xx.xx.xx.xx:8080;
}

server {
location ~ \.jsp$ {
proxy_pass http://mystream;
include proxy_params;
}
}
}

Django

项目目录:

  • project
    • app1/
    • project/
      • wsgi.py
    • manage.py
    • project_wsgi.ini

安装运行uwsgi:

1
2
sudo pip install uwsgi
uwsgi --http :8001 --chdir /django/project/path/ --wsgi-file project.wsgi

注:Windows下需要自行下载源码编译,使用MinGW编译。

通过配置文件project_wsgi.ini启动uwsgi:

1
2
3
4
5
6
7
[uwsgi]
socket = :9090
chdir = /django/project/path
module = project.wsgi # project/wsgi.py
master = true
processes = 4
vacuum = true

启用配置:

1
uwsgi --ini uwsgi.ini

修改Nginx配置:

1
2
3
4
5
6
server {
location / {
uwsgi_pass xx.xx.xx.xx:port;
include uwsgi_params;
}
}

PHP

默认使用fastcgi调用PHP。

1
2
3
4
5
6
7
8
9
server {
...
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}