转运公司网站建设,南平市建设局网站,辽宁高速公路建设局网站,网站建设验收合同模板说明#xff1a;项目中使用到Redis#xff0c;正常情况#xff0c;我们会在用户首次查询数据的同时把该数据按照一定命名规则#xff0c;存储到Redis中#xff0c;称为冷启动#xff08;如下图#xff09;#xff0c;这种方式在一些情况下可能会给数据库带来较大的压力…说明项目中使用到Redis正常情况我们会在用户首次查询数据的同时把该数据按照一定命名规则存储到Redis中称为冷启动如下图这种方式在一些情况下可能会给数据库带来较大的压力。
因此我们可以使用另一种方式在项目启动的时候就提前把一些热点数据提前查询并保存到Redis中称为缓存预热。
冷启动 环境准备
例如现在我数据库中有以下用户的信息我想在项目启动的时候就把这些数据存入到数据库中 以下操作在CentOS系统中完成
代码实现
第一步安装OpenResty
OpenResty是基于Nginx的高性能Web平台可方便地搭建能够超过并发、扩展性极高的动态Web应用、Web服务和动态网关。OpenResty具备以下特点 Nginx的完整功能 基于Lua语言集成了大量精良的Lua库、第三方模块 允许使用Lua自定义业务逻辑、自定义库
可参考安装OpenResty这不是本文的重点
第二步配置环境变量
安装完OpenResty之后先配置一下Nginx的环境变量
vi /etc/profile在文件最下面加入下面两行配置
export NGINX_HOME/usr/local/openresty/nginx
export PATH${NGINX_HOME}/sbin:$PATH保存退出在敲下面的命令使配置生效
source /etc/profile第三步修改配置
OpenResty会默认安装在/usr/local/openresty路径下可在该目录在看到一个Nginx目录也就是说OpenResty拥有Nginx的功能。进入到Nginx目录找到配置文件进行自适应修改 如下修改
#user nobody;
worker_processes 1;
error_log logs/error.log;events {worker_connections 1024;
}http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;server {listen 8081;server_name localhost;location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location /50x.html {root html;}# 设置捕捉的请求路径location /user {# 默认的响应类型default_type application/json;# 响应结果由lua/user.lua文件来决定待会儿我们来编写这个文件content_by_lua_file lua/user.lua;}}#lua 模块lua_package_path /usr/local/openresty/lualib/?.lua;;;#c模块 lua_package_cpath /usr/local/openresty/lualib/?.so;;; # 共享字典就是本地缓存名称为user_cache大小150mlua_shared_dict user_cache 150m;
}第四步启动测试
OpenResty启动、停止、重新加载配置的命令与Nginx基本一样敲nginx启动OpenResty
# 启动服务
nginx# 停止服务
ngxin -s stop# 重新加载配置
ngxin -s stop敲IP地址加端口号看到以下内容即为安装成功 第五步启动MySQL、Redis服务
接下来使用Docker启动mysql、redis服务注意使用云服务器需要开放相关端口使用虚拟机需要关闭防火墙
在启动mysql之前先在/tmp目录下创建一个mysql文件夹用于挂载容器的数据和配置文件目录
# 进入/tmp目录
cd /tmp# 创建文件夹
mkdir mysql# 进入mysql目录
cd mysql启动mysql服务账号密码设置为root、123456注意以下命令需要在mysql目录下执行
docker run \-p 3306:3306 \--name mysql \-v $PWD/conf:/etc/mysql/conf.d \-v $PWD/logs:/logs \-v $PWD/data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD123456 \--privileged \-d \mysql:5.7.25敲下面的命令使用docker启动redis服务
docker run --name redis -p 6379:6379 -d redis redis-server --appendonly yes第六步编写代码
服务启动后来进行代码的编写以下是一个简单的SpringBoot项目DAO层使用的是Mybatis-plus只有两个接口一个查询所有用户信息一个根据用户ID查询用户信息。
controller层代码
import java.util.List;RestController
RequestMapping(/user)
public class UserController {Autowiredprivate UserService userService;/*** 查询所有用户信息* return*/GetMapping(list)public ListUser getUsers() {return userService.list();}/*** 根据ID查询用户信息* param id* return*/GetMapping({id})public User getUserById(PathVariable Long id) {return userService.getById(id);}
}pom.xml文件
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdorg.hzy/groupIdartifactIdcaffeine_demo/artifactIdversion1.0-SNAPSHOT/versionpropertiesmaven.compiler.source11/maven.compiler.sourcemaven.compiler.target11/maven.compiler.target/propertiesparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.7.12/versionrelativePath//parentdependencies!--web依赖--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!--lombok--dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/dependency!--mybatis-plus--dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.2/version/dependency!--mysql驱动--dependencygroupIdcom.mysql/groupIdartifactIdmysql-connector-j/artifactIdscoperuntime/scope/dependency!--druid数据库连接池--dependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactIdversion1.2.8/version/dependency!--测试类--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency!--redis依赖--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency!--hutool工具包依赖--dependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.8.6/version/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build
/project配置文件注意IP地址和端口号如果使用的是云服务器注意MySQL连接时需要加上useSSLfalse
server:port: 8081
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://IP地址:3306/db_user?useUnicodetruecharacterEncodingutf-8useSSLfalseserverTimezoneGMT%2B8username: rootpassword: 123456mvc:pathmatch:matching-strategy: ant_path_matcherredis:host: IP地址
mybatis-plus:type-aliases-package: com.hzy.pojoconfig-locations: classpath/mapper/*.xml
第七步启动测试
这些环境搭建完成后应该启动一下项目看是否能正常跑起来
启动正常 测试接口也没问题
第八步缓存预热实现
创建一个RedisHandler类该类的作用是在项目启动时访问数据库查询所有用户信息并存入到数据库中
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hzy.pojo.User;
import com.hzy.service.UserService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import java.util.List;Component
public class RedisHandler implements InitializingBean {Autowiredprivate StringRedisTemplate redisTemplate;Autowiredprivate UserService userService;private static final ObjectMapper MAPPER new ObjectMapper();Overridepublic void afterPropertiesSet() throws Exception {// 初始化缓存// 1.查询所有用户信息ListUser userList userService.list();// 2.放入缓存for (User user : userList) {// 2.1.将user对象序列化为JSONString json MAPPER.writeValueAsString(user);// 2.2.设置key前缀存入redisredisTemplate.opsForValue().set(user:id: user.getId(), json);}}
}第九步编写lua文件
接下来需要编写两个lua文件一个是通用操作common.lua一个是redis的查询操作user.lua内容分别如下
common.lua文件在 /usr/local/openresty/lualib/common.lua 里面使用以下命令编辑如下
vi /usr/local/openresty/lualib/common.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 thenngx.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 thenngx.log(ngx.ERR, 连接redis失败 : , err)return nilend-- 查询redislocal resp, err red:get(key)-- 查询失败处理if not resp thenngx.log(ngx.ERR, 查询Redis失败: , err, , key , key)end--得到的数据为空处理if resp ngx.null thenresp nilngx.log(ngx.ERR, 查询Redis数据为空, key , key)endclose_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-- 记录错误信息返回404ngx.log(ngx.ERR, http查询失败, path: , path , , args: , args)ngx.exit(404)endreturn resp.body
end
-- 将方法导出
local _M { read_http read_http,read_redis read_redis
}
return _Muser.lua文件是自定义的我们在openresty目录下创建一个lua文件夹用于存放该文件夹
cd /usr/local/openrestymkdir luauser.lua文件内容如下
-- 导入common函数库
local common require(common)
local read_http common.read_http
local read_redis common.read_redis
-- 导入cjson库
local cjson require(cjson)
-- 导入共享词典本地缓存
local user_cache ngx.shared.user_cache-- 封装查询函数
function read_data(key, expire, path, params)-- 查询本地缓存local val user_cache:get(key)if not val thenngx.log(ngx.ERR, 本地缓存查询失败尝试查询Redis key: , key)-- 查询redisval read_redis(127.0.0.1, 6379, key)-- 判断查询结果if not val thenngx.log(ngx.ERR, redis查询失败尝试查询http key: , key)-- redis查询失败去查询httpval read_http(path, params)endend-- 查询成功把数据写入本地缓存user_cache:set(key, val, expire)-- 返回数据return val
end-- 获取路径参数
local id ngx.var[1]-- 查询商品信息
local userJSON read_data(user:id: .. id, 1800, /user/ .. id, nil)-- JSON转化为lua的table
local user cjson.decode(userJSON)-- 把user序列化为json 返回结果
ngx.say(cjson.encode(user))第十步重启项目
此时在云服务器上进入redis容器打开redis客户端查看数据内容
# 进入redis容器
docker exec -it redis bash# 打开redis客户端
redis-cli# 查看redis所有的键值
keys *什么都没有nothing 此时我们重启项目 再敲命令查看redis所有值可以看到所有的数据都已经存入了可根据key查询到每一条用户的信息 到此Redis缓存预热已完成
总结
Redis缓存预热的执行是与项目启动一起的不需要用户发送请求是在项目启动时自发的操作。