AES
aes 的使用
local aes = require "resty.aes"
--aes 的使用
local aes_encode = function ()
--16 位 --cbc 128 pkcs7 16 偏移量为 key
local key = "1234567890123456"
local aesEn = aes:new(key,nil,aes.cipher(),{iv = key})
local str = "123"
local ss = aesEn:encrypt(str)
local b64en = ngx.encode_base64(ss)
return b64en
end
local aes_decode = function (str)
--16 位
local key = "1234567890123456"
local aesEn = aes:new(key,nil,aes.cipher(),{iv = key})
str = ngx.decode_base64(str)
local ss = aesEn:decrypt(str)
return ss
end
FFI
#include <stdio.h>
#include <stdlib.h>
#include "libdbenc.h"
//gcc Demo.c -fPIC -shared -o libtest.so
void say(int a){
//unsigned int len = strlen(str);
}
// 不行 必须是 const char *
void Abc2(char * str){
unsigned int len = strlen(str);
for(int i=0; i<len; i++){
str[i] ^= 1;
}
return;
}
char * Abc(const char * str){
unsigned int len = strlen(str);
char *p = malloc(len+1);
memset(p,0,len+1);
for(int i=0; i<len; i++){
p[i] = str[i]^1;
}
return p;
}
void free2(void *p){
free(p);
}
--ffi 使用 gcc Demo.c -fPIC -shared -o libtest.so
local ffi = require("ffi")
local myffi = ffi.load("./app/lib/libtest.so")
ffi.cdef[[
void say(int a);
char * Abc(const char * str);
void free2(void *p);
]]
local abb = "123"
local c_str = ffi.new("char[?]", #abb)
ffi.copy(c_str, abb)
myffi.Abc2(c_str)
测试简单生成so文件
gcc Demo.c -fPIC -shared -o libtest.so
日志切割
mv aa.log bb.log
./nginx -s reopen
定时备份日志
crontab -l
Nginx 停止
./nginx -s stop
./nginx -s quit
Ningx 检查配置语法
./nginx -t
nginx 版本信息 编译信息
nginx -v nginx -V
Nginx 热部署
ps -ef |grep nginx
更换nginx
mv nginx nginx.old 备份
给nginx master 发送信息告诉他我要热部署:kill -USR2 13195
ps -ef|grep nginx
发送信号给老的master 进程告诉他优雅关闭work进程 kill -WINCH 13195
nginx 静态服务器
gzip on;
gzip_min_length 1; #小于1字节 不压缩
gzip_comp_level 2; #压缩级别
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/gif image/jpeg image/png; #压缩类型
location / {
alias dlib/;
#autoindex on; #共享静态资源
#set $limit_rate 1k; #limit_rate 内置变量 限制访问速度 每秒传输的字节数
#index index.html index.htm;
}
日志位置
access_log ./logs/plugin_access.log main;
日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
nginx 反向代理
缓存服务器
#proxy_cache_path /temp/nginxcache levels=1:2 keys_zone=my_cache:10m max_siz=10g inactive=60m use_temp_path = off;
localtion / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
缓存服务器
#proxy_cache my_cache;
#proxy_cache_key $host$uri$is_args$args;
#proxy_cache_valid 200 304 302 1d;
proxy_pass http://local;
}
go access 格式化
goaccess 安装 使用
生成DV 免费证书
yum install python2-certbot-nginx
certbox --nginx --nginx-server-root=/usr/local/openresty/nginx/conf -d geektime.taohui.pub
Openresty 编译
Nginx 信号
master 进程信号
TERM INT
QUIT
HUP
USR1
USR2
WINCH
Worker 进程信号
TERM INT
QUIT
USR1
WINCH
nginx 命令行信号
reload HUP
reopen USR1
stop TERM
quit QUIT
发送信号 用kill kill -USR2 进程号
提高Nginx网络吞吐量之buffers优化
client_body_buffer_size
此指令设置用于请求主体的缓冲区大小。 如果主体超过缓冲区大小,则完整主体或其一部分将写入临时文件。 如果NGINX配置为使用文件而不是内存缓冲区,则该指令会被忽略。 默认情况下,该指令为32位系统设置一个8k缓冲区,为64位系统设置一个16k缓冲区。 该指令在NGINX配置的http,server和location区块使用
server{
client_body_buffer_size 8k;
}
client_max_body_size
此指令设置NGINX能处理的最大请求主体大小。 如果请求大于指定的大小,则NGINX发回HTTP 413(Request Entity too large)错误。 如果服务器处理大文件上传,则该指令非常重要。
默认情况下,该指令值为1m
server{
client_max_body_size 2m;
}
client_body_in_file_only
此指令禁用NGINX缓冲区并将请求体存储在临时文件中。 文件包含纯文本数据。 该指令在NGINX配置的http,server和location区块使用。 可选值有: off:该值将禁用文件写入 clean:请求body将被写入文件。 该文件将在处理请求后删除。 on: 请求正文将被写入文件。 处理请求后,将不会删除该文件。 默认情况下,指令值为关闭
http{
client_body_in_file_only clean;
}
client_body_in_single_buffer
该指令设置NGINX将完整的请求主体存储在单个缓冲区中。 默认情况下,指令值为off。 如果启用,它将优化读取$request_body变量时涉及的I/O操作
server{
client_body_in_single_buffer on;
}
client_body_temp_path
此指令指定存储请求正文的临时文件的位置。 除了位置之外,指令还可以指定文件是否需要最多三个级别的文件夹层次结构。 级别指定为用于生成文件夹的位数。
默认情况下,NGINX在NGINX安装路径下的client_body_temp文件夹创建临时文件。
server{
client_body_temp_pathtemp_files 1 2;
}
该指令生成的文件路径如temp_files/1/05/0000003051。
client_header_buffer_size
此指令与client_body_buffer_size类似。 它为请求头分配一个缓冲区。 如果请求头大小大于指定的缓冲区,则使用large_client_header_buffers指令分配更大的缓冲区。如下例子:
http{
client_header_buffer_size 1m;
}
large_client_header_buffers
此指令规定了用于读取大型客户端请求头的缓冲区的最大数量和大小。 这些缓冲区仅在缺省缓冲区不足时按需分配。 当处理请求或连接转换到保持活动状态时,释放缓冲区。如下例子:
http{
large_client_header_buffers 4 8k;
}
Nginx 优化
减少请求
-
开发优化:减少请求量 css 合并 图片合并
-
nginx 的expires 利用浏览器的缓存减少请求
-
cdn 来响应请求
-
不可避免的请求 服务器集群 负载均衡来支持
nginx 响应请求
1 建立socket连接
2 打开文件数
系统层优化
ulimit -n
ulimit -n 20000 设置文件打开数量
tcp 加快连接回收 recycle。 echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
空的tcp是否允许回收利用. echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
不做洪水攻击抵御:echo 0>/proc/sys/net/ipv4/tcp_syncookies
echo 50000 >/proc/sys/net/core/somaxconn
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
echo 0>/proc/sys/net/ipv4/tcp_syncookies
nginx 一个进程允许打开多少文件
worker_limit_nofiler 10000;
高并发 keepalive_timeout 0;
防止tcp排队一层取多次数据 加快tcp 快速回收
gzip数据压缩
sdch google支持的压缩方式
gzip on;
gzip_buffers 32 4k| 16 8k. 在内存中缓存几块 往外输出
gzip_min_length 1; #小于1字节 不压缩
gzip_comp_level 6; #压缩级别 一般设置为 6
gzip_http_version 1.0|1.1 可以不设置 开始压缩http协议的版本
gzip_proxied. 设置请求者代理服务器 改如何缓存内容
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/gif image/jpeg image/png; #压缩类型
gzip_vary on|off 告知浏览器是否压缩
eg:
gzip on;
gzip_buffers 32 4k; 缓存32块 每块4k
gzip_com_level 6;
gzip_min_length 200;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/gif image/jpeg image/png;
eg:
gzip_
gzip_proxied any;
expires 过期
缓存到客服端上去
注意 服务器的日期要准确
304 服务端只是返回一个Etag 标签
location / {
expires 30s;
}
location
-
精准匹配。 location = patt {}
-
一般匹配。 location patt {}
-
正则匹配。 location ~patt{}
顺序 :
有精准匹配 匹配到 停止匹配
一般匹配 命中多个 以最长的为准 记忆匹配结果 去正则匹配 无正则返回结果
正则匹配: 多个正则任意一个命中就返回结果 无命中 返回记忆结果
- = 严格匹配
- 区分大小写 匹配 正则
~*不区分大小写 匹配 正则
!~ 区分大小写 不匹配 正则
!~* 不区分大小写 不匹配 正则
^~ 如果把这个前缀用于一个常规字符串,那么告诉nginx 如果路径匹配那么不测试正则表达式
二 alias与root的区别
- root 实际访问文件路径会拼接URL中的路径
- alias 实际访问文件路径不会拼接URL中的路径
示例如下:
location ^~ /sta/ {
alias /usr/local/nginx/html/static/;
}
- 请求:http://test.com/sta/sta1.html
- 实际访问:/usr/local/nginx/html/static/sta1.html 文件
location ^~ /tea/ {
root /usr/local/nginx/html/;
}
- 请求:http://test.com/tea/tea1.html
- 实际访问:/usr/local/nginx/html/tea/tea1.html 文件
命令行方式
resty -e 'ngx.say(“hello word”)'
TCP
ngx_stream_echo_module
ngx_stream_lua_module
UDP
Lua-resty-tt2
OpenResty Package Manger
Opm
/usr/local/openresty/bin/opm
opm search ini
opm get doujiang24/lua-resty-ini
opm remove doujiang24/lua-resty-ini
作者上传 GitHub 方式认证
cat ~/.opmrc
opm build
opm upload
安装openresty 二进制包 修改 yum
sudo yum-config-manager --add-repo \https://openresty.org/yum/cn/centos/OpenResty.repo
Lua-resty-limit-traffic
Lua-tablepool
Openresty edge platform
WAF web防火墙
Y 语言
openresty Trace platform
openresty traffic control platform。流量控制
Linux eBPF in-kernel VM
Orsql 数据分析平台
Fan 语言 文本处理
OpenResty Inc.
如何拿到真实的用户的ip地址
X-Forwarded-For : ip 累计
X-Read-IP 用户真实ip
postread 阶段
Rewrite 阶段
return 指令
return code
code url
return url
http 1.0 301 永久重定向。
302 临时重定向
http 1.1 303 临时
307
308 永久重定向
nginx 自定义 444 关闭连接 不再向客户端返回数据
error_page : 指令
rewrite : 指令 regex 指定url 替换新的url
if 指令
位置 :server location
检查文件是否存在 -f !f -d -e -x软连接
find_config 阶段 找到 localtion
localtion 可以嵌套
前缀
正则
内部@
顺序
先精确
再最长匹配
后正则
停止正则匹配 ^~
preaccess 阶段
Limit
readip
zone 一般都是共享内存
limit_conn 模块
limit_conn_zone key zone=name:size; 指令 http
limit_conn zone number; 限制并发连接数 http server location
limit_rate 50; 限制返回速度 每秒50 字节
limit_conn_log_level info|err|
eg:limit_conn_log_level error;
限制发生向客户端返回的错误码
limit_conn_status 503;
eg
limit_conn_zone $binary_remote_addr zone=addr:10m; 10M的共享内存
limit_conn_zone $server_name zone=perserver:10m;
server{
location /{
limit_conn_status 500; 默认503
limit_conn_log_level warn;
limit_rate 50;限制返回速度 每秒50 字节
limit_conn addr 1; 限制并发连接数为1
}
}
limit_req 模块 限制一个连接数 每秒处理的请求数
leaky bucket 算法
limit_req_zone key zone=name:size rate=true
limit_req zone = name [burst=number][nodelay]
limit_req_log_level info|error #limit_req模块错误级别
limit_red_status 503; #返回默认错误码 默认503
eg:
#limit_conn_zone $binary_remot_addr(用户ip地址) zone=addr:10m;
limit_req_zone $binary_remot_addr zone=one:10m rate=2r/m; 每分中处理2个请求
location /{
#limit_conn_status 500;
#limit_conn_log_level warn;
limit_rate 50;
#limit_conn addr 1;
limit_req zone = one burst=3(盆的大小) nodelay;#nodelay 表示这3个请求立马处理
#limit_req zone=one; #burst默认为0
}
access 阶段 控制URL 是否可以向下访问
allow 允许
deny 不允许
auth_basic 校验密码
auth_basic string|off
auth_basic off;
auth_basic_user_file file;
生成 file。httpd-tools
命令 htpasswd -c file -b user pass
auth_request 模块 静态文件验证 到代理后台 默认没有编译进去nginx
原理:收到请求 生成子请求 通过反向代理技术把请求传递给上游服务
auth_request uri|off; 指令
auth_request_set $variable value;
eg:
location /{
auth_request /test_auth; 生成子请求
}
location = /test_auth {
proxy_pass http://127.0.0.1:8080/auth_upstream;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
satisfy (满足) 指令 允许改变执行顺序
satify all|any;
precontent 阶段 用于反向代理
try_files file;
mirror 模块 处理请求时可以生成子请求访问其他服务 用于简单的流量拷贝
mirror uri|off;指令
mirror_request_body on|off; 请求中的body转发到子请求中
eg:
location /{
mirror /mirror;
mirror_request_body off;
}
location = /mirror{
internal;
proxy_pass http://127.0.0.1:8080$request_uri;
proxy_pass_request_body off;
proxy_set_header Context-Length "";
proxy_set_header X-Original-URI $request_uri;
}
Content 阶段
root 指令
alias 指令 location
log_not_found on|off;
目录访问不带 / 问题
absolute_redirect on|off; 重定向 返回域名 301
server_name_in_redirect on|off;
eg:
server{
server_name return.taohui.tecg dir.taohui.tech;
server_name_in_redirect off;
listen 8800;
proxy_in_redirect on;
absolute_redirect off;
root html/;
}
index
autoindex
autoindex on|off;
autoindex_exact_size on|off;
autoindex_fotmat html|json|xml; 显示格式
autoindex_localtime on|off; 是否用本地时间
eg:
location /{
alias html;
autoindex on;
#index a.html;
autoindex_exact_size off;
autoindex_format json;
autoindex_localtime on;
}
concat 模块 阿里提供 多个小文件 合并到一起
git地址 https://github.com/alibaba/nginx-http-concat
加入nginx --add-module=../nginx-http-concat/
使用 :http://www??a.html,b.html
concat on|off;
concat_delimiter string;
concat_unique on|off;
concat_max_files:number
concat_ignore_file_error on|off;
eg:
catcaon on;
location /{
concat_max_files 20;
concat_types text/plain;
concat_unique on;
concat_delimiter ':::';
concat_ignore_file_error on;
}
static 模块
静态服务器
三个变量:
request_filename:待访问文件的完整路径
document_root:由URI 和root/alias 规则生成的文件夹路径
realpath_root:将document_root 中的软连接等换成真实路径
eg:
location /readpath/{
alias html/readpth(是个软连接)/
return 200 '$request_filename: $document_root :$readpth_root\n';
}
curl http://xxxx/readpath/1.txt
访问目录没有带问号/?
是目录但是url末尾没有加/时 会返回301 重定向 重定向时 url 的变化
eg:
server{
sever_name localhost;
server_name_in_redirect off;
listen 8008;
port_in_redirect on;
#absolute_redirect off;
root html/;
}
Log 阶段 记录日志
log_format name ''; 定义log 格式
access_log path ;定义log日志文件
access_log off;
open_log_file_cache max=n
open_log_file_cache off;
http 过滤模块
sub 模块
ngx_http_sub_filter_module 模块默认没有编译进nginx --with-http_sub_filter_module 启用改模块
sub_filetr string
sub_filetr_last_modified on|off;
sub_filter_once on|off;
sub_filter_types mime-type;
eg:
location /{
sub_filter 'Nginx.org' '$host/nginx';
sub_filter 'nginx.com' '$host/nginx';
sub_filetr_once on;
sub_filter_once off;
sub_filter_last_modified off;
sub_filter_last_modified on;
}
addition 模块
原理: 新增子请求添加到原始的响应的前和后
add_before_body uri;
add_after_body uri;
addition_types mime-type;
eg:
location /{
add_befor_body /befor_action;
add_after_body /after_action;
addition *;
}
location /befor_action {
return 200 'new context before';
}
localtion /after_action{
return 200 'new context after';
}
Nginx 变量
提供变量。使用变量
惰性求值
HTTP 框架提供的变量
http 请求相关的变量
arg_变量 : url 中某个具体参数值
query_string: 与args变量完全相同
args:全部url参数
is_args:如果请求url中有参数者返回 否者返回空
content_length:http 请求中标识长度的Context-Length头部的值
content_type:标识请求包体类型的Content-type头部值
uri:请求的URI(不同于url 不包括?号面的参数)
document_uri:月uri完全相同
request_uri:请求的url 包括uri以及完整的参数
scheme:协议名如http。https
request_method:请求方法,如 get post
request_lenght:所请求的内容大小,包括请求行 头部。包体 等
remote_user:有http basic authentication 协议传入的用户名
2,(1)request_body_file
临时存放请求包体的文件
如何包体非常小则不会存文件
client_body_in_file_only 强制所有包体存入文件,并且可以决定是否删除
(2) request_body
请求中的包体 这个变量当且仅当使用反向代理,且设定用内存暂存包体才有效
(3)request
原始的url请求 get/?a=1&b=2 HTTP 1.1
3 host
http_头部名字 返回一个具体请求头部的值
特殊:http_host
http_user_agent
http_referer
http_via
http_x_forwarded_for
http_cookie
TCP 连接请求相关的变量
binary_remote_addr:客户端地址的整型格式,对于ipv4是4字节 ipv6 是16字节
connection:递增连接序号
connection_requests:当前连接上执行过的请求数 对keepalive连接有意义
remote_addr:客户端地址
remote_port:客户端端口
proxy_protocol_addr:若使用了proxy_protocal协议则返回协议中的地址 否则返回空
proxy_protocol_prot: 者返回协议中的端口 否则返回空
server_addr:服务器地址
server_prot:服务器端口
TCP_INFO:tcp内核层参数
sever_protocol:服务器协议 例如 HTTP/1.1
Nginx 处理请求过程中产生的变量
request_time:请求处理到现在的耗时 单位为秒。精确到毫秒
server_name:匹配上请求server_name 值
https:如何开启了TLS/SSL则返回on 否则为空
requet_completion:若请求处理完则返回ok 否则返回为空
request_id:以16进制的请求标识id 该id共含有16个字节是随机生成的
resquest_filename:待访问文件的完整路径
document_root:由URL和root/alias 规则生成的文件
realpath_root:将document_root中的软连接等换成真实的路径
limit_rate:返回客户端响应时的速度上限 单位为每秒字节数。可以通过set指令修改对请求产出效果
发送HTTP 响应时相关的变量
body_bytes_sent:响应中body包体的长度
bytes_sent:全部http响应的长度
status:http响应中的返回码
sent_trailer_名字:把响应结尾内容值返回
sent_http_头部名字:响应中某个具体头部值
特殊:
sent_http_content_type
sent_http_content_lenght
sent_http_location
sent_http_last_modified
send_http_connection
send_http_keep_alive
send_http_transfer_encoding
send_http_cache_control
send_http_link
Nginx系统变量
time_local:以本地时间标准输出当前时间 例如:14/Nov/2018:15:55:37 +0800
time_iso8601:使用ISO 8601标准输出的当前时间 例如:2018-11-14T15:55:37+0800
nginx_version:nginx版本号
pid:所属worker进程的进程id
pipe:使用了管道则返回p 否则返回.
hostname:所在服务器的主机名
msec:1970年1月1日到现在的时间 单位秒 小数点后精确到毫秒
Referer 模块
简单有效的防盗链模块 目的:拒绝非正常的网站访问我们站点资源
思路:
通过referer模块用invalid_referer 变量根据配置判断referer头部是否合法
指令:
Syntax: valid_referer none(允许为空)|blocked(允许referer头部没有对应的值)|server_names(若referer中站点域名与server_name中的站点域名某个匹配则允许访问)|string(正则表达式) ...;
Context: server,localtion
Syntax:referer_hash_bucket_size size;
Default: referer_hash_bucket_size 64;
Context:server , localtion
Syntax:referer_hash_max_size size;
Defalut:referer_hash_max_size 2048;
Context: server ,localtion;
eg:
server_name referer.taohui.tech;
location / {
valid_referer none blocked server_names *.taohui.pub www.taohui.org.cn/nginx/ ~\.google\.;
if($invalid_referer){
return 403;
}
}
Secure_link 模块
防盗链
原理:哈希算法 客户端拿到的只是 哈希后的url
指令:
Syntax: secure_link expression; 是否过期
Context:http,server,location
Syntax:secure_link_md5 expression;是否过期
Context:http,server,location
Syntax:secure_link_secret word;
Context:location
eg:
localtion /{
secure_link $arg_md5,$arg_expires;
secure_link_md5 '$secure_link_expires$remote_addr secret'
if($secure_link = ""){
return 403;
}
if($secure_link = "0"){
return 410;
}
return 200 '$sercure_link:$secure_link_expires\n';
}
Map 模块
指令:
Syntax: map string $variable {...}
Context:http
Syntax: map_hash_bucket_size size;
Defalut:map_hash_bucket_size 32|64|128;
Context:http
Syntax: map_hash_max_size size;
Defalut:map_hash_max_size 2048;
Context:http
map $http_host $name{
hostnames;
default 0;
~map\.tao\w+\.org.cn 1;
}
map $http_user_agent $mobile{
defalut 0;
"~opera mini" 1;
}
当 $http_user_agent 匹配到 "~opera mini" 时 $mobile =1
split_clients 模块
可以根据变量的值按照百分比的方式生成新的变量
用于AB测试
指令:
syntax: split_clients string $variable {...}
Context:http
split_clients "${http_testcli}" $variant{
0.51% .one;
20.0% .two;
50.5% .three;
40% .four;
* "";
}
geo 模块
ip地址 子网掩码 生成 新的变量
syntax: geo [$address] $variable {...}
Context:http
默认取 X-Forwarder-For 最后一个ip
geo $country{
defalut ZZ;
#include conf/geo.conf
proxy 116.62.160.193;
127.0.0.0/24 US;
127.0.0.1/32 RU;
10.1.0.0/16 RU;
192.168.1.0/24 UK;
}
geoip 模块
IP 所属的位置 默认没有编译进去 通过 --with-http_geoip_module 需要编译地址库
指令:
Sysntax:geoip_city file;
Context:http
geoip_city 指令提供的变量:
$geoip_latiude:经度
$geoip_longitude:纬度
$geoip_city_continent_code:属于那个洲
与$geoip_country 指令生成的变量重叠
$geoip_city_country_code:两个字母的国家代号
$geoip_city_country_code3 :三个字母的国家代号
$geoip_city_country_name :国家名称
$geoip_region:省的编号
$geoip_region_name:省的名称
$geoip_city:城市名称
$geoip_postal_code:邮编
$geoip_area_code:仅美国使用的电话区号
$geoip_dna_code:仅美国使用的DMA 编号
对客户端keepalive
复用TCP连接
Syntax:keepalive_disable none|brower ..;
Default:keepalive_disable msie6; ie6
Context:http,server,location
Syntax:keepalvie_requests number; 一个连接 处理多少个请求
Defalut:keepalive_requests 100;
Context:http,server,location
Syntax:keepalive_timeout timeout [header_timeout]
Default:keepalvie_timeout 75s;
Context:http,server,location
负载均衡
加权 Round-Robin 算法
对上游使用keepalive 长连接 (更好)
proxy_http_version 1.1; 防止1.0
proxy_set_header Connection ""; 防止header close
ip_hash 算法
hash key [consistent] 算法
upstream_keepalive
Syntax:keepalive connectuons;
Context:upstream
1.15.3 不稳定版
keepalive_requests connections;
tcpdump 抓包
tcpdump -i lo prot 8000
Syntax:ip_hash
Context:upstream
Syntax:hash key [consistent]
Context:upstream
server{
real_ip_recursive on;
real_ip_header X-Forward-for;
server_name iphash.taohui.tech;
localtion /{
proxy_pass http://iphashups;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
一致性hash算法
hash 问题:
宕机或者扩容时 hash算法引发大量路由变更可能导致缓存大范围失效
eg: key % 5 变成 key % 4
Syntax:hash key [consistent(一致性哈希)]
Context:upstream
upstream_least_conn 模块
优先选择连接最少的上游服务器(找出并发连接数最少的一个将请求转发给它)
Syntax:least_conn
Context:upstream
upstream_zone 模块
使用共享内存使负载均衡策略对所有worker进程生效
Syntax:zone name[size];
Context:upstream
upstream 提供的变量
upstream 变量 不含cache
upstream_addr:上游服务器地址
upstream_connect_time:与上游连接消耗的时间单位秒 精确到毫秒
upstream_header_time:接收上游服务发回响应中http头部所消耗的时间单位秒 精确到毫秒
upstream_response_time:接收完整的上游服务发回响应中http头部所消耗的时间单位秒 精确到毫秒
upstream_http_名称:从上游服务器返回的响应头部值
upstream_bytes_received:从上游服务器接收到的响应长度 单位为字节
upstream_response_length:从上游服务器返回的响应包体长度 单位字节
upstream_status:上游服务器返回的http响应码,如果未连接上该变量为502
upstream_cookie_名称:从上游服务器发回的响应头Set-Cookie中取出cookie值
upstream_trailer_名称:从上游服务器的响应尾部取到的值
proxy_pass 模块
[外链图片转存失败(img-pqJxKIXI-1564906359006)(./img/proxy_http.png)]
proxy_pass 在content阶段处理
Syntax:proxy_pass url;
Context:localtion if in location,limit_except
url: 必须以http:// 或者https://开头 接下来是域名,ip,unix socket 地址或者upstream 名字
urL 参数中携带uri 与否会导致上游请求url不同
不携带uri 则将客户端请求中的url直接转发给上游
location 后使用正则表达式 @名字时 应采用这种方式
携带uri:则对用户请求中的url作如下操作:
将location参数中匹配上的一段替换为该uri
eg:
location /a {
proxy_pass http://abc/www
}
会变成 http://abc/www/xxxx 会替换掉/a
proxy 模块
生成发网上游的请求行
生成发网上游的请求行
Syntax: proxy_method method;
Context: http,server,location
Syntax:proxy_http_version 1.0|1.1;
Default:proxy_http_version 1.0;
Context:http,server,location
生成发往上游的请求头
生成发网上游的请求头
Syntax:proyx_set_header field value;
Defalut:proxy_set_header Host $proxy_host;
proxy_set_header Connection close;
Context:http,server,location
注意:若value的值为空字符串 则整个header都不会向上游发送
Syntax:proxy_pass_request_headers on|off;
Default:proxy_pass_request_headers on;
Context:http,server,location
生成发往上游的包体
生成发往上游的包体
Syntax:proxy_pass_request_body on|off;
Default:proxy_pass_request_body on;
Context:http,server,location
Syntax:proxy_set_body value;
Context:http ,server,location
接收客户端请求的包体
接收客户端请求的包体:收完再转发还是边收边转发
Syntax:proxy_request_buffering on|off; on:客户端网速慢上游并发能力低适应高吞吐量 off:即时响应,降低nginx读写磁盘消耗 一旦开始就发送
Default:proxy_request_buffering on;
Context:http,server,location
客户端包体的接收:
Syntax:client_body_buffer_size size; :若接收头部时已经接收完全部包体不分配,或者分配
Default:client_body_buffer_size 8k|16k;
Context:http,server,location
Syntax:client_body_in_single_buffer on|off;
Defalut:client_body_in_single_buffer off;
Context:http,server,location
最大包体的长度限制 大于1m 返回 413
Syntax:client_max_body_size size;
Default:client_max_body_size 1m;
Context:http,server,location
临时文件路径格式:
Syntax:client_body_temp_path path[levell[level2[level3]]]
Default:client_body_temp_path client_body_temp; nginx 启动成功创建client_body_temp 存放临时文件
Context:http,server,location
Syntax:client_body_in_file_only on|clean|off; 包体必须存放在文件中 on 记录文件不删除 clean处理完删除 off 小文件不记录文件中 大文件记录文件中处理完成删除
Defalut:client_body_in_file_only off;
Context:http,server,location
读取包体时的超时
Syntax:client_body_timeout time;
Defalut:client_body_timeout 60s; 超时408错误
Context:http,server,location
proxy_connect_timeout 60;后端服务器连接的超时时间 发起握手等候响应时间
proxy_read_timeout 60s;连接成功后等候后端服务器响应时间 其实已经进入后端排队之中等候
proxy_send_timeout 60;后端服务器数据回传时间 就是在规定时间内后端服务器必须传完数据
与上游建立连接
Syntax:proxy_connect_timeout time;
Defalut:proxy_connect_timeout 60s; 建立tcp连接 超时 502 三次握手不成功
Context:http,server,location
Syntax:proxy_next_upstream http_502|...;没有建立连接更换一台重新连接
Default:proxy_next_upstream error timeout;
Context:http,server,location
向上游发送HTTP请求
Syntax:proxy_send_timeout time;
Defalut:proxy_send_timeout 60s;
Context:http,server,location
上游连接启用TCP keepalive
Syntax:proxy_socket_keepalive on|off; on :tcp
Defalut:proxy_socket_keepalive off;
Context:http,server,location
上游连接启用HTTP keepalive
Syntax:keepalive connections;
Context:upstream
Syntax:keepalive_requests number;
Default:keepalive_requests 100;
Context:upstream
修改TCP 连接中的local address
Syntax:proxy_bind address [transparent]|off;
Context:--
Syntax:http,server,location
可以使用变量:
proxy_bind $remote_addr
可以使用不属于所载的机器IP地址:
proxy_bind $remote_addr transparent;
当客户端关闭连接时
Syntax:proxy_ignore_client_abort on|off;
Context:proxy_ignore_client_abort off;
Syntax:http,server,location
接收上游的HTTP响应头部
Syntax:proxy_buffer_size size;
Context:proxy_buffer_size 4k|8k;
Syntax:http,server,location
error.log:upstream send too big header
接收上游HTTP包体
Syntax:proxy_buffers number size;
Context:proxy_buffers 8 4k|8k; 能存放就不向磁盘写入数据。或者向磁盘写入数据
Syntax:http,server,location
接收上游的HTTP包体
Syntax:proxy_buffering on|off;
Context:proxy_buffering on; X-Accel-Buffering 头部 yes no
Syntax:http,server,location
向磁盘写入数据控制
Syntax:proxy_max_temp_file_size size;
Context:proxy_max_temp_file_size 1024m;
Syntax:http,server,location
Syntax:proxy_temp_file_write_size size;
Context:proxy_temp_file_write_size 8k|16k;
Syntax:http,server,location
Syntax:proxy_temp_path path[level1[level2[level3]]]
Context:proxy_temp_path proxy_temp;
Syntax:http,server,location
即时转发包体
Syntax:proxy_busy_buffers_size size;
Context:proxy_busy_buffers_size 8k|16k;
Syntax:http,server,location
接收上游时网络速度相关指令
Syntax:proxy_read_timeout time;
Context:proxy_read_timeout 60s;
Syntax:http,server,location
Syntax:proxy_limit_rate rate;
Context:proxy_limit_rate 0; 0 无限制读取上游数据
Syntax:http,server,location
上游包体的持久化
Syntax:proxy_store_access users:permissions ...;
Defalut:proxy_store_access user:rw;
Context:http,server,location
Syntax:proxy_store on|off|string;
Defalut:proxy_store off;
Context:http,server,location
eg:
server{
root /tmp;
location / {
proxy_pass http://proxyups;
proxy_store on; #保存数据到 /tmp中
proxy_store_access user:rw group:rw all:r;
}
}
禁用上游响应头部的功能
在过滤阶段中起作用
Syntax:proxy_ignore_headers filed ..; #某些响应头可以改变nginx的行为 可以禁止他们生效
Context:http,server,location
在nginx中才识别的头定义
可以禁用的功能头部
X-Accel-Redirect:由上游服务指定在nginx内部重定向 控制请求的执行
X-Accel-Limit-Rate:由上游设置发往客户端的速度限制 等同limit_rate
X-Accel-Buffering:由上游控制是否缓存上游响应
X-Accel-Charset:由上游控制Content-Type中Charset
缓存相关:
X-Accel-Expire:设置响应在nginx中的缓存时间 单位秒 @开头表示一天内谋生刻
Expire:控制nginx缓存时间 优先级低于X-Accel-Expire
Cache-Control:控制nginx缓存时间 优先级低于X-Accel-Expire
set-cookie:响应中出现Set-Cookie则不缓存可以通过proxy_ignore_header 禁止生效
转发上游的响应
Syntax:proxy_hide_header field; #对于上游响应中的某些头部 设置不向客户端转发
Context:http,server,location
proxy_hide_header 默认不转发响应的头部:
Date:由ngx_http_header_filer_module 过滤模块填写
Server:由ngx_http_header_filer_module 过滤模块填写 值为nginx
X-Pad:通常是Apache为避免浏览器bug生成的头部 默认忽略
X-Accel-:用于控制nginx行为响应 不需要向客户端转发
Syntax:proxy_pass_header field; #对于已经被roxy_hide_header 设置向上游转发
Context:http,server,location
修改返回的Set-Cookie头部
Syntax:proxy_cookie_domain off;
proxy_cookie_domian domain replacement;
Default:proxy_cookie_domain off;
Context:http,server,location
Syntax:proxy_cookie_path off;
proxy_cookie_path path replacement;
Default:proxy_cookie_path off;
Context:http,server,location
修改返回的Location 头部
Syntax:proxy_redirect default;
proxy_redirect off;
proxy_redirect redirect replacement;
Default:proxy_redirect default;
Context:http,server,location
上游返回失败时的处理办法
Syntax:proxy_next_upstream error|timeout|invalid_header|http_500|off ...;
Default:proxy_next_upstream error timeout;
Context:http,server,location
限制 proxy_next_upstream 的时间与次数
Syntax:proxy_next_upstream_timeout time;
Default:proxy_next_upstream_timeout 0;
Context:http,server,location
Syntax:proxy_next_upstream_tries number;;
Default:proxy_next_upstream_tries 0;
Context:http,server,location
用error_page 拦截上游失败响应
当上游响应的响应码大于等于300时 应将响应返回客户端还是按error_page指令处理
Syntax:proxy_intercept_errors on|off;
Default:proxy_intercept_errors off;
Context:http,server,location
对上游使用SSL连接
对下游使用证书
Syntax:ssl_certificate file;
Context:http,server;
Syntax:ssl_certificat_key file;
Context:http,server
验证下游证书
Syntax:ssl_verify_client on|off|optional|optional_no_ca;
Default:ssl_verify_client off;
Context:http,server
Syntax:ssl_client_certificate file;
Default:---
Context:http,server
ssl模块提供的变量
安全套件:
ssl_cipher:本次通讯选用的安全套件 eg:ECDHE-RSA-AES-128-GCM-SHA256
ssl_ciphers:客服端支持的所有安全套件
ssl_protocol:本次通讯选用的TLS版本 TLSv1.2
ssl_surves:客户端支持的椭圆曲线
证书:
ssl_client_raw_cert:原始客户端证书 内容
ssl_client_escaped_cert:返回客户端证书做urlencode编码后内容
ssl_client_cert:对客户端证书每一行内用前添加tab制表空白增强可读性
ssl_client_fingerprint:客户端证书的SHA1指纹
对上游使用SSL连接 + proxy
创建证书命令示例
创建根证书:
创建CA私钥
openssl genrsa -out ca.key 2048
制作公钥
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
签发证书:
创建私钥:
openssl genrsa -out a.pem 1024
openssl rsa-in a.pem -out a.key
生成签发请求:
openssl req -new -key a.pem -out a.csr
使用CA证书进行签发:
openssl x509 -req -sha256 -in a.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -out a.crt
验证签发证书是否正确:
openssl verify -CAfile ca.crt a.crt
浏览器过去缓存是否有效
expires 指令
Syntax:expire [modified] time;
expire epoch|max|off;
Default:expire off; #不添加或者修改Expires和Cache-Control字段
Context:http,server,location,if in location
max:永久有效
epoch:cache-control: no-cache
time:设置具体时间 可以携带单位
一天内具体时刻可以加@ eg:下午六点半: @18h30m
缓存的基本用法
nginx 缓存:定义存放缓存的载体
Syntax :proxy_cache zone|off; 共享内存
Default:proxy_cache off;
Context:http,server,locatoion
Syntax:proxy_cache_path path[levels=levels][use_temp_path=on|off] 位置
Context:http
proxy_cache_path 指令:
path:定义缓存文件存放的位置
levels:定义缓存路径的目录层级,最多三级每层目录长度为1或者2字节
use_temp_path:on 使用proxy_temp_path 定义的临时目录,off 直接使用path路径存放临时文件
keys_zone:
name:共享内存名字由proxy_cache 指令使用
size:是共享内存大小 1MB大约可以存放800 个key
inactive:在inactive时间内没有访问的缓存会被淘汰掉 默认10分钟
max_size:设置最大的缓存文件大小,超出后由cache manager 进程按LRU链表淘汰
manager_files:cache manager进程1次淘汰过程中 淘汰文件的最大文件数 默认100
manager_sleep:执行一次淘汰循环后cache manager进程的休眠时间 默认200毫秒
manager_threshold:执行一次淘汰循环最大耗时默认400毫秒
loader_files:cache loader进程载入磁盘中缓存文件至共享内存,每批最多处理文件数默认 100
loader_sleep:执行一次缓存文件到共享内存后 进程休眠时间 默认200毫米
loader_threshold:每批载入缓存文件至共享内存最大耗时 默认50毫秒
缓存的关键字:
Syntax: proxy_cach_key string;
Default: proxy_cache_key $scheme$proxy_host$request_uri;
Context: http,server,location
重要:缓存什么样的响应:
Syntax: proxy_cach_valid [code ...] time; code选填 time 必填
Default: -----
Context: http,server,location
对不同的响应码缓存不等的时长
eg:code 404 5m;
只标识时间:
仅对以下响应码缓存: 200,301,302
通过响应头部控制缓存时长:
X-Accel-Expire 单位秒
为0表示禁止nginx缓存内容
通过@设置缓存到一天中的某个时刻
响应头若含有Set-Cookie则不缓存
响应头含有Vary:*则不缓存
那些内容不缓存:
参数为真时 响应不存入缓存
Syntax: proxy_no_cache string ...;
Default: -----
Context: http,server,location
参数为真时 不使用缓存内容
Syntax: proxy_no_bypass string ...;
Default: -----
Context: http,server,location
变更head方法
Syntax: proxy_cache_convert_head on|off;
Default: proxy_cache_convert_head on; eg:head 转 get
Context: http,server,location
upstream_cache_status 变量
upstream_cache_status
MISS:未命中缓存
HIT:命中缓存
EXPIRED:缓存过期
STALE:命中陈旧缓存
UPDATING:内容陈旧 但正在更新
REVALIDATED:nginx 验证了陈旧的内容依然有效
BYPASS:响应是原始服务器获得的
eg:
server{
server_name cache.taohui.tech;
root html/;
location /{
#expries @20h30m;
proxy_cache two;
proxy_cache_valid 200 1m;
add_header X-Cache-Status $upstream_cache_status;
#proxy_cache_use_stale error timeout updating;
#proxy_cache_revalidate on;
#proxy_cache_background_update on;
#proxy_hide_header Set-Cookie;
#proxy_ignore_headers Set-Cookie;
#proxy_force_ranges on;
proxy_pass http://localhost:8012;
}
}
对客户端请求的缓存处理流程
[外链图片转存失败(img-Ji7TdD3L-1564906359010)(./img/cache_proxy_send.png)]
接收上游响应的缓存处理
X-Accel-Expires 头部
Syntax: X-Accel-Expires[offseconds]
Default: X-Accel-Expires off;
0表示不缓存当前响应
@表示缓存到当天的某个时间
Vary 头部:
Set-Cookie 头部
[外链图片转存失败(img-YbK3EYQ6-1564906359012)(./img/recive_proxy_cache.png)]
如何减轻缓存失效时上游服务器的压力
1,合并回源请求 - 减轻峰值流量下的压力:
Syntax: proxy_cache_lock on|off;
Default: proxy_cache_lock off;
Context:http,server,location
同一时间 仅1个请求发向上游,其他请求等待第一个响应返回或者超时后使用缓存响应客户端
Syntax: proxy_cache_lock_timeout time;
Default: proxy_cache_lock_timeout 5s;
Context:http,server,location
等待第一个请求返回响应的最大时间,到达后直接向上游发送请求,但不缓存响应
Syntax: proxy_cache_lock_age time;
Default: proxy_cache_lock_age 5s;
Context:http,server,location
上一个请求返回响应的超时时间到达后再放行一个请求发向上游
2,减少回源请求-使用stale陈旧的缓存
Syntax: proxy_cache_use_stale error|timeout|invalid_header|
updating|http_500|http_502|http_503|http_504|http_403|http_404|http_429|off ...;
Default:proxy_cache_use_stale off;
Context:http,server,location
Syntax: proxy_cache_background_update on|off;
Defalut:proxy_cache_background_update off;
Context:http,server,location;
及时清除缓存
及时清除缓存
模块
第三方模块 ngx_cache_purge: https://github.com/FRiCKLE/ngx_cache_purge
使用--add-module=指令添加模块到nginx
功能:
接收到指定http请求后立刻清楚缓存
syntax: proxy_cache_purge on|off|<mtthod>
defalut: none
context:http,server,location
syntax: proxy_cache_purge zone_name key
defalut: none
context:location
uwsgi,fastcgi,scgi 指令的对照表
memcached 反向代理
功能:
将http请求转换为memcached协议中的get请求转发请求到上游的memcached服务器
get 命令:get <key>*\r\n
控制命令:<command name><key><flags><exptime><bytes>[noreply]\r\n
通过设置memcached_key 变量构造key键
webscoket反向代理
功能:
由ngx_http_proxy_module 模块实现
配置:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
抓包:
客户端:
HTTP/1.1
Connection:keep-alive,Upgrade
Upgrade:websocket
服务器:
HTTP/1.1 101 Web Socket Protocol Handshake
透传ip的3中方案
1,修改Ip报文
步骤:修改ip报文中的源地址
修改路由规则
方案:ip地址透传:经由nginx转发上游返回的报文给客户端(tcp/udp)
DSR:上游直接发生报文给客户端(仅udp)
性能优化篇
优化方法论:
**从软件层面提升硬件使用率:**
增大cpu的利用率
增大内存的利用率
增大磁盘io的利用率
增大网络宽带利用率
**提升硬件规格:**
网卡:万兆网卡 例如 10G 25G 40G
磁盘:固态硬盘,关注iops和bps
CPU:更快的主频,更多的核心,更大的缓存,更优的架构
内存:更快的访问速度
**超出硬件性能上限后使用DNS**
如何高效使用CPU
如何增大nginx使用cpu的有效时间
能够使用全部CPU资源
master-worker多进程架构
worker进程数量应大于等于Cpu核数
Nginx进程间不做无用功浪费CPU资源
worker进程不应再繁忙时,主动让出CPU
work进程数量不应由于争抢造成资源消耗
worker进程数量应等于cpu核数
worker进程不应调用一些API导致主动让出CPU(注意openresty)
拒绝类似的第三方模块
不被其他进程争抢资源
提升优先占用cpu更长的时间
减少操作系统上资源消耗非nginx进程
确保进程运行状态:
R 运行
S 中断
D 不可中断
Z 僵死
T 停止
减少进程间切换
延迟处理新连接
如何查看上下文切换的次数:
vmstat
Dstat
pidstat -w
设置worker 进程的静态优先级
syntax: worker_priority number
Default: worker_priority 0; # -20
Context: main
多核间的负载均衡
worker 进程间负载均衡
多队列网卡对多核cpu的优化
提升cpu缓存命中率:worker_cpu_affinity
Worker_cpu_affinity auto [coumask]
cat /sys/devices/system/cpu/cpu1/cache/index0/size
控制TCP三次握手
主动建立连接时应用层超时时间
syntax:proxy_connect_timeout time
Default:proxy_connect_timeout 60
Context:http,server,location
客户端提前关闭连接
lua_check_click_abort: 是否检查客户端关闭连接,且发现该事件后回调ngx.on_abort 指定的lua方法 默认关闭
ok,err = ngx.on_abort(callback) 当下游客户端过早关闭连接时,调用callback函数
Openresty SDK
获取请求头部的sdk
ngx.req.get_headers 以lua table的方式返回 最多返回100个头部
ngx.req.raw_header(no_request_line) 返回网络中接收到的原始http请求头
ngx.req.get_method
ngx.req.http_version
ngx.req.get_uri_args 以table方式返回 最多100个
ngx.arg[index]
获取请求包体SDK
指令:lua_need_request_body 强制要求lua代码执行前读取到完整的请求包体 默认关闭
ngx.req.get_post_args
ngx.req.read_body 当前请求没有包体或者包体已经读取了该方法立即返回 读取到包体 需要通过get_body_data 或者get_body_file 获取到内容
ngx.req.get_body_data
ngx.req.get_body_file
HTTP 请求方法名常量
ngx.HTTP_GET
Nix.HTTP_HEAD
Nix.HTTP_POST
…
修改请求信息SDK
针对子请求
ngx.req.set_method(method_id)
ngx.req.set_header(name,value) #之后通过 ngx.location.capture 和 ngx.location.capture_multi 发起的所有子请求都将继承新的头信息
ngx.req.clear_header(header_name) 删除头部
ngx.req.set_uri_args(args) 修改url 请求参数
修改请求包体的SDK
ngx.req.discard_body :丢弃接收到的请求包体
ngx.req.set_body_data 设置当前请求的请求包体内容。必须先读取完请求包体
ngx.req.set_body_file(filename,autoclean) 以磁盘文件来设置请求中的包体。必须先读取完请求包体。若autoclean值为true 则请求执行完后会删除
ngx.req.init_body(buffer_size) 初始化一个缓冲区,用于append_body和finish_body使用 创建用户请求包体,若buffer_size 没有传递,则使用nginx官方提供的client_body_buffer_size 值作为缓存区大小
ngx.req.append_body 向缓冲区写入请求包体内容,若添加的包体大小超出缓冲区,则将包体写入到临时文件中
ngx.req.finish_body 使用limit_body 和append_body添加完包体后,必须调用该方法
修改URL并跳转到SDK
ngx.req.is_internal:以布尔值返回当前请求是否为内部跳转过来的请求
ngx.req.set_uri(uri,jump) 修改当前请求url jump默认为false,若为true 并且在rewrite_by_lua*上下文中时类似rewrite指令进行location 跳转
ngx.exec 更改当前请求的uri 执行location内部跳转
HTTP 响应状态码常量
[外链图片转存失败(img-gkFfcM7R-1564906359014)(./img/reponse_code.png)]
读取,修改响应值
ngx.status 读取响应值,在响应头部发出以前可以修改响应值
修改发送响应的头部
ngx.header.HEADER 或者ngx.header[‘HEADER’]
增删改查 当前请求中的响应头部。注意lua_transform_underscores_in_response_headers指令是默认开启中的
其值为nil时 或者值为{} 表示删除该头部。当头部不存在时,则返回nil
指令:lua_transform_underscores_in_response_headers 是否自动(不包括值)中的’_'更换为 ‘-’ 默认开启
发送响应的sdk
ngx.redirect(uri,status) 向客户端返回重定向请求,status 默认302
ngx.send_headers 显示的向客户端发送响应头部,返回1 表示成功,返回nil表示失败
ngx.headers_sent 返回布尔值标识响应头部是否已经发送
请求响应类指令
lua_use_default_type:指定是否使用nginx框架提供default_type中定义的Content-Type类型,作为lua代码的响应类型默认开启
lua_http10_buffering 控制对于lua生成的http/1.0协议的响应是否先缓存再发送,当lua代码中显示设定了
Content-Length 头部时会自动关闭该缓存功能
发送响应的sdk
ngx.print 写入响应包体向下游客户端发送响应。该方法是异步的 在其后使用ngx.flush(true)可以同步发送
ngx.say 类似 ngx.print
ngx.flush(wait) wait默认false是异步的 true是同步的
ngx.exit(status) 当 status >=200 exit 会终止当前请求lua代码
当status == 0 exit 会终止当前阶段的执行继续后续的执行
ngx.eof 显示的指定响应内容结束。对于http/1.1 RFC2616中定义的chunked编码来说 eof会指定nginx发出 last chunk 来指明响应结果
工具类型的SDK
ngxin 函数的返回值常量
ngx.OK(0)
ngx.ERROR(-1)
ngx.AGAIN(-2)
ngx.DONE(-4)
ngx.DECLINED(-5)
取worker进程信息sdk
ngx.worker.exiting 以布尔返回 当前nginx worker 进程是否正在退出
ngx.worker.pid 返回worker进程id 由于不依赖变量比 ngx.var.pid 应该范围更广
ngx.worker.count 返回ngxin.conf 中配置的 nginx worker进程数量
ngx.worker.id 返回所属worker进程序号。0- n
日志级别常量
ngx.STDERR
ngx.EMERG
ngx.ALERT
ngx.CRIT
ngx.ERR
ngx.WARN
ngx.NOTICE
ngx.DEBUG
写入日志的SDK
print(…): 使用notice级别将日志写入到error.log文件中
ngx.log(log_level,…):以指定日志级别写入日志
获取error.log 日志内容的SDK
lua_capture_error_log
get_logs:
local errlog = require "nix.errlog"
local res = errlog.get_logs(10)
建立上下文信息的SDK
ngx.ctx
编解码
ngx.escape_uri :将字符串进行url编码
ngx.unescape_uri:将url编码的字符串解码
ngx.encode_args 将lua table以url格式中参数的形式编码
ngx.decode_args 将url格式的参数解码为lua table
ngx.encode_base64 :
ngx.decode_base64
ngx.quote_sql_str:将sql语句字符串转为mysql格式
哈希编码sdk
ngx.crc32_short(str) 计算字符串的CRC32编码 该方法适用于较短字符串 小于30~60字节
ngx.crc32_long 计算字符串的CRC32编码 该方法适用于较长字符串 大于30~60字节
ngx.hmac_sha1(secret_key,str) 返回字符串sha1摘要
ngx.md5(str) 返回字符串md5 摘要
ngx.md5_bin 返回字符串二进制格式的md5摘要
ngx.sha1_bin 返回字符串二进制sha1摘要
正则表达式SDK
ngx.re.match :进行正则表达式匹配 返回匹配中的字符串
ngx.re.find 进行正则表达式匹配,返回匹配中的字符串中的第1个,最后1个字符的索引。未创建字符串
ngx.re.gmatch 进行正则表达式匹配,但返回的是迭代器,需要通过迭代器取出所有匹配的字符串
ngx.re.sub 进行正则表达式匹配并将匹配中的字符串替换为新的字符串
ngx.re.gsub 进行正则表达式匹配并以全局方式将匹配中的字符串替换为新的字符串
相关指令
lua_regex_match_limit 限制ngx.re正则表达式类api匹配中的最大数组 默认值0表示使用PCRE库编译时设定的最大值
lua_regex_cache_max_entries:对于以下api:ngx.re.match ngx.re.gmatch ngx.re.sub ngx.re.gsub 所生成的正则表达式缓存至内存中,该指令定义了缓存的最大条目数。当达到最大数目后新的正则表达式不会被缓存。默认值1024
同步且非阻塞的底层SDK cosocket
Cosocket 相关指令
lua_socket_connect_timeout
设置SDK中的cosocket的连接超时时间,默认60 秒
lua_socket_send_timeout
设置sdk中cosocket的两次写操作的超时时间默认60秒
lua_socket_read_timeout
设置sdk中cosocket的两次读操作的超时时间默认60秒
lua_socket_buffer_size
设置sdk中cosocket中的读缓存区大小 默认为4k/8k
lua_socket_pool_size
设置sdk中cosocket连接池中最大连接数(每个worker进程)超出后使用LRU算法关闭,淘汰连接
lua_socket_keepalive_timeout
设置sdk中cosocket连接的最大空闲时间 默认60秒
lua_socket_log_errors
控制cosocket出错时是否记录错误日志至nginx的error.log
Cosocket 的SDK
ngx.socket.tcp
创建TCP 协议的cosocket 对象 当没有显示关闭cosocket 或者把它放入连接池 则当前阶段结束时会自动关闭对象
**connect**:连接上游服务器(ip端口 或者 unix地址)
**sslhandshake**:在已经建立好的tcp连接上进行ssl握手
**send** 将消息发送到对端。在send方法返回时,它已经将用户态的字节流拷贝到内核socket缓存区中
**receive(size) 或者recevie(pattern)**
自cosocket中接收size个字节的消息未接收足时则不返回,直到满足超时条件
pattern为*a 表示一直接收消息 直到连接关闭
pattern为*l 表示接收一整行消息 直到收到LF为止 没有参数时等同 于 *l
**receive any(max):**
自cosocket中接收到任意数据即返回最多接收max字节(若内核读缓冲区数据超过max,仍只返回max)
**receive until(pattern, options?)**
返回用于读取消息的lua迭代器方法,直到pattern匹配到接收到字节流
**close**:关闭连接
**settimeout(microseconds)** 设置超时时间 改时间应用在connect send receive 方法上 单位为毫秒
**settimeouts(connect_timeout,send_timeout,read_timeout)**:分别设置超时时间
**setkeepalive(timeout,size):**
关闭cosocket对象 并将当前TCP 连接放入keepalive连接池 最长空闲时间为timeout 单位毫秒 设置0表示永不过期,若忽略该参数则使用lua_socket_keepalive_timeout 指令的值
size 指定了连接池中当前上游服务的最大连接数,忽略该参数时使用lua_socket_pool_size 指令的值。达到size 上限后 (每个worker进程内)按LRU关闭较老的空闲连接
若内核缓存区仍有数据,则该方法会返回错误信息 connection in dubious state
**getreusedtimes:** 返回当前tcp连接复用了多少次
获取客户端的TCP cosocket
ngx.req.socket 将与下游客户端的socket连接 以读取cosocket对象的形式封装后返回 仅支持receive 和 receiveuntil
ssL 相关指令
lua_ssl_ciphers: 定义socket 中所有使用ssl 安全套件
lua_ssl_crl 指定ssl 中CRL 吊销证书列表
lua_ssl_protocols 指定cosocket中SSL 协议版本号
Lua_ssl_trusted_certificate 指定验证cosocket 中对端证书的可信证书文件
lua_ssl_verify_depth 指定证书cosocket中对端证书的证书链深度
UDP cosocket
ngx.socket.udp 创建udp 协议的cosocket 对象支持5个方法
setpeername :设置对端节点的地址
send :将消息通过udp协议发送到对端
receive(size?) 最多接收size个字节 默认为8192 在规定时间内未接收到则返回nil
close:关闭socket对象
settimeout(microseconds):设置receive读取超时时间 单位为毫秒
基于协程并非sdk
协程SDK
co = ngx.thread.spawn(func,arg1,arg2,…)
生成轻量级的线程并有nginx及 ngx_http_lua_module模块立刻调度执行
ok,res1,res2,… = ngx.thread.wait(thread1,thread2)
等待1个或者多个轻量级线程执行完毕后返回 ok 表示所有线程是否正常结束
res 返回值 按照次序是每个func 的返回结果
Ok, err = ngx.thread.kill(thread)
杀死正在运行的轻量级线程 仅父线程可以执行
同步非阻塞的sleep方法 及lua-resty-lock 锁
ngx.sleep(seconds)
对当前lua代码块休眠一段时间 seconds 可以精确到0.001 毫米 它通过nginx中的定时器实现不会阻塞nginx运行
Lua-resty-lock 模块
**obj,err = lock:new(dict_name,opts)**
自共享内存字典中新建锁对象
opts 选项包括:
exptime:设置锁的过期时间 到期后自动释放 默认30秒
timeout:lock 方法的最大等待时间 默认5秒
step 锁是由ngx.sleep 方法实现的step定义了第一次slepp的时间默认0.001秒
ratio:定义每轮sleep 时间的增长率 默认为2
max_step 定义了最大sleep时间默认0.5 秒
**elapsed, err = ob:lock(key)**
获取关于key的锁 返回等待的时间 如果未获取到锁则返回nil
**ok,err = obj:unlock()**
释放锁
**ok,err = obj:expire(timeout)**
重置new方法传入的timeout选项
定时器及时间相关的SDK
定时器SDK
**hdl,err = ngx.timer.at(delay, callback,arg1,arg2)**
新增定时器,定时器触发后执行callback arg为参数
delay 延迟时间 单位秒 精确到毫秒 0 表示立刻在后台的轻量线程中执行callback
callback 的第一个参数时布尔值的premature 他表示定时器是否过早被触发,例如ngxin worker 进程在关闭不会等待定时器触发,而是直接调用callback 此时premature值为true 接下来arg参数
callback由于没有当前请求故不能使用子请求类API 也不能获取当前请求参数
**ngx.timer.every** :类似at 相当于daley 秒就调用一次callback
**ngx.timer.running_count**:当前正在执行的定时器数量
**ngx.timer.pending_count**:等待执行的定时器数量
定时器指令
lua_max_pending_timers 指定API ngx.timer.at 中等待执行的定时器的最大数目,达到后新增的定时器直接返回nil 默认值1024
lua_max_running_times 指定API ngx.timer.at 中正在执行回调方法的定时器最大数目,达到后新增的定时器的回调方法不会被执行 在error.log 中出现lua_max_running_timers are not enough 的日志 默认值256
获取请求的处理时间SDK
ngx.req.start_time 返回开始处理当前请求经过的时间
时间类SDK
ngx.tody :从nginx 中取出当前时间的日期 格式为yyyy-mm-dd
ngx.time 获取epoch时间(1970 年1月1日0点) 到现在的时间
ngx.now 获取epoch时间(1970 年1月1日0点)到现在的秒数 精确到0.001(毫秒)
ngx.update_time 更新nginx 缓存时间
ngx.localtime 返回本地时区下的当前时间 格式 yyyy-mm-dd hh:mm:ss
ngx.utctime 返回UTC 当前时间 格式 yyyy-mm-dd hh:mm:ss
ngx.cookie_time(sec) 将epoch时间秒数转换为cookie中可以识别的时间格式例如 ngx.cookie_time(1547466623) 返回Mon,14-jan-19 11:50:23 GMT
ngx.parse_http_time(str) 将http头部标准时间格式的字符串转换为epoch 时间秒数
share.DICT 基于共享内存的字典
指令
**lua_shared_dict** 基于nginx的共享内存(使用Slab管理器)实现的跨worker进程字典容器,支持LRU淘汰功能。由于reload不会清除共享内存中的内容。故reload后shared_dict 值仍在
SDK
共享内存的所有方法都是原子的线程安全的
**value,flags = ngx.shared.DICT:get(key)**
返回字典中未过期key值,返回值如下:
value 返回关键字的值,如果不存在或者过期,则返回nil
flags 若set设置时flag为0则返回nil,否则返回设置时的flags
**value,flags,stale=ngx.shared.DICT:get_stale(key)**
返回字典中的key的值(无论是否过期)返回值如下
value:返回关键字的值,如果不存在则返回nil 过期扔返回其值
flags:若set设置时flags为0则返回nil 否则返回设置时的flags
stale:如果关键字过期 则stale为true,否则为false
**success, err,forcible = ngx.shared:DICT:set(key, value,exptime?,flags?)**
向字典中设置键值对,当内存不足时会按过期时间淘汰老的元素。当连续淘汰30个 元素后仍然无法分配出足够内存则返回失败
key, value 键值对
exptime 过期时间单位秒 可精确到毫秒 默认为0表示永远不过期(会被LRU淘汰)
flags 用于区别不同的键默认为0 以无符号32位整型存储
success 以布尔值返回成功或者失败
err:错误信息
forcible:若是由于LRU强制淘汰较老的键才为本次设置成功分配内存则为true
ok,err = ngx.shared.DICT:safe_set(key, value,exptime?,flags?)
类似set方法 但不会在内存不足时根据LRU强行淘汰未过期的键。内存不足时返回nil 和 no memory
success,err,forcible = ngx.shared.DICT:add(key, value,exptime?,flags?)
类似set 但仅向字典中添加新的键值对,若key已经存在则返回false,exists false
ok,err = ngx.shared.DICT:safe_add(key, value,exptime?,flags?)
类似add 但不会在内存不足时根据LRU强行淘汰未过期的键。内存不足时返回nil 和 no memory
success,err,forcible=ngx.shared.DICT:replace(key, value,exptime,flags)
类似set 但仅能覆盖字典中已经存在的键值,若key不存在则返回false,not found false
ngx.shared.DICT:delete(key)
从字典中删除key及对应的值,等价于set(key,nil)
newval,err,forcible = ngx.shared.DICT:incr(key, value,init?,init_ttl?)
如果字典中key对应的值存在,则将其加上value并返回相加后的值newval.若key在字典中对应的值不是数字,则返回nil not a number false
init 当key 不存在时 使用 init+value 4作为该key的值添加到字典
init_ttl 当init生效时 指定key到过期时间
forcible 当init未设置时 由于不会添加新键值,故其值始终为nil
length,err = ngx.shared.DICT:lpush(key,value)
设置key对应的值为队列,从队列的头部插入元素value。返回length为插入后队列中的所有元素个数。它不会引起LRU淘汰
如果key不存在,则插入一个仅含有元素value的队列
如果key存在 但其值不是一个队列,则返回nil value not list
length,err = ngx.shared.DICT:rpush(key,value)
类似lpush 但却是从队列的尾部插入元素
val,err = ngx.shared.DICT:lpop(key)
从key对应的队列头部取出一个元素
val,err = ngx.shared.DICT:rpop(key)
从key对应的队列尾部取出一个元素
len,err = ngx.shared.DICT:llen(key)
查询key对应的队列的元素个数。若key对应的值不是队列 则返回nil value not a list
ttl,err = ngx.shared.DICT:ttl(key)
返回key对应的元素距离过期时间的剩余秒数,如果是永不过期类型则返回ttl 为0
success,err = ngx.shared.DICT:expire(key,exptime)
设置key对应元素的过期时间,如果key对应元素不存在则返回nil ,exptime为0 永不过期
ngx.shared.DICT:flush_all()
标记字典中所有元素皆为过期
flushed = ngx.shared.DICT:flush_expired(max_count)
将字典中最多max_count(0 表示不做限制)个过期元素释放内存
keys = ngx.shared.DICT:get_keys(max_count?)
将字典中最多max_count 个键取出 max_count 默认值为1024 设置0表示取出所有键
capacity_bytes = ngx.shared.DICT:capacity()
以字节数返回共享内存大小,须 require "resty.core.shdict"
free_page_bytes = ngx.shared.DICT:free_space()
以字节数返回共享内存slab管理器中空闲页的大小须 require "resty.core.shdict"
子请求的使用方法
location.capture 子请求
ngx.location.capture(uri,option)
生成nginx子请求,且接下来的Lua代码会以同步的方式等待子请求返回后再执行。因此该方法执行前应读取完整的请求包体,防止读取请求包体超时发生。子请求继承当前请求所有头部
返回值依赖子请求的响应,且响应都会放置在内存中,故子请求返回的响应不应过大,对于大文件响应可以使用cosocket
返回值为lua table 包括4个成员:
status 响应码
header 以lua table方式保存的响应头部。如果某个头部值属于多值头部,则其值返回lua table并以数组的方式按出现次序返回成员
body 响应包体,可能被拦截
truncated 如果响应包体被截断则为true
option选项
method 请求方法名 须传递http请求方法名常量默认为get方法
body 设置请求包体 仅接受string类型的参数
args 设置uri 参数
ctx 以lua table方式设置子请求api ngx.ctx 中可以使用的上下文字典
vars 以lua table方式设置子请求中的变量
copy_all_vars 是否拷贝当前请求中的的变量至子请求中
share_all_vars 是否共享当前请求中的变量至子请求中,修改子请求影响当前请求
always_forward_body:若body选项未设置其值为真时将当前请求包体作为子请求包体
ngx.location.capture_multi({uri,option},{uri,option},…)
支持并发发起多个子请求,返回值代表的子请求响应与参数中uri顺序相同
仅所有子请求皆获得响应后,当前方法所属的代码才继续执行
ngx.is_subrequest
以布尔值返回当前请求是否为子请求
WAF 防火墙
Nginx 密码学
1,对称加密 处理大数据
明文:原始数据
秘钥:cipher [ˈsaɪfɚ]
密文:加密后的数据
128位的AES 需要16字节的输入数据
散列函数:将任意长度的输入转化为定长输出
SHA1 输出160位
SHA256
用于验证数据的完整性
2,非对称加密 处理小数据
RSA
GCC 相关
gcc -c -Idir main.c -o main.o
gcc -c -Idir param.c -o param.o
gcc main.o param.o -o main
make 变量
mian:mian.o param.o
gcc $^ -o $@
main.o:main.c
gcc -Idir -c $< -o $@
param.o:param.c
gcc -Idir -c $< -o $@
说明:
$^ :对应所用依赖对象
$< :第一个依赖对象
$@ : 对应目标
%.o:%.c
gcc -Idir -c $< -o $@
%通配符
gcc -MM -Idir main.c > .main_depend
.main_depend 内容为: main.o:main.c dir/param.h
tcpdump 抓包
tcpdump -i lo port 8012 -A -s0
tcpdump -i eth0 port 80 and host 174.129.224.73 -A -s0
wrk参数说明
参数:
- -t 需要模拟的线程数
- -c 需要模拟的连接数
- –timeout 超时的时间
- -d 测试的持续时间
结果:
- Latency:响应时间
- Req/Sec:每个线程每秒钟的完成的请求数
- Avg:平均
- Max:最大
- Stdev:标准差
- +/- Stdev: 正负一个标准差占比(结果的离散程度,越大越不稳定)
- Requests/sec:QPS(每秒请求数)
- Transfer/sec:每秒传输数量
标准差如果太大说明样本本身离散程度比较高. 有可能系统性能波动很大.
wrk lua
wrk 在执行http 请求的时候 调用lua 分为3个阶段
setup:(每个线程执行一次)
running:
init(args) 每个线程执行一次
delay(),request() 每次请求都调用
done:
summary,latency,requests :整个过程执行一次
wrk的全局属性
wrk = {
scheme = "http",
host = "localhost",
port = nil,
method = "GET",
path = "/",
headers = {},
body = nil,
thread = <userdata>,
}
wrk的全局方法
-- 生成整个request的string,例如:返回
-- GET / HTTP/1.1
-- Host: tool.lu
function wrk.format(method, path, headers, body)
-- 获取域名的IP和端口,返回table,例如:返回 `{127.0.0.1:80}`
function wrk.lookup(host, service)
-- 判断addr是否能连接,例如:`127.0.0.1:80`,返回 true 或 false
function wrk.connect(addr)
Setup阶段:setup是在线程创建之后,启动之前。
function setup(thread)
-- thread提供了1个属性,3个方法
-- thread.addr 设置请求需要打到的ip
-- thread:get(name) 获取线程全局变量
-- thread:set(name, value) 设置线程全局变量
-- thread:stop() 终止线程
Running阶段:
function init(args)
-- 每个线程仅调用1次,args 用于获取命令行中传入的参数, 例如 --env=pre
function delay()
-- 每个线程调用多次,发送下一个请求之前的延迟, 单位为ms
function request()
-- 每个线程调用多次,返回http请求
function response(status, headers, body)
-- 每个线程调用多次,返回http响应
Done阶段:可以用于自定义结果报表,整个过程中只执行一次
function done(summary, latency, requests)
latency.min -- minimum value seen
latency.max -- maximum value seen
latency.mean -- average value seen
latency.stdev -- standard deviation
latency:percentile(99.0) -- 99th percentile value
latency(i) -- raw value and count
summary = {
duration = N, -- run duration in microseconds
requests = N, -- total completed requests
bytes = N, -- total bytes received
errors = {
connect = N, -- total socket connection errors
read = N, -- total socket read errors
write = N, -- total socket write errors
status = N, -- total HTTP status codes > 399
timeout = N -- total request timeouts
}
}
eg: 表单提交
wrk.method = "POST"
wrk.body = "" -- 直接写死,如果不需要请求数据的差异化
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
-- 如果要实现每次都不一样的表单内容
local queries = {
"language=php",
"language=java",
"language=lua"
}
local i = 0
request = function()
local body = wrk.format(nil, nil, nil, queries[i % #queries + 1])
i = i + 1
return body
end
常见问题
nginx 分发请求报错upstream sent invalid chunked response while reading upstream问题解决
location中添加
proxy_http_version 1.1;
proxy_set_header Connection "";
Lua-Nginx 笔记
1,default_type ‘text/plain’ 表示传输文本格式
2,content_by_lua
3,ngx 对象:
a)ngx.print()
b) ngx.var['arg_a' ] 或者 ngx.var.arg_a 来访问
c)ngx.location.capture('/domain') 访问内部的域名
i 返回对象包含:status. body. header 对象
ii 例如 res = ngx.location.capture('/domain'); res.status res.body
4 lua_package_path 和 lua_package_cpath 指定lua 文件时的路径
5 echo_location 发送子请求到指定的location
6 init_by_lua 初始化载入模块 类似类的包机制
a)init_by_lua 'cjson = require "cjson"';
b) ngx.say(cjson.encode({dog=5,cat=6}))
c) cjson 最好安装 LuaRocks 工具
d)http://luarocks.org/releases/luarocks-2.0.12.tar.gz
7 set_by_lua 和 set_by_lua_file 执行 lua代码赋值给变量
8 ngx.var.a 和 ngx.var.arg_a 分别用于获取局部变量$a 和传递的参数变量 a
9 rewrite ^/domain redirect 跳转到指定的子站点
10 ngx.redirect("/domain",301) 在 …by_lua 中 执行跳转的方式 301为跳转方法可以省略
11 ngx.exit
12 ngx.var.remote_addr 获取本地服务器的ip
13 access_by_lua 和 access_by_lua_file 用于校验是否合法
14 ngx.exec(’/domain’,‘a=3&b=c’) 执行其他的domain
15 res1,res2 …=ngx.location.capture_multi({ {uri,options?},{uri,options?},… })
16 ngx.time()
17 ngx.now()
18 ngx.utctime 返回当前时间的UTC 格式 2013-07-13 13:20:28
19 ngx.cookie_time 设置cookie的失效时间
20,ngx.http_time(时间戳) 设置header的时间戳
21 ngx.req.start_time() 获取开始时间 计算程序的运行时间 ngx.now() - ngx.req.start_time()
22 ngx.md5(str)
23 ngx.req.http_version() 获取http的版本
24 ngx.print(ngx.req.raw_header(true)) 获取 http 头信息
25 ngx.req.get_method 获取method 方式
26 ngx.req.get_post_args() 获取post 的数据 ngx.req.get_uri_args() 获取GET 方式的数据
注意 ngx.req.get_post_args() 在之前需要 ngx.req.read_body() 不然会报错
27 ngx.ctx 可以在当前的通信中跨lua段处理,变量可以不销毁,可以全部使用
28 nxg.req.get_method() 和 ngx.req.set_method()
29 ngx.req.set_uri_args(“a=3”) 设置url 请求的参数 两种方式
a)ngx.req.set_uri_args("a=3&b=hello")
b) ngx.req.set_uri_args({a = 3,b = "hello word"})
30 ngx.req.get_headers() 获取当前请求的header信息 返回一个数组
31 ngx.req.set_header(“Content-Type”,“text/css”) 指定header头的格式和内容
a) ngx.req.set_header("Foo",{"a","abc"}) 设置一维数组
32 ngx.req.clear_header(header_name) 清除对应的头信息
33 ngx.req.read_body() 读取nginx不阻塞的时候 body的内容
34 ngx.req.init_body(buffer_size) 设置开始请求时的body缓存大小
ngx.req.init_body(128*1024) --buffer is 128KB
ngx.req.append_body(chunk) --cach chunk can be 4KB
ngx.req.finish_body()
35 ngx.req.append_body(str) 把内容拼凑到body中
36 ngx.escape_uri 和 ngx.unescape_uri 转义uri 和反转义uri
37 ngx.encode_args(table) 转义
a) ngx.encode_args({foo=3,["br"] == "hello word"})
38 ngx.decode_args(str, max_args)
40 ngx.encode_base64 加密 和 ngx.decode_base64 解密
41 ngx.hmac_sha1(secret_key,str) 使用秘钥加密
local key = "thisisverysecretstuff"
local src = "some string we wang to sign"
local digest = ngx.hmac_sha1(key,src)2
42 ngx.today() 返回当前时间。年 月 日
43 ngx.localtime 返回当前时间 年 月 日 时 分 秒
44 ngx.is_subrequest 判断当前请求是否为子请求
45 ngx.re.match 正则匹配