通过Nginx+lua+Kafka收集日志

935 阅读7分钟

访问日志收集

日志在我们项目中是非常重要的,日志的作用也有差异,例如根据日志查找问题、根据日志做数据分析。在我们秒杀系统中,活跃的热点商品其实并不多,我们往往需要对热点商品进行额外处理。用户每次抢购商品的时候,都是从详情页发起的,因此统计热度商品,详情页的访问频次可以算一个方向,详情页访问的频次我们可以记录访问日志,然后统计某一段时间的访问量,根据访问量评判商品是否是热点商品。

业务分析

日志收集流程如上图,用户请求经过nginx,此时已经留下了用户对某个商品访问的足迹,我们可以在这里将用户访问的商品信息发送给我们kafka,采用大数据实时分析工具Apache Druid实时存储访问信息,再通过程序分析计算访问情况。

安装kafka

创建Kafka之前,必须先确保Zookeeper已经存在

通过docker安装zk

#直接执行运行命令,以2181端口来运行zookeeper,服务器防火墙需要放开2181端口权限
docker run -d --name zookeeper -p 2181:2181 -t wurstmeister/zookeeper
#这时候因为原先没有下载zookeeper镜像,会先拉取zookeeper镜像,然后启动

完成后,通过docker ps 指令查看运行状态,下图的状态表示运行成功


也可以通过docker logs -f zookeeper 查看运行日志

通过docker安装kafka

启动kafka,同样的,因为没有镜像,也会先拉取镜像再启动

docker run -d --name kafka -p 9092:9092 -e KAFKA_BROKER_ID=0 -e KAFKA_ZOOKEEPER_CONNECT=47.110.58.254:2181 -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://47.110.58.254:9092 -e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 -t wurstmeister/kafka

参数解释

  • -e KAFKA_BROKER_ID=0 在kafka集群中,每个kafka都有一个BROKER_ID来区分自己
  • **-e KAFKA_ZOOKEEPER_CONNECT=**47.110.58.254:2181 /kafka 配置zookeeper管理kafka的路径47.110.58.254:2181 /kafka
  • -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT:// 47.110.58.254:9092 把kafka的地址端口注册给zookeeper
  • -e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 配置kafka的监听端口

通过docker ps查看kafka运行状态,如下表示成功

注意:需要修改47.110.58.254为宿主机的实际ip地址(如果有公网IP,填写公网IP地址)

测试Kafka

创建队列:

# 进入容器
docker exec -it kafka /bin/bash

# 进入目录
cd /opt/kafka_2.13-2.8.1/bin

# 创建队列
./kafka-topics.sh --create --bootstrap-server localhost:9092 --partitions 1 --replication-factor 1 --topic itemaccess

使用kafka-topics.sh创建队列:

  • --create:执行创建一个新的队列操作
  • --bootstrap-server:需要链接的kafka配置,必填
  • --partitions 1:设置一个topic设置几个分区(就是把数据切割成几块,分别存储)
  • --replication-factor 1:设置分区的副本数量(就是设置多少个备份,有了备份,一个挂了没事,可以使用备份)
  • --topic itemaccess:队列的名字叫itemaccess

消息发布

在kafka容器中执行消息发送(接着上面的步骤执行):

# 发送消息
./kafka-console-producer.sh --broker-list localhost:9092 --topic itemaccess

# 发送内容为
{"actime":"2021-4-10 9:50:10","uri":"http://www-seckill.itheima.net/items/333.html","IP":"119.123.33.231","Token":"Bearer itcast"}

消息订阅

# control C 退出消息发送

# 执行以下命令订阅消息
./kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic itemaccess --from-beginning

其他命令

# 进入容器
docker exec -it kafka /bin/bash

# 进入目录
cd /opt/kafka_2.12-2.4.1/bin

# 查看topic列表
./kafka-topics.sh --bootstrap-server localhost:9092 --list

# 删除topics
./kafka-topics.sh --bootstrap-server localhost:9092 --delete --topic itemaccess2

收集日志-Lua

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。OpenResty 通过lua脚本扩展nginx功能,可提供负载均衡、请求路由、安全认证、服务鉴权、流量控制与日志监控等服务。

OpenResty® 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。

关于Lua的基本知识,我们这里就不学习了,直接进入日志收集的使用操作。

使用Lua实现日志收集,并向Kafka发送访问的详情页信息,此时我们需要安装一个依赖组件lua-restry-kafka

1)收集流程

日志收集流程如下:

用户请求/web/items/1.html,进入到nginx第1个location中,在该location中向Kafka发送请求日志信息,并将请求中的/web去掉,跳转到另一个location中,并查找本地文件(前端页面),这样既可以完成日志收集,也能完成页面的访问

