SpringBoot+Redis+MemCache+Nginx+Lua实现三级缓存架构(四)——Nginx+Redis+Cache实现三级缓存架构

11 篇文章 1 订阅
2 篇文章 0 订阅

前几篇博文我们介绍了三级缓存架构的内容,并且实现了基于Nginx的定向请求分发的功能,那么接下来我们依次来编写Nginx缓存,redis缓存和Tomcat堆缓存的代码实现

参考之前的博文

本博文今天主要基于Nginx+Lua+Redis+Cache实现三级缓存架构的代码实现,使用的平台代码,我们可以直接在《缓存与数据库双写一致性的解决方案——附上代码解决方案》的框架基础上来实现,具体的代码可以参考Gitee上的 《doubleWriterConsistence》

第一篇博文已经整理了三级架构的业务逻辑,为了方便接下来我们代码的开发,我们再来整理下三级缓存架构的业务逻辑

image

  • 用户请求过来首先经过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接收分发层的参数,并且请求系统服务的功能

  1. 搭建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;
        }
    }

至此我们就完成了简单的三级缓存架构的框架,后端的代码是比较简单的,主要是理解三级缓存架构的流程,通过三级缓存来支持高并发时的访问,减少数据库的交互,同时降低缓存带来的一系列的风险

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值