【实战干货】Nginx + Lua 实现域名访问日志统计,CentOS环境一键落地!

作为运维/开发同学,你是否遇到过需要精准统计域名访问数据的需求?比如统计 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),先备份再替换:

perl 复制代码
# 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节点:

ini 复制代码
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格式,数据实时更新):

css 复制代码
{
    "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实现,无需额外部署应用,性能拉满。

本文使用 markdown.com.cn 排版

相关推荐
青云计划14 小时前
知光项目知文发布模块
java·后端·spring·mybatis
Victor35614 小时前
MongoDB(9)什么是MongoDB的副本集(Replica Set)?
后端
Victor35614 小时前
MongoDB(8)什么是聚合(Aggregation)?
后端
yeyeye11115 小时前
Spring Cloud Data Flow 简介
后端·spring·spring cloud
Tony Bai16 小时前
告别 Flaky Tests:Go 官方拟引入 testing/nettest,重塑内存网络测试标准
开发语言·网络·后端·golang·php
+VX:Fegn089516 小时前
计算机毕业设计|基于springboot + vue鲜花商城系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
程序猿阿伟16 小时前
《GraphQL批处理与全局缓存共享的底层逻辑》
后端·缓存·graphql
小小张说故事17 小时前
SQLAlchemy 技术入门指南
后端·python
识君啊17 小时前
SpringBoot 事务管理解析 - @Transactional 的正确用法与常见坑
java·数据库·spring boot·后端
想用offer打牌18 小时前
MCP (Model Context Protocol) 技术理解 - 第五篇
人工智能·后端·mcp