提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


文章目录

  • 前言
  • 一、JVM进程缓存
  • 1.1Caffeine
  • 1.1.1Caffeine使用步骤
  • 1.2Caffeine基于SpringBoot的使用
  • 1.2.1创建配置类
  • 1.2.2自动注入后使用
  • 二、nginx本地缓存
  • 2.1为什么要使用OpenResty和Lua
  • 2.2基于OpenResty和Lua实现本地缓存
  • 2.2.1 修改nginx.conf文件
  • 2.2.2编写lua文件
  • 2.2.2.1 获取参数的API
  • 2.2.3 将数据缓存到nginx
  • 三、查询redis
  • 3.1引入redis并初始化
  • 3.2封装redis工具类
  • 3.1.3 在执行文件中的使用
  • 三、查询tomcat



前言

随着数据量,访问量及用户的增加,增加服务器等措施已经不能满足需求,高频访问数据库对数据库的压力仍然很大,而“加一层”可以有效缓解对数据库的压力,这里可以对数据进行多级操作,将他存储在缓存中以减轻对数据库的压力,它包括浏览器缓存,nginx缓存,tomcat缓存及redis等第三方技术


Redisson 缓存空List redis缓存大量数据_nginx

一、JVM进程缓存

JVM进程缓存,即将数据缓存到tomcat中,这里用到的是Caffeine

1.1Caffeine

缓存主要分为2种,reids第三方缓存和jvm本地缓存

  • 分布式缓存,例如Redis:
  • 优点:存储容量更大、可靠性更好、可以在集群间共享
  • 缺点:访问缓存有网络开销
  • 场景:缓存数据量较大、可靠性要求较高、需要在集群间共享
  • 进程本地缓存,例如HashMap、GuavaCache:
  • 优点:读取本地内存,没有网络开销,速度更快
  • 缺点:存储容量有限、可靠性较低、无法共享
  • 场景:性能要求较高,缓存数据量较小
    Caffeine是基于java8的本地缓存,在众多本地缓存中,他的性能遥遥领先

1.1.1Caffeine使用步骤

1.导入依赖

<dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
        </dependency>

2.创建缓存对象

Cache<T,T> cache = Caffeine.newBuilder().build();

在newBuilder()后面可以继续设置 Caffeine的属性,如初始大小initialCapacity,最大容量(键)maximumSize,过期时间expireAfterWrite
常用api

  • getIfPresent():获取一个存在的键,如果不存在就返回null
  • get():查询目标键,返回对应的值,如果不存在则去数据库查找
@Test
    void testBasicOps() {
        // 创建缓存对象
        Cache<String, String> cache = Caffeine.newBuilder().build();

        // 存数据
        cache.put("gf", "迪丽热巴");

        // 取数据,不存在则返回null
        String gf = cache.getIfPresent("gf");
        System.out.println("gf = " + gf);

        // 取数据,不存在则去数据库查询
        String defaultGF = cache.get("defaultGF", key -> {
            // 这里可以去数据库根据 key查询value
            return "柳岩";
        });
        System.out.println("defaultGF = " + defaultGF);
    }

1.2Caffeine基于SpringBoot的使用

1.2.1创建配置类

@Component
public class CaffeineConfig {

    @Bean
    public Cache<Long, Item> itemCache(){
        return Caffeine.newBuilder()
                //初始化大小为100
                .initialCapacity(100)
                //最大键容量为10000
                .maximumSize(10_000)
                .build();
    }

    @Bean
    public Cache<Long, ItemStock> stockCache(){
        return Caffeine.newBuilder()
                .initialCapacity(100)
                .maximumSize(10_000)
                .build();
    }
}

1.2.2自动注入后使用

Redisson 缓存空List redis缓存大量数据_redis_02

二、nginx本地缓存

基于nginx实现本地缓存需要借助于OpenResty和Lua

2.1为什么要使用OpenResty和Lua

OpenResty 是一个基于 Nginx的高性能 Web 平台,用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。能在其中使用lua脚本进行逻辑处理,具备下列特点:

  • 具备Nginx的完整功能
  • 基于Lua语言进行扩展,集成了大量精良的 Lua 库、第三方模块
  • 允许使用Lua自定义业务逻辑自定义库

Lua:lua是一种轻量级的脚本语言,可以保证事务的完整性

2.2基于OpenResty和Lua实现本地缓存

OpenResty目录机构

Redisson 缓存空List redis缓存大量数据_Redisson 缓存空List_03


nginx

Redisson 缓存空List redis缓存大量数据_Redisson 缓存空List_04


nginx.conf

Redisson 缓存空List redis缓存大量数据_redis_05

2.2.1 修改nginx.conf文件

文件位置:OpenResty/nginx/conf/nginx.conf
1.设置监听路径

location 监听路径(如:/api/item) {
    # 默认的响应类型
    default_type application/json;
    # 响应结果由lua目录下的某个lua文件来决定,如:lua/item.lua
    content_by_lua_file 文件位置;
}

