nginx + Lua 实现域名访问日志统计

作为运维/开发同学,你是否遇到过需要精准统计域名访问数据的需求?比如统计 www.mnxz.funblog.mnxz.fun 这类域名的今日访问量、总访问量,原生Nginx又搞不定Lua相关的高级玩法?今天就手把手教你基于CentOS系统,用OpenResty(Nginx+Lua生态)实现这一需求,避开手动编译的各种坑,全程可复制!

一、需求背景

我们需要实现:

✅ 统计指定域名(www.mnxz.fun/blog.mnxz.fun)的访问记录;

✅ 区分「今日访问量」和「总访问量」,数据持久化不丢失;

✅ 基于Nginx+Lua实现,轻量化、高并发不卡顿。

避坑提醒 :原生Nginx不带Lua模块,手动编译易遇到版本兼容、resty.core 缺失等问题,优先选择OpenResty(官方集成Lua生态,开箱即用)。

二、核心方案:用OpenResty替换原生Nginx

OpenResty是Nginx的增强版,内置了完整的Lua模块和resty核心库,完美解决手动编译的版本兼容坑,这也是我们踩过多次坑后总结的最优方案!

步骤1:环境准备(CentOS系统)

bash 复制代码
# 1. 检查系统环境(确认CentOS版本,适配依赖)
cat /etc/redhat-release

# 2. 安装基础依赖
yum install -y wget gcc gcc-c++ make zlib-devel pcre-devel openssl-devel

步骤2:安装OpenResty(替换原生Nginx)

如果你的服务器已装过原生Nginx(路径/usr/local/nginx),先备份再替换:

bash 复制代码
# 1. 停止原生Nginx,避免端口冲突
/usr/local/nginx/sbin/nginx -s stop
ps -ef | grep nginx | grep -v grep | awk '{print $2}' | xargs kill -9 2>/dev/null

# 2. 备份原生Nginx配置(防止丢失)
mv /usr/local/nginx /usr/local/nginx_legacy

# 3. 添加OpenResty官方源
wget https://openresty.org/package/centos/openresty.repo -O /etc/yum.repos.d/openresty.repo
yum clean all && yum makecache

# 4. 安装OpenResty(自带Lua模块,无需手动编译)
yum install -y openresty

# 5. 创建软链接,复用原有路径习惯
ln -s /usr/local/openresty/nginx /usr/local/nginx

步骤3:安装Redis(数据持久化必备)

统计数据需要持久化,避免Nginx重启丢失,Redis是最优选择:

bash 复制代码
# 1. 安装Redis
yum install -y redis

# 2. 启动Redis并设置开机自启
systemctl start redis
systemctl enable redis

# 3. 验证Redis(返回PONG即正常)
redis-cli ping

步骤4:核心配置(Lua统计访问日志)

编辑Nginx主配置文件 /usr/local/nginx/conf/nginx.conf,替换http块内的server节点:

