多级缓存的使用场景
大多数网站首页都有轮播图,轮播图数据一般需要通过后台接口获得,当并发量较大时会给服务器带来压力。一般的解决方案是将轮播图数据缓存到Redis中,这样就能减少对数据库的访问。我们访问Redis也需要使用Java,Java项目部署在Tomcat中,Tomcat服务器也会面对并发量大的压力。Nginx服务器的并发性能要远远高于Tomcat,在Nginx中使用Lua脚本就能实现MySQL和Redis的读写,绕过Tomcat,大大提高首页的并发性能。
优化方案
问题1:首页广告轮播图接口多,轮播图接口需要连接后台数据库,访问导致页面响应较慢
第一轮优化:
因为轮播图比较少更新,可以使用Redis缓存,通过Tomcat来访问
问题2:
Tomcat在高并发情况下性能不高,Nginx服务器的并发性能大大优于Tomcat,如何利用Nginx访问Redis,提升响应速度和并发性能
第二轮优化: 通过OpenResty整合Lua脚本访问Redis,直接将数据返回给前端页面
Lua
简介
Lua 是由巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组于1993年开发的一种轻量、小巧的脚本语言,用标准 C 语言编写,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua的特性
-
轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
-
可扩展: Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。
-
其它特性:
-
支持面向过程(procedure-oriented)编程和函数式编程(functional programming);
-
自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;
-
语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;
-
通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。
-
官网:The Programming Language Lua
安装
Windows安装
下载地址:Releases · rjpcomputing/luaforwindows · GitHub
Linux安装
curl -R -O http://www.lua.org/ftp/lua-5.3.0.tar.gz tar zxf lua-5.3.0.tar.gz cd lua-5.3.0 make linux test make install
安装IDE
Elicpse插件:Lua Development Tools, an IDE for the Lua programming language
Idea插件:在plugins菜单中搜索 EmmyLua,安装重启
语法
2.3.1 Helloworld
print("Hello Lua")
2.3.2 注释
-- 单行注释 -- [[ 多行注释 -- ]]
2.3.3 数据类型
Lua的数据类型有:
-
number 只有double类型,占4字节
-
string 单引号和双引号括起来
-
boolean 包含true、false,nil也算false
-
空 nil
2.3.4 变量
包含全局变量和局部变量
-- 全局变量 num = 10; print("num="..num) -- 局部变量 local name = false; -- 使用type获得数据类型 local myType = type(name) print("type="..myType)
2.3.5 IF-ELSE
-- if-else local age = 14; local gender = "male"; if age >= 18 and gender == "female" then print("女大十八变"); elseif age >= 18 and gender == "male" then print("男大十八变"); else print("小毛孩子"); end
2.3.6 WHILE循环
-- while循环 local i = 0 while i <= 100 do print("i = "..i); i = i + 1; end
2.3.7 FOR循环
for 变量初始化,结束值,递增值
-- for循环 for i = 0,100,1 do print("i = "..i); end
2.3.8 函数
基本函数
-- 定义函数 function sum(n1,n2) return n1 + n2; end local result = sum(22,33); print(result);
Lua的函数可以返回多个结果
-- 返回多个结果 function getNumbers() return 11,22,33; end local n1,n2,n3 = getNumbers(); print(n1,n2,n3);
闭包机制,函数也可以看做值
function func() local num = 0; -- 匿名函数 return function() num = num + 2; return num; end end local foo = func(); print(foo()) print(foo()) print(foo())
2.3.9 数组
Lua的数组可以包含多种数据类型,甚至函数
下标是从1开始的
-- 数组 arr = {"haha", 100, "hehe",function() print("hoho") return 99 end} print(arr[1],arr[2],arr[3],arr[4]())
使用for-in可以遍历下标和值
for k,v in pairs(arr) do print(k,v) end
2.3.10 面向对象
Lua支持面向对象的语法
-- 定义对象属性 local person = {id = 1,name = "张三",age = 20}; print(person.id,person.name,person.age); -- 定义对象方法 function person.sayHi() print("我是"..person.name.."年龄"..person.age); end person.sayHi();
OpenResty服务器
简介
OpenResty是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
OpenResty通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。
OpenResty的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。
安装
Centos为例
预编译安装
yum install yum-utils yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo yum install openresty
源码编译安装
1. 下载源码包 http://openresty.org/cn/download.html 2. 解压后,进入目录执行配置命令 ./configure make && make install 3. 执行编译命令后,出现目录/usr/local/openresty
使用入门
1. 修改/etc/profile,配置nginx环境变量
PATH=/usr/local/openresty/nginx/sbin:$PATH
export PATH
2. 使profile生效
source /etc/profile
3. 启动nginx
nginx
4. 修改配置文件/usr/local/openresty/nginx/conf/nginx.conf
server {
listen 8080;
location / {
default_type text/html;
content_by_lua_block {
ngx.say("<p>hello, world</p>")
}
}
}
5. 重启nginx
nginx -s reload
6. 测试,显示hello, world
curl http://localhost:8080
使用进阶
3.4.1 引入lua脚本
在conf目录中新建lua/hello.lua
ngx.say("<h1>Hello Lua!!</h1>")
修改nginx.conf
server { listen 8080; location /lua { default_type text/html; content_by_lua_file conf/lua/hello.lua; } }
3.4.2 获得URI中的单个变量
修改hello.lua
ngx.say(ngx.var.arg_a)
arg_a能获得参数a的值
http://192.168.7.188:8080/lua?a=zhangsan
3.4.3 获得URI中的所有变量
修改hello.lua
local uri_args = ngx.req.get_uri_args() for k, v in pairs(uri_args) do if type(v) == "table" then ngx.say(k, " : ", table.concat(v, ", "), "<br/>") else ngx.say(k, ": ", v, "<br/>") end end
可以在网页输出所有参数列表
http://192.168.7.188:8080/lua?a=22&b=33&c=88
3.4.4 获得请求头信息
local headers = ngx.req.get_headers() ngx.say("Host : ", headers["Host"], "<br/>") ngx.say("user-agent : ", headers["user-agent"], "<br/>") ngx.say("user-agent : ", headers.user_agent, "<br/>") for k,v in pairs(headers) do if type(v) == "table" then ngx.say(k, " : ", table.concat(v, ","), "<br/>") else ngx.say(k, " : ", v, "<br/>") end end
3.4.5 获得POST请求的参数
ngx.req.read_body() ngx.say("post args begin", "<br/>") local post_args = ngx.req.get_post_args() for k, v in pairs(post_args) do if type(v) == "table" then ngx.say(k, " : ", table.concat(v, ", "), "<br/>") else ngx.say(k, ": ", v, "<br/>") end end
3.4.6 获得其他内容
http协议版本
ngx.say("ngx.req.http_version : ", ngx.req.http_version(), "<br/>")
请求方法
ngx.say("ngx.req.get_method : ", ngx.req.get_method(), "<br/>")
原始的请求头内容
ngx.say("ngx.req.raw_header : ", ngx.req.raw_header(), "<br/>")
body内容体
ngx.say("ngx.req.get_body_data() : ", ngx.req.get_body_data(), "<br/>")
3.5 MySQL操作
3.5.1 连接MySQL数据库
在OpenResty服务器中使用Lua脚本连接MySQL
-- 引用mysql模块 local mysql = require "resty.mysql" -- 获得数据库对象 local db = mysql:new() -- 设置超时 db:set_timeout(1000) -- 连接数据库 ok是正常结果,err是错误结果 local ok, err = db:connect{ host = "IP", port = 端口, database = "数据库名", user = "账号", password = "密码", charset = "utf8" } if not ok then ngx.say("连接失败", err) return end ngx.say("连接成功")
3.5.2 执行查询语句
在OpenResty服务器中使用Lua脚本对表进行查询
-- 执行查询 res是查询结果 local res, err = db:query("查询语句") if not res then ngx.say("查询错误 ", err) return end db:close() -- 将查询结果转换为json local cjson = require "cjson" ngx.say(cjson.encode(res))
3.6 Redis操作
3.6.1 连接Redis
在OpenResty服务器中使用Lua脚本连接Redis
local redis = require "resty.redis" local red = redis:new() red:set_timeout(2000) local ok,err = red:connect("127.0.0.1",6379) if not ok then ngx.say("connect error: ", err) return end
3.6.2 执行Redis命令
在OpenResty服务器中使用Lua脚本读写Redis数据
red:set("键",值) local value = red:get("键") red:close()
总结
本次课程我们了解了使用OpenResty和Lua脚本对广告轮播图接口优化的业务流程,然后学习了Lua脚本语言和OpenResty服务器的基本使用,最后使用Lua语言实现了MySQL和Redis数据的查询,完成了接口优化业务的技术储备,后面我们将使用这些技术实现接口优化业务。