前几篇博文我们介绍了三级缓存架构的内容,并且实现了基于Nginx的定向请求分发的功能,那么接下来我们依次来编写Nginx缓存,redis缓存和Tomcat堆缓存的代码实现
参考之前的博文
- SpringBoot+Redis+MemCache+Nginx+Lua实现三级缓存架构(一)——三级缓存架构体系
- SpringBoot+Redis+MemCache+Nginx+Lua实现三级缓存架构(二)——Nginx环境安装和整合Lua
- SpringBoot+Redis+MemCache+Nginx+Lua实现三级缓存架构(三)——Nginx+Lua实现定向请求分发
本博文今天主要基于Nginx+Lua+Redis+Cache实现三级缓存架构的代码实现,使用的平台代码,我们可以直接在《缓存与数据库双写一致性的解决方案——附上代码解决方案》的框架基础上来实现,具体的代码可以参考Gitee上的 《doubleWriterConsistence》
第一篇博文已经整理了三级架构的业务逻辑,为了方便接下来我们代码的开发,我们再来整理下三级缓存架构的业务逻辑

- 用户请求过来首先经过Nginx的分发层来分发请求,对于同一访问定向请求到Nginx的应用层
- Nginx应用层接收请求,判断Nginx本地有没有数据
- 如果有数据直接返回给用户,如果没有数据,则向Redis中请求获取数据
- Redis中如果没有数据库,则会向Tomcat堆缓存中获取数据
- 如果Tomcat堆缓存中也没有数据的话,则最终会到数据库中获取数据
- 每层的数据获取都会到将数据保存到上一层的容器里面
Nginx+Lua对服务的定向请求访问
在上一篇博文中,我们规划了三个服务器来作为Nginx的分发层和应用层
服务器 | 用途 |
---|---|
192.168.56.105 | 分发层 |
192.168.56.106 | 应用层 |
192.168.56.107 | 应用层 |
并且已经整理好了Nginx分发层到应用层请求的功能,现在我们来实现Nginx接收分发层的参数,并且请求系统服务的功能
- 搭建Nginx应用层渲染Html的环境
我们需要使用一个html静态界面来展示我们获取的数据,Lua层需要依赖来支持
## 在我们的自定的lualib包中添加依赖支持
cd /home/work/lualib/resty/
## 下载template.lua
wget https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template.lua
## 创建html模板的文件夹,放置以后的html模板
mkdir /home/work/lualib/resty/html
## 在该文件夹下,添加html.lua
wget https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template/html.lua
然后我们在应用层的nginx.conf中添加访问模板的路径配置
-- vi /home/work/conf/nginx.conf
server {
listen 80;
server_name _;
set $template_location "/templates";
set $template_root "/home/work/templates";
location /lua {
default_type text/html;
content_by_lua_file /home/work/conf/lua/requestDirect.lua;
}
}
## 创建templates的模板目录
mkdir /home/work/templates
## 创建 requestDirect.html
vi requestDirect.html
<!--我们只做展示使用 -->
<html>
<title>192.168.56.107</title>
<bddy>
<div>request_direct:{* name *}</div>
</body>
</html>
再同样设置另一个应用层的nginx
在Nginx中如果想使用共享缓存的话,需要在 nginx.conf中设置缓存的大小
-- 修改/usr/local/openresty/nginx/conf/nginx.conf,添加缓存的支持 在http的模块中添加如下的配置
lua_shared_dict my_cache 128m;
配置完成之后,我们来编写应用层调用服务和返回结果保存nginx缓存和渲染界面的代码
local uri_args = ngx.req.get_uri_args()
-- 获取参数id
local id = uri_args["id"]
-- 获取nginx缓存
local cache_ngx = ngx.shared.my_cache
-- 定义缓存的key
local idCacheKey = "id_"..id
-- 根据key获取缓存
local idCache = cache_ngx:get(idCacheKey)
-- 如果缓存为空的话 定向请求
if idCache == "" or idCache == nil then
local http = require("resty.http")
local httpc = http.new()
-- 这里的ip是业务层的ip地址和端口号
local resp, err = httpc:request_uri("http://192.168.56.1:8080",{
method = "GET",
path = "/load?id="..id
})
-- 将返回结果赋值给缓存变量
idCache = resp.body
-- 设置缓存并且添加失效时间
cache_ngx:set(idCacheKey, idCache, 10 * 60)
end
-- 加载cjson插件
local cjson = require("cjson")
local idCacheJSON = cjson.decode(idCache)
local context = {
name = idCacheJSON.name
}
local template = require("resty.template")
-- 渲染html数据
template.render("requestDirect.html", context)
lua层的代码开发完成之后,我们来校验下编写的是否有问题
## 查看是否有编写的问题
nginx -t
nginx -s reload
Tomcat堆缓存设置
使用之前双写数据一致性的代码框架来编写,该框架中已经集成了操作redis的功能,现在我只需要在此基础上添加ehCache的功能,然后在此基础上编写业务代码即可
<!-- 在maven中添加 ehcache的依赖-->
<!-- Ehcache 坐标 -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
在resource目录下添加 ehcache.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<config
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.1.xsd">
<!-- 定义缓存为堆内存-->
<cache-template name="heap-cache">
<resources>
<heap unit="entries">2000</heap>
<offheap unit="MB">100</offheap>
</resources>
</cache-template>
<!-- 定义缓存别名-->
<cache alias="inventory" uses-template="heap-cache">
<expiry>
<!-- 缓存失效时间-->
<ttl unit="seconds">40</ttl>
</expiry>
</cache>
</config>
最后在 applicaiton.yml
中添加路径
spring:
cache:
jcache:
config: classpath*:ehcache.xml
这样ehcache就已经集成好了,下面我们开始编写业务代码
/**
* Copyright © 2018 五月工作室. All rights reserved.
*
* @Project: double-writer-consistence
* @ClassName: InventoryCacheServiceImpl
* @Package: com.amos.doublewriterconsistence.service.impl
* @author: zhuqb
* @Description: Ehcache 缓存
* @date: 2019/8/30 0030 下午 16:06
* @Version: V1.0
*/
@Service
@Transactional(readOnly = false, rollbackFor = Exception.class)
@CacheConfig(cacheNames = "inventory")
public class InventoryCacheServiceImpl implements InventoryCacheService {
@Autowired
InventoryMapper inventoryMapper;
/**
* 从数据库中查询数据,并且加载数据到缓存
* condition满足缓存条件的数据才会放入缓存,condition在调用方法之前和之后都会判断
* unless用于否决缓存更新的,不像condition,该表达只在方法执行之后判断,此时可以拿到返回值result进行判断了
*
* @param id
* @return
*/
@Override
@Cacheable(key = "'id_'+#id", unless = "#result == null")
public Inventory select(String id) {
return this.inventoryMapper.selectById(id);
}
/**
* 数据删除的时候,也删除堆缓存
*
* @param id
*/
@Override
@CacheEvict(key = "'id_'+#id")
@Transactional(readOnly = false, rollbackFor = Exception.class)
public void delete(String id) {
this.inventoryMapper.delete(id);
}
}
定义Nginx请求后端的接口
/**
* Copyright © 2018 五月工作室. All rights reserved.
*
* @Project: double-writer-consistence
* @ClassName: InventoryCacheController
* @Package: com.amos.doublewriterconsistence.web
* @author: zhuqb
* @Description:
* @date: 2019/9/5 0005 下午 18:23
* @Version: V1.0
*/
@RestController
public class InventoryCacheController {
@Autowired
InventoryService inventoryService;
@Autowired
InventoryCacheService inventoryCacheService;
/**
* nginx定向请求
* 1. 先从redis中查询,
* 2. 如果redis中查询不到,则从ehcache中查询
* 3. ehcache中查询不到,则从数据库总查询(这步包括在ehcache中)
*
* @param id
* @return
*/
@GetMapping("/load")
public Inventory load(@RequestParam("id") String id) {
// 先从redis中查询
Inventory inventory = this.inventoryService.getInventoryCache(id);
if (StringUtils.isEmpty(inventory)) {
inventory = this.inventoryCacheService.select(id);
}
return inventory;
}
}
至此我们就完成了简单的三级缓存架构的框架,后端的代码是比较简单的,主要是理解三级缓存架构的流程,通过三级缓存来支持高并发时的访问,减少数据库的交互,同时降低缓存带来的一系列的风险
网友评论