2.2.2编写lua文件

编写执行业务逻辑的Lua文件,如上面说到的lua/item.lua文件

Redisson 缓存空List redis缓存大量数据_缓存_06

2.2.2.1 获取参数的API

Redisson 缓存空List redis缓存大量数据_数据库_07

2.2.3 将数据缓存到nginx

1.修改配置

Redisson 缓存空List redis缓存大量数据_nginx_08


2.具体逻辑写入执行逻辑的Lua文件中

Redisson 缓存空List redis缓存大量数据_Redisson 缓存空List_09

三、查询redis

OpenResty提供了操作Redis的模块,我们只要引入该模块就能直接使用

3.1引入redis并初始化

-- 导入redis
local redis = require('resty.redis')
-- 初始化redis
local red = redis:new()
red:set_timeouts(1000, 1000, 1000)

3.2封装redis工具类

-- 关闭redis连接的工具方法,其实是放入连接池
local function close_redis(red)
    local pool_max_idle_time = 10000 -- 连接的空闲时间,单位是毫秒
    local pool_size = 100 --连接池大小
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
    if not ok then
        ngx.log(ngx.ERR, "放入redis连接池失败: ", err)
    end
end

-- 查询redis的方法 ip和port是redis地址,key是查询的key
local function read_redis(ip, port, key)
    -- 获取一个连接
    local ok, err = red:connect(ip, port)
    if not ok then
        ngx.log(ngx.ERR, "连接redis失败 : ", err)
        return nil
    end
    -- 查询redis
    local resp, err = red:get(key)
    -- 查询失败处理
    if not resp then
        ngx.log(ngx.ERR, "查询Redis失败: ", err, ", key = " , key)
    end
    --得到的数据为空处理
    if resp == ngx.null then
        resp = nil
        ngx.log(ngx.ERR, "查询Redis数据为空, key = ", key)
    end
    close_redis(red)
    return resp
end

完整的工具类lua文件

--导入redis
local redis = require("resty.redis")
--初始化redis
local red = redis:new()
red:set_timeouts(1000,1000,1000)


-- 关闭redis连接的工具方法,其实是放入连接池
local function close_redis(red)
    local pool_max_idle_time = 10000 -- 连接的空闲时间,单位是毫秒
    local pool_size = 100 --连接池大小
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
    if not ok then
        ngx.log(ngx.ERR, "放入redis连接池失败: ", err)
    end
end

-- 查询redis的方法 ip和port是redis地址,key是查询的key
local function read_redis(ip, port, key)
    -- 获取一个连接
    local ok, err = red:connect(ip, port)
    if not ok then
        ngx.log(ngx.ERR, "连接redis失败 : ", err)
        return nil
    end
    -- 查询redis
    local resp, err = red:get(key)
    -- 查询失败处理
    if not resp then
        ngx.log(ngx.ERR, "查询Redis失败: ", err, ", key = " , key)
    end
    --得到的数据为空处理
    if resp == ngx.null then
        resp = nil
        ngx.log(ngx.ERR, "查询Redis数据为空, key = ", key)
    end
    close_redis(red)
    return resp
end
-- 封装函数,发送http请求,并解析响应
local function read_http(path, params)
    local resp = ngx.location.capture(path,{
        method = ngx.HTTP_GET,
        args = params,
    })
    if not resp then
        -- 记录错误信息,返回404
        ngx.log(ngx.ERR, "http not found, path: ", path , ", args: ", args)
        ngx.exit(404)
    end
    return resp.body
end
-- 将方法导出
local _M = {  
    read_http = read_http,
    read_redis = read_redis
}  
return _M

Redisson 缓存空List redis缓存大量数据_Redisson 缓存空List_10

3.1.3 在执行文件中的使用

Redisson 缓存空List redis缓存大量数据_redis_11

三、查询tomcat

nginx提供了内部API用以发送http请求:

local resp = ngx.location.capture("/path",{
    method = ngx.HTTP_GET,   -- 请求方式
    args = {a=1,b=2},  -- get方式传参数
})

返回的响应内容包括:

  • resp.status:响应状态码
  • resp.header:响应头,是一个table
  • resp.body:响应体,就是响应数据

注意:这里的path是路径,并不包含IP和端口。这个请求会被nginx内部的server监听并处理。

但是我们希望这个请求发送到Tomcat服务器,所以还需要编写一个server来对这个路径做反向代理:

Redisson 缓存空List redis缓存大量数据_Redisson 缓存空List_12


具体的方法已经写好分装到工具类中

-- 封装函数,发送http请求,并解析响应
local function read_http(path, params)
    local resp = ngx.location.capture(path,{
        method = ngx.HTTP_GET,
        args = params,
    })
    if not resp then
        -- 记录错误信息,返回404
        ngx.log(ngx.ERR, "http not found, path: ", path , ", args: ", args)
        ngx.exit(404)
    end
    return resp.body
end