电商详情页案例介绍
电商的详情页是并发量很高的服务,开发者通常采用静态化或缓存的方式减少后台服务器的压力
案例的技术点:
- OpenResty服务器,
- Lua调用Redis
- Lua的http模块
- Lua页面模板
下面先使用几个案例,介绍这些技术点
使用Lua连接Redis
OpenResty的库中自带的resty.redis可以用于连接Redis
在openresty/nginx/conf新建lua目录,新建redis_test.lua文件
-- 导入resty.redis
local redis = require "resty.redis"
-- 创建redis对象
local red = redis:new()
-- 连接redis ok返回成功信息,erro是错误信息
local ok,err = red:connect("127.0.0.1",6379)
if not ok then
ngx.say("connect failed",err)
return
end
-- 调用set命令
ok,err = red:set("city","wuhan")
if not ok then
ngx.say("redis set failed",err)
return
end
ngx.say("set city ok<br>")
-- 调用get命令
local val,err = red:get("city")
if not val then
ngx.say("redis get failed",err)
return
end
if val == ngx.null then
ngx.say("city is null")
return
end
-- 输出结果
ngx.say("city==>",val)
修改nginx.conf
server {
listen 8080;
location /redis_test {
default_type text/html;
content_by_lua_file conf/lua/redis_test.lua;
}
}
重启nginx,访问
使用Lua发送HTTP请求
使用Lua发送http请求可以借助开源框架 lua-resty-http
https://github.com/ledgetech/lua-resty-http
下载文件:http_headers.lua、http.lua、http_connect.lua
保存到openresty/lualib/resty目录下
基本的http请求
创建http_test.lua
-- 创建连接对象
local httpc = require("resty.http").new()
-- 发送GET请求
local res, err = httpc:request_uri("http://192.168.101.11:8001/item?id=99", {
method = "GET",
headers = {
["Content-Type"] = "application/x-www-form-urlencoded"
}
})
if not res then
ngx.log(ngx.ERR, "request failed: ", err)
return
end
-- 返回请求内容
local status = res.status
local length = res.headers["Content-Length"]
local body = res.body
ngx.say("body:",body)
使用SpringBoot开发接口
@RestController
public class HelloController {
@Value("${server.port}")
private Long port;
@RequestMapping("item")
public String item(Long id){
return "From: "+ port +",Get Item " + id;
}
}
修改nginx.conf
location /http_test3 {
default_type text/html;
content_by_lua_file conf/lua/http_test.lua;
}
重启nginx,访问
URL哈希实现负载均衡
下面案例通过对id参数进行哈希运算,对服务器列表实现负载均衡
local http = require("resty.http").new()
-- 服务器列表
local hosts = {"192.168.101.11:8001","192.168.101:11:8002"}
-- 获得id参数
local item_id= ngx.var.arg_id
if not item_id then
ngx.say("id is null")
return
end
-- 获得哈希值
local id_hash = ngx.crc32_long(item_id)
-- 计算下标 lua从1开始
local index = (id_hash % 2) +1
-- 通过下标获得主机名,执行GET请求
local resp, err = http:request_uri("http://"..hosts[index], {
method = "GET",
path = "/item?id="..item_id,
headers = {
["Content-Type"] = "application/x-www-form-urlencoded"
}
})
if not resp then
ngx.say("request error :", err)
return
end
ngx.say(resp.body)
http:close()
让后台接口在8001和8002两个端口启动,不同的id会访问不同的服务器
使用Lua创建页面模板
lua-resty-template可以使用Lua创建类似JSP的模板
https://github.com/bungle/lua-resty-template
下载template和template.lua,保存到resty中
基本使用
view_template.lua
local template = require "resty.template"
-- 方法1 使用new方法进行页面渲染,message是自定义数据
local view = template.new "view.html"
view.message = "Hello, World!"
view:render()
-- 方法2 使用render实现页面渲染
template.render("view.html", { message = "Hello, World!" })
nginx/html/view.html
<!DOCTYPE html>
<html>
<body>
<h1>{{message}}</h1>
</body>
</html>
nginx.conf
location /view_template {
default_type text/html;
content_by_lua_file conf/lua/view_template.lua;
root html;
index view.html;
}
访问效果
Lua+Redis+OpenResty实现电商详情页
实现思路
course.lua
-- 获得路径参数id
local id = ngx.var.id
-- 连接redis
local redis = require "resty.redis"
local red = redis:new()
local ok,err = red:connect("127.0.0.1",6379)
if not ok then
ngx.say("connect failed",err)
return
end
-- 查询该id的课程
local val,err = red:get("course"..id)
if not val then
ngx.say("redis get failed",err)
return
end
if val == ngx.null then
-- 缓存为空,查询后台接口
local httpc = require("resty.http").new()
local res, err = httpc:request_uri("http://192.168.101.18:8002/course/"..id, {
method = "GET",
headers = {
["Content-Type"] = "application/x-www-form-urlencoded"
}
})
if not res then
ngx.log(ngx.ERR, "request failed: ", err)
return
end
val = res.body
if val == ngx.null then
ngx.say("course not found ",id)
return
end
-- 保存到redis
red:set("course"..id,val)
end
-- 实现页面渲染
local template = require "resty.template"
template.caching(false)
local context = {
course = val
}
template.render("course.html", context)
nginx.conf
location ~*/course/([0-9]+)$ {
default_type text/html;
root html;
set $id $1; #获得uri最后的数字保存到id变量中,lua中通过ngx.var.id获得
index course.html;
content_by_lua_file conf/lua/course.lua;
}
course.html\css\js 保存到nginx/html目录中
页面使用了Vue.js,其中Vue的{{ }}符号和Lua模板冲突,所以需要使用{-raw-}括起来,Lua不进行处理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>课程页面</title>
<link rel="stylesheet" type="text/css" href="/css/course.css">
</head>
<body>
<div id="course">
<div style="background: #eee;">
<div class="nav-wrap">
<p class="nav-p-pc" style="margin-top:-25px;text-align:left;">
<a href="/">课程列表</a>
<span class="sharp-content">></span>
<span class="nav-sec">{-raw-}{{course.courseName}}{-raw-}</span>
</p>
</div>
<!-- 课程详情 -->
...
{-raw-}{{course.price}}{-raw-}
...
</div>
</div>
</body>
<script src="/js/vue.js"></script>
<script>
var vue = new Vue({
el: "#course",
data: {
course:{*course*} //以非转义方式绑定lua模板中的course对象
},
created() {
console.log(this.course);
}
});
</script>
</html>
后台查询接口
@RestController
public class CourseController {
@Autowired
private CourseService courseService;
@GetMapping("course/{courseid}")
public Course getCourseById(@PathVariable("courseid")Integer courseid) {
Course course = courseService.getCourseById(courseid);
return course;
}
}
访问效果