【图解秒杀系列】秒杀技术点——静态化

【图解秒杀系列】秒杀技术点------静态化

什么是静态化、静态化的作用

静态化就是指通过某种静态化技术,将原本需要动态渲染生成的HTML页面固定下来变成一个静态页面文件,后续请求该页面都直接返回该静态页面。

首先要有模板和数据,然后根据给定的模板和数据,通过模板引擎,就能生成对应的静态HTML文件。

生成的静态HTML页面,可以推到Nginx上缓存到Nginx本地。当用户请求访问对应的页面时,Nginx直接返回缓存在本地的静态页面,这样响应速度就大大提升。

在秒杀场景中,商品详情页就可以进行静态化处理,提升商品详情页的访问速度。

如何实现静态化

FreeMarker、Thymleaf

一种方式是通过FreeMarker、Thymleaf这种Java语言的模板引擎实现。

处理流程

FreeMarker、Thymleaf需要跑在一个Tomcat进程里面,当接收到请求时,通过Freemarker、Thymleaf等模板引擎技术,根据指定的模板和数据,生成静态HTML页面,返回客户端。

另外,我们可以监听MQ上的修改操作消息,当监听到有修改操作发生时,就在异步工程里面使用模板引擎生成静态HTML页面,然后推到Nginx上缓存到Nginx本地。

问题

但是这种方案会有几个问题。

首先第一个问题是,如果我们修改了模板,那么使用该模板生成的静态HTML页面全部都要删除或刷新。

第二个问题是如果我们有多个Nginx,则要同时推送给多个Nginx。

如果是多Nginx场景下,碰上批量刷新,那这个操作就很复杂了。

OpenResty + Lua

为了解决上面的问题,就有了一个更好的解决方案,那就是OpenResty加Lua脚本。

OpenResty是基于Nginx进行二次开发的Web平台,支持执行Lua脚本,并且内部集成了许多Lua库和第三方模块。

lua_shared_dict & lua-resty-template

在这个方案下,我们用到OpenResty的两个重要的东西,一个是"lua_shared_dict"指令、lua-resty-template模块。

lua_shared_dict用于声明一个共享内存区域,可以将其作为缓存空间使用,比如"lua_shared_dict my_cache 128m;"表示声明一个128m大小名为"my_cache"的内存共享区域。

而lua-resty-template模块的作用就是一个模板引擎,它的作用与FreeMarker或者Thymleaf类似,只是它是跑在OpenResty内部而不是后端服务。

处理流程

那么此时处理流程如下:

  1. 客户端的请求被OpenResty接收
  2. OpenResty在location块中通过content_by_lua_file命令指定执行的lua脚本
  3. lua脚本被执行,首先判断lua_shared_dict命令声明的缓存空间中是否缓存了对应的数据
  4. 如果缓存命中,则直接通过lua-resty-template模块进行模板渲染生成静态html文件并返回
  5. 如果缓存不命中,则请求后端服务获取对应数据,再缓存到lua_shared_dict命令声明的缓存空间中,然后再进行模板渲染生成静态html文件并返回

这么做的好处就是:

  • 即使模板变了,我们只需要更新OpenResty上的模板即可,由于最终的html文件是由OpenResty动态渲染生成的,所以只要更新了模板,生成的html就会更新。
  • 由于是OpenResty自己通过模板渲染生成的html,而不是后端服务生成的,因此不再需要推送ng的这一步操作。

具体操作

在nginx.conf文http模块中加入:

bash 复制代码
lua_package_path '../lualib/?.lua;;';
lua_package_cpath '../lualib/?.so;;';
include lua.conf;

lua.conf:

bash 复制代码
lua_shared_dict my_cache 128m;
server {
	listen 222;
	set $template_location "/templates";
	set $template_root "D:/ProgramData/nginx/";
	
	location /product {
		default_type 'text/html;charset=UTF‐8';
		lua_code_cache on;
		content_by_lua_file D:/ProgramData/nginx/product.lua;
	}
}

product.lua:

lua 复制代码
	local uri_args = ngx.req.get_uri_args()
	local productId = uri_args["productId"]
	local cache_ngx = ngx.shared.my_cache
	local productCacheKey = "product_info_"..productId
	local productCache = cache_ngx:get(productCacheKey)
	if productCache == "" or productCache == nil then
		local http = require("resty.http")
		local httpc = http.new()
		local resp, err = httpc:request_uri("http://127.0.0.1:8866",{
			method = "GET",
			path = "/pms/productInfo/"..productId
		})
		productCache = resp.body
		local expireTime = math.random(600,1200)
		cache_ngx:set(productCacheKey, productCache, expireTime)
	end
	local cjson = require("cjson")
	local productCacheJSON =cjson.decode(productCache)
	ngx.say(productCache);
	local context = {
		id = productCacheJSON.data.id,
		name = productCacheJSON.data.name,
		price = productCacheJSON.data.price,
		pic = productCacheJSON.data.pic,
		detailHtml = productCacheJSON.data.detailHtml
	}
	local template = require("resty.template")
	template.render("product.html", context)

html模板:

html 复制代码
<html>
	<head>
		<meta http‐equiv="Content‐Type" content="text/html; charset=utf‐8" />
	</head>
	<body>
		<h1>
			商品id: {* id *}<br/>
			商品名称: {* name *}<br/>
			商品价格: {* price *}<br/>
			商品库存: <img src={* pic *}/><br/>
			商品描述: {* detailHtml *}<br/>
		</h1>
	</body>
</html>
相关推荐
咖啡の猫1 小时前
Shell脚本-for循环应用案例
前端·chrome
uzong3 小时前
面试官:Redis中的 16 库同时发送命令,服务端是串行执行还是并行执行
后端·面试·架构
百万蹄蹄向前冲3 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
The Open Group4 小时前
英特尔公司Darren Pulsipher 博士:以架构之力推动政府数字化转型
大数据·人工智能·架构
追逐时光者4 小时前
.NET 使用 MethodTimer 进行运行耗时统计提升代码的整洁性与可维护性!
后端·.net
朝阳5814 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路4 小时前
GeoTools 读取影像元数据
前端
ssshooter5 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
你的人类朋友5 小时前
【Node.js】什么是Node.js
javascript·后端·node.js
曼岛_5 小时前
[系统架构设计师]系统质量属性与架构评估(八)
架构·系统架构