mysql如何实现实时存储_OpenResty + Mysql 实现日志实时存储
应用场景和日志文件解析
本配置主要解决 Nginx 向 MySQL 中实时插入日志的问题,采用 OpenResty + Mysql 实现。
1. 刚开始的时候看了 Nginx 和 MySQL 的连接模块。比如说 nginx-mysql-module,可以连接 MySQL。但是插入日志时遇到问题,我们知道 nginx 的执行过程先是 location 解析并重写阶段,然后是访问权限控制阶段,接着是内容生成阶段,最后是日志记录阶段。MySQL 访问阶段属于内容生成阶段,所以代理运行的时间和状态,MySQL 都无法获取的到。因此,这种通过 nginx 直连 MySQL 的方式无法达到我们的要求。
2. 通过 lua 脚本在日志生成阶段获取信息,然后将数据插入 MySQL。nginx 有一个限制,无法在 log 阶段访问 socket 即无法访问 MySQL,所以无法在 log 阶段直接将数据存入 MySQL。但是可以通过运行包含 MySQL 操作的 shell 脚本来解决这个问题。但是这个方法有两个弊端:
获取到 Nginx 代理的结果后,每次都要连接 MySQL 并向其插入数据。当并发量大时,MySQL 端会出现问题。
不向 MySQL 插入数据,整个时间的消耗大约在 0.02-0.04s 之间。而向 MySQL 插入数据后,整个时间消耗大约在 0.4-0.9 之间,消耗的时间是原来的 10 倍。
3. 通过 lua + ngx.time.at + lua_MySQL + lua.share.dict 解决问题。整个过程如下所示:
在 nginx 启动阶段,ngx.time.at 启动一个延时任务。在任务中,每隔一段时间取出 nginx 内存共享区的 log 数据,将数据合并,存入 MySQL,同时再一个相同的延时任务,递归调用。这样就与 crontab 命令相似。当定时器到期,定时器中的 Lua 代码是在一个“轻线程”中运行的,它与创造它的原始请求是完全分离的,因此不存在大量线程同时运行的情况。
在日志生成阶段,将数据封装并存入 nginx 的内存共享区。
MySQL 访问权限的问题
不但访问 MySQL 的 MySQL 用户需要有操作对应数据库的权限,还需要调用 MySQL 命令的用户具有访问 MySQL 的权限。授权命令如下:
GRANT ALL PRIVILEGES ON *.* to root@xxx IDENTIFIED BY 'password';
MySQL 编码类型
总的来说,MySQL 的数据库对应三种编码。MySQL 客户端显示数据的编码,连接 MySQL 用的编码(即数据存入 MySQL 时,数据的编码),MySQL 存储用的编码(字段,表,数据库三种格式可能不同)。不管 MySQL 存储用的编码是什么,只要 MySQL 客户端显示数据的编码和连接 MySQL 用的编码相同,数据就能通过 MySQL 客户端正确显示。
配置文件
user root;
worker_processes 2;
events {
worker_connections 1024;
}
http{
lua_package_path "/home/oicq/guomm/nginx_lua/lua-resty-mysql-master/lib/?.lua;;"; --重要
lua_shared_dict logs 10m;
init_worker_by_lua_block {
local delay = 10
function put_log_into_mysql(premature)
local mysql = require "resty.mysql"
local db, err = mysql:new()
if not db then
ngx.log(ngx.ERR,"failed to instantiate mysql: ", err)
return
end
db:set_timeout(1000)
local ok, err, errcode, sqlstate = db:connect{
host = "xxx",
port = 3306,
database = "database_name",
user = "username",
password = "password",
charset = "utf8",
}
if not ok then
ngx.log(ngx.ERR,"failed to connect: ", err, ": ", errcode, " ", sqlstate)
return
end
-- get data from shared dict and put them into mysql
local key = "logs"
local vals = ""
local temp_val = ngx.shared.logs:lpop(key)
while (temp_val ~= nil)
do
vals = vals .. ",".. temp_val
temp_val = ngx.shared.logs:lpop(key)
end
if vals ~= "" then
vals = string.sub(vals, 2,-1)
local command = ("insert into es_visit_record(access_ip,server_ip,access_time,run_time,es_response_time,request_body_byte,run_state,url,post_data) values "..vals)
ngx.log(ngx.ERR,"command is ",command)
local res, err, errcode, sqlstate = db:query(command)
if not res then
ngx.log(ngx.ERR,"insert error: ", err, ": ", errcode, ": ", sqlstate, ".")
return
end
end
local ok, err = db:close()
if not ok then
ngx.log(ngx.ERR,"failed to close: ", err)
return
end
-- decycle call timer to run put_log_into_mysql method, just like crontab
local ok, err = ngx.timer.at(delay, put_log_into_mysql);
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
end
local ok, err = ngx.timer.at(delay, put_log_into_mysql)
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
}
upstream elasticsearch_servers {
server xxx max_fails=3 fail_timeout=30s;
server xxx max_fails=3 fail_timeout=30s;
server xx max_fails=3 fail_timeout=30s;
}
log_format porxy '$remote_addr,$upstream_addr,[$time_local],$request,$request_body,$status,$body_bytes_sent,$request_time,$upstream_response_time';
server {
listen 9202;
location / {
proxy_pass http://elasticsearch_servers;
log_by_lua_block{
local currentTime = os.date("%Y-%m-%d %H:%M:%S", os.time())
currentTime = "\"" .. currentTime .. "\""
local req_body = '-'
if ngx.var.request_body then
req_body = ngx.var.request_body
req_body = string.gsub(req_body,"\n","")
--req_body = string.gsub(req_body,"\t","")
end
req_body = "\"" .. req_body .. "\""
local req_status = 0
if ngx.var.status then
req_status = ngx.var.status
end
local req_time = 0
if ngx.var.request_time then
req_time = ngx.var.request_time
end
local req_req = "\"" .. ngx.var.request .. "\""
local remote_addr = "\"" .. ngx.var.http_x_forwarded_for .. "\""
local server_addr = "\"" .. ngx.var.upstream_addr .. "\""
local myparams = ("("..remote_addr..",".. server_addr..","..currentTime..","..ngx.var.request_time .. ",".. ngx.var.upstream_response_time..","..ngx.var.body_bytes_sent..","..ngx.var.status..","..req_req..","..req_body..")")
local key = "logs"
local len,err = ngx.shared.logs:rpush(key, myparams)
if err then
ngx.log(ngx.ERR,"failed to put log vals into shared dict")
return
end
}
}
access_log logs/es_access.log porxy;
}
}
相关文章:

