在javashop电商系统中像买家端首页、商品详情页、店铺首页等这类页面有两个共同的特点,即访问量大,数据不常变动。对于这类页面,为了减轻前端服务器的压力,javashop将这些页面缓存到了Redis中,当用户访问这些页面时,服务器会通过OpenResty代理执行lua脚本访问Redis中缓存的静态页面信息。接下来,我们来详细的讲解下整个流程中所涉及到的技术点。
1、缓存静态页
缓存静态页的过程在javashop中是通过httpClient抓取前端服务器中的静态页内容放到redis中。
1)抓取静态页信息
private String getHTML(String url, String type, int times) throws IOException {
String html;
// socket超时 connect超时
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(50000)
.setConnectTimeout(50000)
.build();
CloseableHttpClient httpClient =HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("Client-Type", type);
CloseableHttpResponse response = httpClient.execute(httpGet);
int status = response.getStatusLine().getStatusCode();
if (status != 200) {
html = EntityUtils.toString(response.getEntity(), "utf-8");
debugger.log("生成静态页出错:静态页渲染服务返回" + status, "uri:" + url);
debugger.log(html);
if (times >= 4) {
return ("create error ,return code is :" + status + ",url is :" + url);
} else {
times++;
debugger.log(times + "次重试...");
//重试时等待时间,在一段时间后重试 防止出现429(Too much connections in one mintue)问题
try {
Thread.sleep(times * 1000);
} catch (InterruptedException e) {
logger.debug(e.getMessage());
}
html = getHTML(url, type, times);
return html;
}
} else {
if (times > 0) {
debugger.log("重试成功");
}
html = EntityUtils.toString(response.getEntity(), "utf-8");
}
return html;
}
2)存入redis
//生成消息
String url = getUrl(path, type);
//通过http 来获取html存储redis
String html = this.getHTML(url, type);
stringRedisTemplate.opsForValue().set(name, html);
2、访问静态页
OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
OpenResty官网地址:http://openresty.org/cn/
下面我们着重介绍一下OpenResty是如何通过lua脚本访问redis的,以PC端静态页为例。
1)首先我们准备好访问redis的lua脚本,代码如下
单机版:single_connector.lua
local request_uri = ngx.var.uri
local redis = require "resty.redis"
local red = redis:new()
--red:set_timeout(10000) -- 1 sec
local ok, err = red:connect("ip", port)
if not ok then
ngx.say("failed to connect: ", err)
return
end
--如果设置了密码请打开注释,并填写密码
--local res, err = red:auth("password")
-- if not res then
-- ngx.say("failed to authenticate: ", err)
-- return
--end
local res, err = red:get(request_uri)
-- 请修改https://www.test.com 为实际配置域名或IP端口
if res == ngx.null then
ngx.redirect("https://www.test.com/404.html")
return
end
if not res then
ngx.say("failed to get: " .. request_uri , err)
return
end
red:set_keepalive(6000,1000)
ngx.say(res)
集群版:cluster_connector.lua
local request_uri = ngx.var.uri
local config = {
name = "testCluster", --rediscluster name
-- enableSlaveRead = true,
serv_list = { --redis cluster node list(host and port),
{ ip = "ip", port = port },
{ ip = "ip", port = port }
},
keepalive_timeout = 60000, --redis connection pool idle timeout
keepalive_cons = 300, --redis connection pool size
connection_timout = 15000, --timeout while connecting
max_redirection = 5 --maximum retry attempts for redirection
}
local redis_cluster = require "resty.rediscluster"
local red_c = redis_cluster:new(config)
-- 请修改https://www.test.com 为实际配置域名或IP端口
local v, err = red_c:get(request_uri)
if v == ngx.null then
ngx.redirect("https://www.test.com/404.html")
return
end
if err then
ngx.log(ngx.ERR, "err: ", err)
else
ngx.say(v)
end
注意:
redis配置注释掉bind 127.0.0.1、设置protected-mode 为no;否则通过lua连接redis出错
2)在OpenResty中配置server路径
#新建静态页输出节点,content_by_lua_file为lua脚本实际路径。
server {
listen 80;
server_name localhost;
#pc端静态响应
location /PC {
default_type text/html;
content_by_lua_file "/usr/local/openresty/nginx/conf/lua/single_connector.lua";
#redis集群请使用此配置
#content_by_lua_file "/usr/local/openresty/nginx/conf/lua/cluster_connector.lua";
}
}
易族智汇(javashop)原创文章