Openresty/Lua/Redis/Mysql实现静态化网页加载
场景
类似于秒杀活动,或者是有一些不常变动的网页,可以通过生成本地html文件,用户访问时直接通过nginx访问本地文件,不走或者减少操作数据库,以降低用户等待时间,提升用户体验。
实践
下载openresty
OpenResty - 下载
然后解压备用
新增lua工具
1、在解压目录下的lualib文件夹下新建myutil文件夹
2、新增并编辑redis_factory.lua文件,这个文件用来操作redis
local redis_factory = function(h)local h = hh.redis = require('resty.redis')h.cosocket_pool = {max_idel = 10000, size = 10000}h.commands = {"append", "auth", "bgrewriteaof","bgsave", "bitcount", "bitop","blpop", "brpop","brpoplpush", "client", "config","dbsize","debug", "decr", "decrby","del", "discard", "dump","echo","eval", "exec", "exists","expire", "expireat", "flushall","flushdb", "get", "getbit","getrange", "getset", "hdel","hexists", "hget", "hgetall","hincrby", "hincrbyfloat", "hkeys","hlen","hmget", "hmset", "hscan","hset","hsetnx", "hvals", "incr","incrby", "incrbyfloat", "info","keys","lastsave", "lindex", "linsert","llen", "lpop", "lpush","lpushx", "lrange", "lrem","lset", "ltrim", "mget","migrate","monitor", "move", "mset","msetnx", "multi", "object","persist", "pexpire", "pexpireat","ping", "psetex", "psubscribe","pttl","publish", "punsubscribe", "pubsub","quit","randomkey", "rename", "renamenx","restore","rpop", "rpoplpush", "rpush","rpushx", "sadd", "save","scan", "scard", "script","sdiff", "sdiffstore","select", "set", "setbit","setex", "setnx", "setrange","shutdown", "sinter", "sinterstore","sismember", "slaveof", "slowlog","smembers", "smove", "sort","spop", "srandmember", "srem","sscan","strlen", "subscribe", "sunion","sunionstore", "sync", "time","ttl","type", "unsubscribe", "unwatch","watch", "zadd", "zcard","zcount", "zincrby", "zinterstore","zrange", "zrangebyscore", "zrank","zrem", "zremrangebyrank", "zremrangebyscore","zrevrange", "zrevrangebyscore", "zrevrank","zscan","zscore", "zunionstore", "evalsha",-- resty redis private command"set_keepalive", "init_pipeline", "commit_pipeline", "array_to_hash", "add_commands", "get_reused_times",}-- connect-- @param table connect_info, e.g { host="127.0.0.1", port=6379, password="", timeout=1000, database=0}-- @return boolean result-- @return userdata redis_instanceh.connect = function(connect_info)local redis_instance = h.redis:new()redis_instance:set_timeout(connect_info.timeout)if not redis_instance:connect(connect_info.host, connect_info.port) then return false, nilendif connect_info.password ~= '' thenredis_instance:auth(connect_info.password)endredis_instance:select(connect_info.database)return true, redis_instanceend-- spawn_client-- @param table h, include config info-- @param string name, redis config name-- @return table redis_clienth.spawn_client = function(h, name)local self = {}self.name = ""self.redis_instance = nilself.connect = nilself.connect_info = {host = "", port = 0, password = "", timeout = 0, database = 0}-- constructself.construct = function(_, h, name)-- set infoself.name = nameself.connect = h.connectself.connect_info = h[name]-- gen redis proxy clientfor _, v in pairs(h.commands) doself[v] = function(self, ...)-- instance test and reconnect if (type(self.redis_instance) == 'userdata: NULL' or type(self.redis_instance) == 'nil') thenlocal okok, self.redis_instance = self.connect(self.connect_info)if not ok then return false endend-- get datalocal vas = { ... }return self.redis_instance[v](self.redis_instance, ...)endendreturn trueend-- do constructself:construct(h, name) return selfend local self = {}self.pool = {} -- redis client name pool-- construct-- you can put your own construct code here.self.construct = function()returnend-- spawn-- @param string name, redis database serial name-- @return boolean result-- @return userdata redisself.spawn = function(_, name)if self.pool[name] == nil thenngx.ctx[name] = h.spawn_client(h, name) self.pool[name] = truereturn true, ngx.ctx[name]elsereturn true, ngx.ctx[name]endend-- destruct-- @return boolean allok, set_keepalive resultself.destruct = function()local allok = truefor name, _ in pairs(self.pool) dolocal ok, msg = ngx.ctx[name].redis_instance:set_keepalive(h.cosocket_pool.max_idel, h.cosocket_pool.size)if not ok then allok = false end endreturn allokend-- do constructself.construct() return self
endreturn redis_factory
3、新增并编辑mysql_factory.lua文件,这个文件用来操作数据库
local mysql = require("resty.mysql") local mysql_pool = {} --[[ 先从连接池取连接,如果没有再建立连接. 返回: false,出错信息. true,数据库连接
--]]
function mysql_pool:get_connect(cfg) if ngx.ctx[mysql_pool] then return true, ngx.ctx[mysql_pool] end local client, errmsg = mysql:new() if not client then return false, "mysql.socket_failed: " .. (errmsg or "nil") end client:set_timeout(10000) --30秒 local options = { host = cfg.mysqlConfig.prod.host, port = cfg.mysqlConfig.prod.port, user = cfg.mysqlConfig.prod.user, password = cfg.mysqlConfig.prod.password, database = cfg.mysqlConfig.prod.database } local result, errmsg, errno, sqlstate = client:connect(options) if not result then return false, "mysql.cant_connect: " .. (errmsg or "nil") .. ", errno:" .. (errno or "nil") .. ", sql_state:" .. (sqlstate or "nil") end local query = "SET NAMES " .. "utf8" local result, errmsg, errno, sqlstate = client:query(query) if not result then return false, "mysql.query_failed: " .. (errmsg or "nil") .. ", errno:" .. (errno or "nil") .. ", sql_state:" .. (sqlstate or "nil") end ngx.ctx[mysql_pool] = client -- 测试,验证连接池重复使用情况 --[[ comments by leon1509 local count, err = client:get_reused_times() ngx.say("xxx reused times" .. count); --]] return true, ngx.ctx[mysql_pool]
end --[[ 把连接返回到连接池 用set_keepalive代替close() 将开启连接池特性,可以为每个nginx工作进程,指定连接最大空闲时间,和连接池最大连接数 --]]
function mysql_pool:close() if ngx.ctx[mysql_pool] then -- 连接池机制,不调用 close 而是 keeplive 下次会直接继续使用 -- lua_code_cache 为 on 时才有效 -- 60000 : pool_max_idle_time , 100:connections ngx.ctx[mysql_pool]:set_keepalive(60000, 80) -- 调用了 set_keepalive,不能直接再次调用 query,会报错 ngx.ctx[mysql_pool] = nil end
end --[[ 查询 有结果数据集时返回结果数据集 无数据数据集时返回查询影响 返回: false,出错信息,sqlstate结构. true,结果集,sqlstate结构.
--]]
function mysql_pool:query(sql, flag) local ret, client = self:get_connect(flag) if not ret then return false, client, nil end local result, errmsg, errno, sqlstate = client:query(sql) while errmsg == "again" do result, errmsg, errno, sqlstate = client:read_result() end self:close() if not result then errmsg = "mysql.query_failed:" .. (errno or "nil") .. (errmsg or "nil") return false, errmsg, sqlstate end return true, result, sqlstate
end return mysql_pool
4、新增并编辑config_constant.lua文件,这个文件用来配置redis和mysql的连接信息
config = {}config.redisConfig = {redis_a = {--iphost = '127.0.0.1',--端口port = 6379,--密码password = 'qweasdzczs',--超时时间,如果是测试环境debug的话,这个值可以给长一点;如果是正式环境,可以设置为200timeout = 2000,--redis的库database = 0,},}config.mysqlConfig = {prod = {host = '127.0.0.1',port = 3306,user = 'root',password = '!@#QWEasdzxc',database = 'file',}
}
return config
5、新增并编辑article.lua文件,这个是业务文件,目的实现文章的静态化访问
--平台公共的配置文件常量
local config = require "myutil.config_constant"
--redis连接池工厂
local redis_factory = require('myutil.redis_factory')(config.redisConfig) -- import config when construct
--获取redis的连接实例
local ok, redis_a = redis_factory:spawn('redis_a')
local json = require("cjson")--获取URL链接中的文章ID,例、http://testdomain.com/article/66666666,http://testdomain.com/article/66666666.html,key为66666666或者66666666.html
local key = ngx.re.sub(ngx.var.uri, "^/article/(.*)", "$1", "o")
if (string.len(key) == 0) then -- 传参有误,根目录下要有404.htmlngx.req.set_uri('/404.html')
end--在redis中获取key对应的值
local va = redis_a:get("article:"..key)local file = nilif(va and va ~= ngx.null) then--redis中存在数据file = json.decode(va)
else--mysql连接池local mysqlUtil = require "myutil.mysql_factory"--组装sql语句local sql = "SELECT id, article_name articleName, html_path htmlPath, create_time createTime, user_id userId FROM article_info where id = "..key--执行sql语句local ret, res, sqlstate = mysqlUtil:query(sql, config);--判断查询结果if((not ret) or res ==nil or #res<1) thenlocal hasSplit, endSplit = string.find(key,'%.',1)if(hasSplit)thenlocal keySplit = string.sub(key, 0, hasSplit - 1)sql = "SELECT id, article_name articleName, html_path htmlPath, create_time createTime, user_id userId FROM article_info where id = "..keySplitret, res, sqlstate = mysqlUtil:query(sql, config);endend--判断查询结果if(ret and res ~=nil and #res>0) then--取出最新的值file = res[1]local fileStr = json.encode(file)redis_a:set("article:"..key,fileStr)end
end
if (file) then -- 根据数据库中配置的地址加载文件local du = file.htmlPathngx.req.set_uri(du)
elsengx.req.set_uri('/404.html')
end
配置Nginx
nginx通过rewrite + lua的方式访问
worker_processes 1;events {worker_connections 1024;
}http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;autoindex on;# 显示目录autoindex_exact_size on;# 显示文件大小autoindex_localtime on;# 显示文件时间server {listen 80;server_name localhost;location /article {root d:/;#这里的lua文件的路径为绝对路径,请根据自己安装的实际路径填写#记得斜杠是/这个,从window中拷贝出来的是\这样,这样是有问题的,务必注意rewrite_by_lua_file D:/openresty-test/lualib/myutil/article.lua;}}}
Openresty/Lua/Redis/Mysql实现静态化网页加载相关推荐
- OpenResty+Lua+redis+mysql实现高性能高可用限流缓存
OpenResty(又称:ngx_openresty) 是一个基于 NGINX 的可伸缩的 Web 平台.并发性能可在10k-1000k OpenResty安装 1.添加仓库执行命令 yum inst ...
- 如何提升网页加载性能
摘自 https://github.com/xitu/gold-miner/blob/master/TODO/building-a-shop-with-sub-second-page-loads-le ...
- 内容分发网络 CDN 是如何提高网页加载时间的?
几乎 Internet 上的每个人都体验过内容分发网络 (CDN) 的好处.大多数科技公司,包括谷歌.苹果和微软等公司,都使用 CDN 来减少加载网页内容的延迟. CDN 通常会将服务器放置在不同网络 ...
- web前端 网页加载 性能优化大全
web前端 性能优化 - - 如何提高网页加载速度 文章目录 web前端 性能优化 --- --- 如何提高网页加载速度 1. 减少DNS查找 2. 使用CDN托管资源 3. 减少Http请求 浏览器 ...
- centos uwsgi配置_centos下配置nginx+uwsgi运行py以及静态文件的加载
其实不限于centos,个人觉得所有的linux都一样,就好像你喜欢把钱放在左边的口袋,我喜欢把钱放右边的口袋,还有的人喜欢把钱放里面的口袋,无非是配置文件的地方不一样 首先安装nginx,嗯,这个自 ...
- 用 Flask 来写个轻博客 (28) — 使用 Flask-Assets 压缩 CSS/JS 提升网页加载速度
Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 扩展阅读 Flask-Assets 将 Flask-Assets 应用 ...
- 静态原型设计 加载中_见解1:原型设计有助于填补静态设计留下的空白。
静态原型设计 加载中 In April 2015, I joined the Disney Parks creative team to design mobile experiences for t ...
- 关于前端性能优化问题,认识网页加载过程和防抖节流
前端性能优化-网页加载过程.性能优化方法.防抖和节流 一.网页加载过程 1.加载资源的形式 2.加载资源的过程 3.渲染页面的过程 4.关于window.onload 和 DOMContentLoad ...
- 打开服务器网页要5秒,网页优化技巧 如何把网页加载时间控制在1.5秒以内
今天为大家分享"如何把网页加载时间控制在1.5秒以内(必看)"当然了,控制在2秒以内其实是完全可以的,我们为什么要控制页面打开速度那?首先百度会对加载慢的页面进行打击,对SEO排名 ...
最新文章
- 荣耀30pro系统_荣耀30 pro:正式再见!
- Keep 再融资以后
- cpp遇到问题:include重复包含导致的redefinition
- 掌握python编程语言tensorflow_手把手教你eclipse集成Python语言+Tensorflow环境
- Springmvc 中org.springframework.http.converter.json.MappingJackson2HttpMessageConverter依赖jackson包...
- arcgis计算地形起伏度
- 模拟生态圈_电源工程师必备求生技能——经典20种模拟电路
- android 录音原始文件_Android 11可能最终会取消Android对视频录制的4GB文件大小限制...
- hive数据库的哪些函数操作是否走MR
- hdu3068 求一个字符串中最长回文字符串的长度 Manacher算法
- MMKV_MMKV—强大的存储工具
- excel易用宝的修复
- Mybatis多参数查询与列表查询不同方式实现
- imx8qxp uuu 下载脚本example_kernel_emmc.uuu注释
- 简单学生成绩分析系统
- CentOS防火墙配置(资源)
- 如何在EverEdit编辑器中一键编译运行JAVA并接收键盘输入数据
- 阅读笔记04——魔鬼搭讪学
- java的concurrenthashmap和hashtab
- css如何去掉或修改浏览器默认滚动条