简易RS232 建模二 (接收)
//clK系统时钟为50MHZ 先发低位后发高位 先接收地位后接收高位module uart_tx (input clk,rst_n, UART_CTS, output reg UART_RTS, input UART_RXD, output reg UART_TXD, output [7:0] led);reg [3:0]state;reg [30:0] count;reg [7:0] data;assign led rx_data; reg [7:0] …

《UNIX高级环境编程》 -- apue.h
在看《UNIX高级环境编程》这本书的时候,会遇到一个问题就是这个”apue.h”,这个是作者为了编写代码方便封装了一个库,我们可以使用下面的方式解决这个问题,让我们的代码可以像作者一样去使用,这样的话,我们就可以好好研…

mysql 多少个数据库_mysql数据库的几个基本概念
1、表数据库表是一系列二维数组的集合,用来存储数据和操作数据的逻辑结构。行是记录,列是字段,每一列表示记录的一个属性,都有相应的描述信息。2、数据类型:数据类型决定了数据在计算机中的存储格式,代表不…

教孩子正确对待分数
期末考试各科成绩渐渐公布了,小丽迫不及待地跑到办公室向老师打听分数。看到自己那一科成绩好时欣喜若狂,看到差的则懊悔不已。如果你的孩子也象小丽一样视考分为命根,该如何教孩子正确对待分数?让她从考分的困惑中解放出来?●考前给孩子制…

