上篇文章介绍了nginx.conf的参数和变量及实现根据给定的参数实现日志动态分发。但是如果参数不固定呢?比如上篇我固定参数arg2值为click,view,look,content四个,那如果生产系统中增加了一个类型play,search呢?难道每次增加一个参数都要Kill掉nginx然后修改配置文件增加if代码块吗?这显然是低效的。那如何实现这个功能呢?
答案:Lua可以帮到你
Lua简介
Lua 是一个简洁、轻量、可扩展的脚本语言,也是号称性能最高的脚本语言,用在很多需要性能的地方,比如:游戏脚本,nginx,wireshark的脚本,当你把他的源码下下来编译后,你会发现解释器居然不到200k,非常变态。。。很多应用程序使用Lua作为自己的嵌入式脚本语言,以此来实现可配置性、可扩展性。
Lua原生支持的数据类型非常之少,它只提供了nil、数字(缺省是双精度浮点数,可配置)、布尔量、字符串、表、子程序、协程(coroutine)以及用户自定义数据这8种。但是其处理表和字符串的效率非常之高,加上元表的支持,开发者可以高效的模拟出需要的复杂数据类型(比如集合、数组等)。Lua是一个动态弱类型语言,支持增量式垃圾收集策略。有内建的,与操作系统无关的协作式多线程(coroutine)支持。它还可以用于嵌入式硬件,不仅可以嵌入其他编程语言,而且可以嵌入微处理器中。
lua可以实现nginx.conf中复杂的逻辑处理,但是前提是nginx要安装lua-nginx-module插件,安装步骤参考https://blog.csdn.net/qq_25551295/article/details/51744815。
nginx.conf结合lua编程实现逻辑处理
由于本人也是因为业务的需求才开启了nginx+lua编程的学习,所以以下内容为本人精心挑选过网友分享的知识,感谢这些网友。
- lua的语法学习参考菜鸟教程:http://www.runoob.com/lua/lua-tutorial.html
- Nginx+Lua 开发入门:http://wiki.jikexueyuan.com/project/nginx-lua/introduction.html
- nginx + lua实现复杂的控制:https://blog.csdn.net/huangyimo/article/details/80791816
- ngx映射到lua模块函数变量一览:https://blog.csdn.net/xiejunna/article/details/53465202
- 通过nginx_lua实现根据请求参数分发道不同后端节点: https://blog.csdn.net/yevvzi/article/details/52593490
- ngx_lua常用变量参数:https://blog.csdn.net/xiejunna/article/details/53444616
nginx个性化需求
nginx有几个功能,最常用的就是做负载均衡服务器和web服务器。而且因为nginx可以支撑上万的并发量,所以非常适合作为互联网公司的埋点日志服务器。
需求:根据访问参数里面的两个参数appkey和ltype动态生成日志文件。日志文件命名规则为appkey的值_ltype的值.log。同时根据请求方式的不同,get方式访问的日志才有这种需求,而post方式访问的日志命名直接用old_post命名即可。
- 改造前的nginx.conf:
user nginx nginx;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 409600;
events {
use epoll;
multi_accept on;
worker_connections 409600;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
keepalive_timeout 60;
tcp_nodelay on;
charset utf-8;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location /statistics/EventAgent {
add_header Content-Type 'text/html; charset=utf-8';
default_type 'application/json';
return 200 '{"success":200}';
}
}
}
- 实现需求
- 时间格式需要更改为2018/12/13 16:38:15,而nginx.conf默认的时间格式为:14/Dec/2018:17:40:55 +0800 。
- 在nginx.conf中server{}上先根据fmt_localtime参数。
- 日志格式中[fmt_localtime
- 根据lua获取到nginx的所有参数将ngx.localtime()赋值给$fmt_localtime
map $host $fmt_localtime {
default '';
}
log_by_lua_block {
ngx.var.fmt_localtime = ngx.localtime();
}
- 根据访问参数里面的两个参数appkey和ltype动态生成日志文件。日志文件命名规则为appkey的值_ltype的值.log.
- 开启lua获取nginx的所有参数功能
- 在location代码块中嵌套rewrite_by_lua_block代码块;rewrite_by_lua执行内部URL重写或者外部重定向,默认执行在rewrite处理阶段的最后。
set $log_name '';
rewrite_by_lua_block {
local var = ngx.var
local request_method = ngx.var.request_method
if request_method == "GET" then
if(ngx.var.arg_appkey ~= nil and ngx.var.arg_ltype ~= nil) then
ngx.var.log_name = ngx.var.arg_appkey.."_"..ngx.var.arg_ltype
else
ngx.var.log_name='access'
end
elseif request_method == "POST" then
ngx.req.read_body()
ngx.var.log_name='old_post'
end
}
- 如果请求是post请求,需要打印出请求体。
实现这个功能需要在lua 的content代码块中写入ngx.req.read_body(),不然请求体为空。 - 如果嵌套了lua代码,返回值的时候不能用return 了,不然返回不了。需要嵌套content_by_lua代码块将返回值打印出来。
content_by_lua_block {
ngx.req.read_body()
ngx.say("{success:200}")
}
改造后的nginx.conf配置文件如下:
user nginx nginx;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 409600;
events {
use epoll;
multi_accept on;
worker_connections 409600;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
keepalive_timeout 60;
tcp_nodelay on;
charset utf-8;
log_format access_get '$fmt_localtime $server_addr $uri $args $remote_addr $status';
log_format access_post '$fmt_localtime $server_addr $uri $request_body $remote_addr $status';
map $host $fmt_localtime {
default '';
}
log_by_lua_block {
ngx.var.fmt_localtime = ngx.localtime();
}
lua_need_request_body on;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location /statistics/EventAgent {
add_header Content-Type 'text/html; charset=utf-8';
set $log_name '';
rewrite_by_lua_block {
local var = ngx.var
local request_method = ngx.var.request_method
if request_method == "GET" then
if(ngx.var.arg_appkey ~= nil and ngx.var.arg_ltype ~= nil) then
ngx.var.log_name = ngx.var.arg_appkey.."_"..ngx.var.arg_ltype
else
ngx.var.log_name='access'
end
elseif request_method == "POST" then
ngx.req.read_body()
ngx.var.log_name='old_post'
end
}
if ($request_method = "GET") {
access_log /data/log/nginx/$log_name.log access_get ;
}
if ($request_method = "POST") {
access_log /data/log/nginx/$log_name.log access_post ;
}
#return 200 '{"success":true,"status":1,"message":"成功"}';
default_type 'application/json';
content_by_lua_block {
ngx.req.read_body()
ngx.say("{success:200}")
}
}
location /statistics/log {
add_header Content-Type 'text/html; charset=utf-8';
access_log /data/log/nginx/h5.log access_get ;
default_type 'application/json';
return 200 '{"success":200}';
}
location /status {
vhost_traffic_status_display;
vhost_traffic_status_display_format html;
access_log off;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
vhost_traffic_status_zone shared:vhost_traffic_status:24m;
}
通过修改后的nginx.conf配置,nginx启动之后日志分类很完美。
分类日志.png
lua的功能还有很多,执行速度也很快,非常适合在其他组件中作为嵌套代码执行,继续学习分享中...
网友评论