上篇文章介绍了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编程的学习,所以以下内容为本人精心挑选过网友分享的知识,感谢这些网友。

  1. lua的语法学习参考菜鸟教程:http://www.runoob.com/lua/lua-tutorial.html
  2. Nginx+Lua 开发入门:http://wiki.jikexueyuan.com/project/nginx-lua/introduction.html
  3. nginx + lua实现复杂的控制:https://blog.csdn.net/huangyimo/article/details/80791816
  4. ngx映射到lua模块函数变量一览:https://blog.csdn.net/xiejunna/article/details/53465202
  5. 通过nginx_lua实现根据请求参数分发道不同后端节点: https://blog.csdn.net/yevvzi/article/details/52593490
  6. 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" '
                      'statusbody_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}';
        }
    }
}
  • 实现需求
  1. 时间格式需要更改为2018/12/13 16:38:15,而nginx.conf默认的时间格式为:14/Dec/2018:17:40:55 +0800 。
  • 在nginx.conf中server{}上先根据host map为fmt_localtime参数。
  • 日志格式中[time_local] 更改为fmt_localtime
  • 根据lua获取到nginx的所有参数将ngx.localtime()赋值给$fmt_localtime
 map hostfmt_localtime {
        default '';
    }
    log_by_lua_block {
       ngx.var.fmt_localtime = ngx.localtime();
    }  
  1. 根据访问参数里面的两个参数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
            }
  1. 如果请求是post请求,需要打印出请求体。
    实现这个功能需要在lua 的content代码块中写入ngx.req.read_body(),不然请求体为空。
  2. 如果嵌套了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_localtimeserver_addr  uriargs   remote_addrstatus';
    log_format  access_post  'fmt_localtimeserver_addr  urirequest_body   remote_addrstatus';
    
    map hostfmt_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的功能还有很多,执行速度也很快,非常适合在其他组件中作为嵌套代码执行,继续学习分享中…

文章来源于互联网:nginx联合lua打造大数据日志采集系统个性化需求

发表评论