canvas初尝试
最近学习了canvas,就拿它做了这么个小东西,感觉已经爱上canvas了。上代码 /* * auhor : 开发部-前端组-李鑫超 * property { tableData : {Array} 表格数据 add v-1.0.0 } * property { columData : {Array} 表头数据 add v-1.0.0 } * property { me…

模板1.0 -- 模板基本原理
为什么需要模板 我们经常有这样的一种使用的情形,就是我们可能需要设计一个函数,然后函数的参数可能是整形的,也可能是浮点型的,还有可能是其他的类型的,这个时候如果对于每一个类型都写一个函数,未免有点…
[置顶] 我的GB28181标准开发里程碑——基于eXosip的IPC端与SPVMN注册成功
昨天编译搭建好eXosip的开发环境后,今天完成了SIP注册功能,里程碑一战啊!加油加油,成功就在眼前! 今天基于eXosip做了一个IPC客户端,成功与公安部的SPVMN视频监控联网调测软件自测工具进行注册交互…

mysql如何避免特殊字符查询_如何避免MySQL中的特殊字符?
慕的地10843我已经用Java开发了自己的MySQL转义方法(如果对任何人都有用的话)。请参阅下面的类代码。警告:如果启用了任何_反斜杠_转义SQL模式,则出错。private static final HashMap sqlTokens;private static Pattern sqlTokenPattern;static{ …

visio 2010 修改 默认字体 字号大小 方法
哈哈,我这是标题党,先给大家泼个冷水。Visio2010 并不支持对一次性地修改绘图中所有图形的字体大小!但可以有一个比较笨的方法解决。1.新建一个模具2.将常用的图形放到这个模具中3.对每个图形进行编辑4.对这个形状的字体,字号进行…

[BZOJ3329] Xorequ
题解: 网上的方法基本是建立在发现临位不能相等的基础上的 这个很好证。。 但是不利用这个特征也是可以的 x^2x3x 我们考虑二进制的前i位,我们会发现3x最多涉及到了前i2位 于是我们可以记录一下前i位的3x的i1,i2位的状态,以及第i位填了什么 因…

Asp.net中时间格式化的几种方法
1. 数据控件绑定时格式化日期方法:<asp:BoundColumn DataField"AddTime" HeaderText"添加时间" DataFormatString"{0:yyyy-MM-dd HH:mm}></asp:BoundColumn><asp:BoundField DataField"AddTime" HeaderText"添加时间&q…

centos设置网络自动启动
问题描述 centos7虚拟机如何设置开机自启动网络设置 解决方法 切换到root用户进入到网络设置的目录下面cd /etc/sysconfig/network-scripts/当前目录下面有一个类似于ifcfg-ens33,使用vim打开文件进行编辑,将ONBOOTno修改成为yes就可以了

mysql删除原则_MySQL数据库的增删选查
数据库是专门存储数据对象的容器,这里的数据对象包括表、视图、触发器、存储过程等,其中表是最基本的数据对象。创建数据库在 MySQL 数据库中存储数据对象之前,先要创建好数据库。语法:create database [if not exists] [[default…

ubuntu网卡配置
1、dhcp自动获取 sudo vi /etc/network/interfaces auto eth0 iface eth0 inet dhcp 设置生效: sudo /etc/init.d/networking restart 或 sudo dhclient eth0 2、静态IP地址 sudo vi /etc/network/interfaces auto eth0 iface eth0 inet static address 192.168.3.90 netmask 2…

自动化运维工具----ansible
自动化运维工具----ansible ansible是新出现的运维工具是基于Python研发的糅合了众多老牌运维工具的优点实现了批量操作系统配置、批量程序的部署、批量运行命令等功能。 主要模块以及功能: 1 command 2 user 3 group 4 cron 5 copy 6 file 7 ping 8 yum 9 service …

【内核】嵌入式linux内核的五个子系统
Perface Linux内核主要由进程调度(SCHED)、内存管理(MM)、虚拟文件系统(VFS)、网络接口(NET)和进程间通信(IPC)5个子系统组成,如图1所示。 图1 Li…

git用户文档1 — git基础
1. git基础 1.1 分布式 我们把远端仓库(云端的仓库)称为repo,repo必须有一个master分支,就是主分支。 repo除了有一个master分支,还有很多其他的分支,若干个分支之间存储的数据一版都是不一样的本地可以git clone下来repo的mast…

MySQL5.7的date类型_Mysql5.7 虚拟列数据类型为DATE时,如何存入数据?
表结构:v_date为虚拟列CREATE TABLE test ( json TEXT NULL, date DATETIME NULL DEFAULT NULL, v_date DATE AS (json_extract(json,$.date)) VIRTUAL)COMMENT测试表\r\nCOLLATEutf8mb4_general_ciENGINEInnoDB;插入:INSERT INTO test (json) …

找出字符串中所有数字
刚才网友在SKYPE问Insus.NET一个问题,在MS SQL中,怎样找出一个字符串所有数字。 Insus.NET使用较简单与平常的方法,就是使用循环的方法,循环字符串中每一个字符,并插入至一个表变量中。然后再SELECT这个表变量…

Safair 浏览器cllick事件不生效或者需要双击才生效
针对Safair 浏览器cllick事件不生效或者需要双击才生效的解决方案。 方法一:给元素加上cursor: pointer样式。(不生效) 方法二:ios事件机制不一样,将click事件改为mousedown或其他事件即可解决。(需要双击&…

springboot mysql行锁_SpringBoot基于数据库实现简单的分布式锁
本文介绍SpringBoot基于数据库实现简单的分布式锁。1.简介分布式锁的方式有很多种,通常方案有:基于mysql数据库基于redis基于ZooKeeper网上的实现方式有很多,本文主要介绍的是如果使用mysql实现简单的分布式锁,加锁流程如下图&…

简单shell
执行脚本结果重定向 sh hah.sh hello 1>>/home/qiso/job.log 2>&1 上面这句话的意思是 首先通过sh执行脚本hah.sh,其中执行这个脚本的时候,需要传入参数,参数是hello, 1表示的是标准输出,以上脚本执行…

个人随笔、收藏——(包括技术、设计思想等)
1、开源自动化工具 Sahi、Selenium、AutoIt Sahi,是一个用于Web应用程序的自动测试工具。Sahi运行为一个代理服务器,必须把浏览器的代理设置为Sahi服务器。 然后Sahi注入javascript来访问Web页面中的元素。Sahi支持Http与Https并且独立于Web站点或Web应用…

安装QCreator2.5+Qt4.8.2+MinGW_gcc_4.4
QCreator最近升级了。正好想试试新功能,所以把原来安装的QCreator2.3.1Qt4.7.1卸载了。安装新的版本。具体步骤如下: 1# 下载QCreator2.5version。 2# 下载Qt4.8.2version。 3# 根据Qt官网给的链接,下载MinGW-gcc440_1(因为从QCre…

第一次结对作业
211606368林书浩 211606352陈彬 一、预估与实际 PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)Planning计划• Estimate• 估计这个任务需要多少时间3540Development开发• Analysis• 需求分析 (包括学习…

mysql如何根据业务分表设计_mysql分表分库的应用场景和设计方式
很多朋友在论坛和留言区域问mysql在什么情况下才需要进行分库分表,以及采用何种设计方式才是最优的选择,根据这些问题,小编为大家整理了关于MySQL分库分表的应用场景和最优的设计方式举例。一. 分表场景:对于大型的互联网应用来说…

简单protobuf
protobuf的数据类型,有最简单的那种数据类型,就是一个文件中,定义了一个message 可以在一个文件中定义两个message,两个message之间是没有关联的可以在一个文件中,定义两个message,其中一个是简单的&#x…

迭代器、生成器
迭代器 lst range(10) #生成一个枚举列表 从0-9 itr iter(lst) #生成一个迭代器 itr.next() #访问迭代器方法 遍历迭代器 try:while True:val itr.next()print val except StopIteration:pass 注意: 1、如果对list dict tuple 用for遍历,则for内部自动…

Linq 无法删除尚未附加的实体的问题
Linq删除个集合数据, 刚开始用的注释掉的那行, 会提示"无法删除尚未附加的实体"错误, 使用Attach方法依然不行. 想想以前用过DeleteAllOnSubmit没啥问题哈, 估计是_db对象的引用问题, 换了种写法就OK了. //删掉所有工作组部门关联 //wgdrLst Wor…

从云端到边缘 AI推动FPGA应用拓展
近日,全球最大的FPGA厂商赛灵思宣布收购深鉴科技的消息,引发人工智能芯片行业热议,这也是首起中国AI芯片公司被收购的案例。值得注意的是,收购深鉴科技的赛灵思在2018年下半年重点发展方面是汽车自动驾驶。 FPGA市场的竞争正在发生…