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

前几篇博文我们介绍了三级缓存架构的内容,并且实现了基于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》

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

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
    
    
    192.168.56.107
    
        
request_direct:{* name *}

再同样设置另一个应用层的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的功能,然后在此基础上编写业务代码即可

    
    
    
        org.ehcache
        ehcache
    

在resource目录下添加 ehcache.xml配置文件

    
    
        
        
            
                2000
                100
            
        
        
        
            
                
                40
            
        
    
    

最后在 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;
        }
    }

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

你可能感兴趣的:(SpringBoot+Redis+MemCache+Nginx+Lua实现三级缓存架构(四)——Nginx+Redis+Cache实现三级缓存架构)