应用场景和日志文件解析

本配置主要解决 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;

}

}

mysql如何实现实时存储_OpenResty + Mysql 实现日志实时存储相关推荐

  1. 京东App秒级百G日志传输存储架构设计与实战

    本文作者:平台业务研发部-武伟峰,数据与智能部-李阳 背景 在日常工作中,我们通常需要存储一些日志,譬如用户请求的出入参.系统运行时打印的一些info.error之类的日志,从而对系统在运行时出现的问 ...

  2. MySQL存储引擎 lnnoDB逻辑架构 innodb存储引擎表空间(ibd文件)详解 回滚日志的物理空间

    文章目录 存储引擎 一 MySQL组织架构 二 查看存储引擎信息 三 修改存储引擎 3.1 配置文件修改存储引擎 3.2 临时修改存储引擎 3.3 建表时修改存储引擎 四 存储引擎实验 五 数据库升级 ...

  3. 实战干货|自研数据存储迁移MySQL实战

    背景   最近公司内部在做某自研数据存储的下线工作,这里我们暂且化名其为DistributeSQL,由于DistributeSQL不再进行服务支持,需要迁移项目中使用到该存储到其他数据存储中.   本 ...

  4. mysql更改数据库数据存储目录_MySQL更改数据库数据存储目录

    MySQL数据库默认的数据库文件位于/var/lib/mysql下,有时候由于存储规划等原因,需要更改MySQL数据库的数据存储目录.下文总结整理了实践过程的操作步骤. 1:确认MySQL数据库存储目 ...

  5. mysql支持的并发数_重学MySQL系列(五):谈谈对MySQL的存储引擎的理解

    原创作者,公众号[程序员读书],欢迎关注公众号,转载文章请注明出处哦. MySQL关于存储引擎的架构设计,相较于其他关系数据库管理系统,比如Oracle,SQL Server等数据库,这是MySQL最 ...

  6. mysql 外键引擎_对于mysql的外键和mysql的存储引擎

    存储引擎说白了就是如何存储数据.如何为存储的数据建立索引和如何更新.查询数据等技术的实现方法.因为在关系数据库中数据的存储是以表的形式存储的,所以存储引擎也可以称为表类型(即存储和操作此表的类型). ...

  7. mysql数据是怎么存储的_mysql数据是怎么存储的

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

  8. mysql int 默认值 为ull_mysql的 约束 数据库设计 数据库 存储 触发器 mysql 权限问题...

    今天的目标: mysql的 约束 数据库设计 数据库 存储 触发器 mysql 权限问题 先讲约束: 要他唯一 不能重复 不能空值 : 什么是 mysql的约束: 对mysql 进行约束 2.2  默 ...

  9. Mysql学习总结(4)——MySql基础知识、存储引擎与常用数据类型

    1.基础知识 1.1.数据库概述 简单地说:数据库(Database或DB)是存储.管理数据的容器: 严格地说:数据库是"按照某种数据结构对数据进行组织.存储和管理的容器". 总结 ...

最新文章

  1. 中国数据中心市场时评—简析全国数据中心布局情况
  2. jquery简单原则器(匹配索引为指定值的元素)
  3. KDB支持单步调试功能(ARM架构)
  4. 适用于ios和android,适用于iOS和Android的OpenGL ES差异
  5. 德力西双电源自动转换开关说明书_聊聊双电源转换的那点事。
  6. OpenBUGS抽样数据基本操作
  7. CACTI 仙人掌监控平台
  8. Freescale i.mx28 Boot-stream分析
  9. 改造二叉树 (长乐一中模拟赛day2T1)
  10. [转载] python 列表List中index函数的坑
  11. linux下多条命令组合使用
  12. 【java期末复习题】第15章 JDBC数据库编程
  13. 给快播指一条生路:转型会员付费吧
  14. Amazfit T-Rex Pro 体验:户外运动爱好者的装备清单里,有它一席之地
  15. 淘宝API item_search_similar - 搜索相似的商品
  16. pyodbc mysql_Robot Framework 通过pyodbc连接Mysql
  17. java info()方法_Java中的提供者getInfo()方法
  18. 冗余技术----线路冗余与生成树技术及其安全增强
  19. 为员工 尚德机构建教育圈最大期权池
  20. 数据加密——列置换加密

热门文章

  1. Maya硬表面建模学习教程 Master Hard Surface Modeling in Maya 2020
  2. Apache源码包在LINUX(CENTOS6.8)中的安装(出现问题及解决)
  3. c# .netframwork 4.0 调用 2.0时报错 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集。...
  4. [kuangbin带你飞]专题六 最小生成树 L - 还是畅通工程 (简单最小生成树)
  5. 手把手教你如何扩展GridView之自带CheckBox
  6. 4514: [Sdoi2016]数字配对
  7. 【BZOJ1015】【JSOI2008】星球大战 并查集
  8. 转:在线框架引用 bootstrap/jq/jqmobile/css框架
  9. android Viewpager取消预加载及Fragment方法的学习
  10. JS魔法堂:mmDeferred源码剖析