2)插件配置

lua-restry-kafka****

# 上传lua-resty-kafka-master.zip

# 解压
unzip lua-resty-kafka-master.zip

# 移动到指定目录
mv lua-resty-kafka-master /usr/local/openresty/

# 修改nginx.conf
vi /usr/local/openresty/nginx/conf/nginx.conf

# 添加内容  在配置文件中指定lua-resty-kafka的库文件位置
# 配置到http里面,和server平级
lua_package_path "/usr/local/openresty/lua-resty-kafka-master/lib/?.lua;;";

配置效果如下:

http {
    ......
    
    #gzip  on;

    #添加内容  在配置文件中指定lua-resty-kafka的库文件位置
    lua_package_path "/usr/local/openresty/lua-resty-kafka-master/lib/?.lua;;";

    server {
        listen       80;
        server_name  localhost;   
        
    ......
}

3)配置请求 指向lua脚本

创建一个lua脚本,items-access.lua

编写测试内容:

# 进入到lib目录
cd /usr/local/openresty/nginx

mkdir lua
#创建items-access.lua并输入以下内容
vim lua/items-access.lua


ngx.say("test")
ngx.exit(200)

按照上面的流程图,我们需要配置nginx的2个location,修改nginx.conf,代码如下:

上图代码如下:

# lua插件位置
lua_package_path "/usr/local/openresty/lua-resty-kafka-master/lib/?.lua;;";

server {
    listen       8880;
    server_name  localhost;

    location /web/items/ {
        #向kafka发送日志记录,处理请求路径,把/web去掉
        content_by_lua_file /usr/local/openresty/nginx/lua/items-access.lua;
    }

    location /items/ {
        root   /usr/local/server/web;
    }

    location / {
        root   html;
        index  index.html index.htm;
    }

}

修改完后,重新加载配置,重启nginx

nginx -s reload

4)日志收集

用户访问详情页的时候,需要实现日志收集,日志收集采用Lua将当前访问信息发布到Kafka中,因此这里要实现Kafka消息生产者。

定义一个消息格式:

{
    "actime": "2020-4-10 9:50:30",
    "uri": "/web/items/S1235433012716498944.html",
    "ip": "192.168.200.1",
    "token": "HELLO WORLD"
}

生产者脚本:

定义好了消息格式后,创建一个生产者,往Kafka中发送详情页的访问信息。脚本内容如下:

上图脚本内容如下:

--引入json解析库
local cjson = require("cjson")
--kafka依赖库
local client = require "resty.kafka.client"
local producer = require "resty.kafka.producer"

--配置kafka的服务地址
local broker_list = {
	{ host = "47.110.58.254", port = 9092 }
}

--创建kafka生产者
local pro = producer:new(broker_list,{ producer_type="async"})

--获取IP
local headers=ngx.req.get_headers()
local ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr or "0.0.0.0"

--定义消息内容
local msg = {}
msg["ip"]=ip
msg["actime"]=os.date("%Y-%m-%d %H:%M:%S")
msg["uri"]=ngx.var.uri
msg["token"]="HELLO WORLD"

--发送异步消息,无论消息是否发送成功,都会执行后面的逻辑
local offset, err = pro:send("logsitems", nil, cjson.encode(msg))

--请求转发到/items/,给用户提供html静态页
local uri = ngx.var.uri
uri = string.gsub(uri,"/web","")
ngx.exec(uri)

Lua脚本的时间获取

local getTime = os.date("%c");

其中的%c可以是以下的一种:(注意大小写)

格式输出内容
%aabbreviated weekday name (e.g., Wed)
%Afull weekday name (e.g., Wednesday)
%babbreviated month name (e.g., Sep)
%Bfull month name (e.g., September)
%cdate and time (e.g., 09/16/98 23:48:10)
%dday of the month (16) [01-31]
%Hhour, using a 24-hour clock (23) [00-23]
%Ihour, using a 12-hour clock (11) [01-12]
%Mminute (48) [00-59]
%mmonth (09) [01-12]
%peither "am" or "pm" (pm)
%Ssecond (10) [00-61]
%wweekday (3) [0-6 = Sunday-Saturday]
%xdate (e.g., 09/16/98)
%Xtime (e.g., 23:48:10)
%Yfull year (1998)
%ytwo-digit year (98) [00-99]
%%the character '%'

5)日志收集测试

请求地址:http://124.221.73.149/web/items/S1235433012716498944.html

查看Kafka的logsitems队列数据:

访问日志收集完成。