nginx 复制代码
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    # Lua共享字典(减轻Redis压力)
    lua_shared_dict traffic_stats 10m;

    # 定义获取今日日期的Lua函数
    init_by_lua_block {
        function get_today_date()
            return os.date("%Y%m%d")
        end
    }

    server {
        listen       80;
        server_name  www.mnxz.fun blog.mnxz.fun;

        # 核心:访问统计逻辑(所有请求触发)
        access_by_lua_block {
            -- 1. 获取当前访问域名,仅统计目标域名
            local domain = ngx.var.server_name
            local target_domains = {["www.mnxz.fun"]=true, ["blog.mnxz.fun"]=true}
            if not target_domains[domain] then return end

            -- 2. 定义Redis键名(区分今日/总访问量)
            local today = get_today_date()
            local total_key = "traffic:total:" .. domain
            local today_key = "traffic:today:" .. domain .. ":" .. today

            -- 3. 连接Redis(超时1秒,失败不影响业务)
            local redis = require "resty.redis"
            local red = redis:new()
            red:set_timeout(1000)
            local ok, err = red:connect("127.0.0.1", 6379)
            if not ok then
                ngx.log(ngx.WARN, "Redis连接失败: ", err)
                return
            end

            -- 4. 原子自增统计(高并发不丢数)
            red:incr(total_key)  -- 总访问量+1
            red:incr(today_key)  -- 今日访问量+1
            red:expire(today_key, 86400*2)  -- 今日数据保留2天

            -- 5. 复用Redis连接(提升性能)
            red:set_keepalive(10000, 100)

            -- 日志记录(方便排查)
            ngx.log(ngx.INFO, "【访问统计】域名:", domain, " 今日Key:", today_key)
        }

        # 静态文件根目录(替换为你的实际路径)
        root  /usr/local/nginx/html;
        index  index.html index.htm;

        # 【实用】本地查看统计数据的接口(仅允许服务器本地访问)
        location = /get_traffic_stats {
            allow 127.0.0.1;
            deny all;
            default_type application/json;
            content_by_lua_block {
                local redis = require "resty.redis"
                local red = redis:new()
                red:set_timeout(1000)
                local ok, err = red:connect("127.0.0.1", 6379)
                if not ok then
                    ngx.say('{"code":500,"msg":"Redis连接失败:'..err..'"}')
                    return
                end

                local today = get_today_date()
                local result = {}
                local domains = {"www.mnxz.fun", "blog.mnxz.fun"}
                for _, domain in ipairs(domains) do
                    local total = red:get("traffic:total:" .. domain) or 0
                    local today_count = red:get("traffic:today:" .. domain .. ":" .. today) or 0
                    result[domain] = {
                        total = tonumber(total),
                        today = tonumber(today_count)
                    }
                end

                -- 输出JSON格式统计结果
                ngx.say(require("cjson").encode({
                    code = 200,
                    data = result,
                    date = today
                }))
                red:set_keepalive(10000, 100)
            }
        }

        # 错误页面配置
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

步骤5:启动并验证效果

bash 复制代码
# 1. 检查配置语法(无报错即正常)
/usr/local/nginx/sbin/nginx -t

# 2. 启动OpenResty
/usr/local/nginx/sbin/nginx

# 3. 模拟访问,触发统计
curl http://www.mnxz.fun
curl http://blog.mnxz.fun

# 4. 本地查看统计结果(仅服务器内执行)
curl http://127.0.0.1/get_traffic_stats

预期输出(JSON格式,数据实时更新):

json 复制代码
{
    "code":200,
    "data":{
        "www.mnxz.fun":{"total":1,"today":1},
        "blog.mnxz.fun":{"total":1,"today":1}
    },
    "date":"20260209"
}

步骤6:查看访问日志

bash 复制代码
# 查看Lua统计日志(关键信息)
tail -f /usr/local/nginx/logs/error.log

# 查看常规访问日志
tail -f /usr/local/nginx/logs/access.log

三、避坑指南(踩过的坑全告诉你)

  1. 报错:unknown directive "access_by_lua_block"
    → 原因:用了原生Nginx而非OpenResty;解决方案:按步骤替换为OpenResty即可。
  2. 报错:failed to load the 'resty.core' module
    → 原因:手动编译的Lua模块版本过低;解决方案:放弃手动编译,直接用OpenResty(自带兼容版本)。
  3. 报错:open() "/usr/local/nginx/logs/nginx.pid" failed
    → 原因:logs目录缺失/权限不足;解决方案:mkdir -p /usr/local/nginx/logs && chmod 755 /usr/local/nginx/logs

四、拓展与总结

1. 功能拓展

  • 按小时统计:修改Redis键名为 traffic:hour:www.mnxz.fun:2026020919(拼接小时);
  • 按页面统计:新增 local uri = ngx.var.uri,将uri加入Redis键名;
  • 数据可视化:结合Grafana+Redis,制作访问量仪表盘。

2. 核心总结

✅ 优先选OpenResty:避免手动编译Lua模块的版本兼容坑,开箱即用;

✅ 数据持久化:用Redis的incr原子操作,高并发下统计不丢数;

✅ 轻量化:基于Nginx+Lua实现,无需额外部署应用,性能拉满。

相关推荐
程序员洪志道1 天前
封装复杂性:一个反复生效的架构手法
nginx·程序员
Dear~yxy1 天前
Nginx知识点详解
运维·nginx
cc.ChenLy1 天前
Nginx核心解析:正向代理、反向代理、负载均衡、下载、安装、使用...
运维·nginx·负载均衡
FJW0208141 天前
Nginx + Redis + srcache + PHP-FPM架构部署
redis·nginx·php
codingWhat1 天前
手把手系列之——前端的Nginx 与 Docker 部署实践
nginx·docker
kiramario1 天前
本地环境后端服务nginx反向代理,前端联调CORS设置不生效解决
运维·前端·nginx
nnbulls11 天前
Linux下安装Nginx服务及systemctl方式管理nginx详情
linux·运维·nginx
小闫同学yxy2 天前
离线安装Nginx(Linux环境且无root权限)
nginx
程序员洪志道2 天前
连接层里住着一个 HTTP 解析器
nginx·程序员
007php0072 天前
后端面试 100 题 + 简短满分答案
java·redis·nginx·缓存·面试·职场和发展·php