「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」
1、问题引入
这里有两个问题,一个时服务重启时,如何将需要同步到redis中的数据进行预加载。另一个是,针对数据结构较为复杂的redis数据,数据库数据更新时,如何对redis中的数据进行更新。显然,这两部分都应该抽象成一个公共工具类,或者一个管理功能,而不是让每个开发人员重复这些代码。下面就先为大家介绍下,服务启动时redis热点数据的预加载实现。
2、Redis数据预加载实现代码
@Component
public class PreloadCache implements CommandLineRunner {
@Autowired
private ComnDao comnDao;
//根据不同的服务名区分不同的服务的热点数据(每个服务只预加载自己服务的redis热点数据)
@Value("#{'${spring.application.name}'.trim()}")
private String serverName;
@Override
public void run(String... args) throws Exception {
log.info("预加载缓存开始执行");
preloadCache();
log.info("预加载缓存执行完毕");
}
public void preloadCache() throws Exception{
Map<String, Object> params = new HashMap<String, Object>();
params.put("servername", serverName);
String sql = "select * from cache_preload where servername = $S{servername}";
//查询本服务需要预加载的缓存
SqlResult result = comnDao.sqlQuery(sql, SqlUtil.getDataSourceSys(), params);
while(result.next()) {
String queryid = result.getString("queryid");
//数据库查询结果处理器,对于复杂结构的redis数据需要进一步处理
String queryHandler = result.getString("queryhandler");
if(!queryid.isEmpty()){
loadCacheByQueryidConfig(result.getString("cachekey"), queryid, result.getString("cacheprefix"), result.getString("noncachekey"), result.getString("cachevalhandler"), result.getString("valuetype"));
}else if(!queryHandler.isEmpty()) {
loadCacheByHandlerConfig(result.getString("cachekey"), queryHandler, result.getString("cacheprefix"), result.getString("noncachekey"), result.getString("cachevalhandler"), result.getString("valuetype"));
}else {
log.error("缓存配置异常,查询id和查询处理器必须配置一个");
throw new PromptException("缓存配置异常,查询id和查询处理器必须配置一个");
}
}
}
/**
* 根据queryid配置加载缓存
* @param cacheKey 缓存键key
* @param queryId 缓存值查询id
* @param cachePrefix 缓存前缀
* @param nonCacheKey 无需缓存键
* @param cacheValHandler 缓存值处理器
* @param redisValueType 缓存值类型
* @throws Exception
*/
private void loadCacheByQueryIdidConfig(String cacheKey, String queryId, String cachePrefix, String nonCacheKey, String cacheValHandler, String redisValueType) throws Exception {
SqlResult result = comnDao.query(queryId, new HashMap<String, Object>());
CacheAble cacheAble = SysBeans.getCacheAble();
List<SqlRow> rows = result.getRows();
for(SqlRow row : rows) {
Map<String, Object> val = new HashMap<String, Object>();
if(!cacheValHandler.isEmpty()) {
val = CacheUtil.cacheValHandler(nonCacheKey, (Map<String, Object>) KInvoke.invokeDistributedHander(cacheValHandler, row));
}else {
val = JSON.parseObject(JSON.toJSONString(CacheUtil.cacheValHandler(nonCacheKey, row)), val.getClass());
}
String key = CacheUtil.cacheKeyHandler(cacheKey, cachePrefix, val);
if(cacheAble.existsKey(key, true)) {
cacheAble.delCache(true, key);
}
if(RedisValueTypeEntity.HASH.equals(redisValueType)) {
cacheAble.setMap(key, true, CacheUtil.cacheMapValHandler(val));
}else {
cacheAble.setCache(key, JSON.toJSONString(val), true);
}
}
}
/**
* 根据处理器配置加载缓存
* @param cacheKey 缓存键key
* @param queryHandler 缓存查询处理器
* @param cachePrefix 缓存前缀
* @param nonCacheKey 无需缓存键
* @param cacheValHandler 缓存值处理器
* @param redisValueType 缓存值类型
* @throws Exception
*/
private void loadCacheByHandlerConfig(String cacheKey, String queryHandler, String cachePrefix, String nonCacheKey, String cacheValHandler, String redisValueType) throws Exception {
List<Map<String, Object>> rows = (List<Map<String, Object>>) KInvoke.invokeDistributedHander(queryHandler, comnDao);
CacheAble cacheAble = SysBeans.getCacheAble();
for(Map<String, Object> map : rows) {
if(!cacheValHandler.isEmpty()) {
map = (Map<String, Object>) KInvoke.invokeDistributedHander(cacheValHandler, map);
}
String key = CacheUtil.cacheKeyHandler(cacheKey, cachePrefix, map);
map = CacheUtil.cacheValHandler(nonCacheKey, map);
if(cacheAble.existsKey(key, true)) {
cacheAble.delCache(true, key);
}
if(RedisValueTypeEntity.HASH.equals(redisValueType)) {
cacheAble.setMap(key, true, CacheUtil.cacheMapValHandler(map));
}else {
cacheAble.setCache(key, JSON.toJSONString(map), true);
}
}
}
}
//缓存工具,定义缓存的存储结构
public class CacheUtil {
public static String getUuidKey (String key){
return RequestSupport.getLocalRequest().getAttribute(FilterAttrConfig.UUID_STR)+":"+key;
}
/**
* 缓存键处理器
* @param cacheKey 缓存键
* @param cacheVal 缓存值
* @return
*/
public static String cacheKeyHandler(String cacheKey, Map<String, Object> cacheVal) {
StringBuffer cacheK = new StringBuffer();
String[] cacheKeys = cacheKey.split("\\+");
for(String key : cacheKeys) {
cacheK.append(cacheVal.get(key));
}
return cacheK.toString();
}
/**
* 缓存键处理器
* @param cacheKey 缓存键
* @param cachePrefix 缓存前缀
* @param cacheVal 缓存值
* @return
*/
public static String cacheKeyHandler(String cacheKey, String cachePrefix, Map<String, Object> cacheVal) {
StringBuffer cacheK = new StringBuffer(cachePrefix);
return cacheK.append(cacheKeyHandler(cacheKey, cacheVal)).toString();
}
/**
* 缓存值处理器
* @param nonCacheKey 不缓存键
* @param cacheVal 缓存值
* @return
*/
public static Map<String, Object> cacheValHandler(String nonCacheKey, Map<String, Object> cacheVal) {
String[] nonCacheKeys = nonCacheKey.split(",");
ConcurrentHashMap<String, Object> retMap = new ConcurrentHashMap<String, Object>();
for(String key : cacheVal.keySet()) {
if(null != cacheVal.get(key)) {
retMap.put(key, cacheVal.get(key));
}
}
for(String key : nonCacheKeys) {
//增加对模糊键的处理 XXX结尾、XXX开头、包含XXX 使用#做通配符
String containsPattern = ".*\\#.*";
String atPattern = "^\\#.*";
String endPattern = ".*\\#$";
if(key.matches(endPattern)){
//XXX开头
String regex = "^" + key.replaceAll("#", "") + ".*";
Set<String> keys = retMap.keySet();
for(String valKey : keys) {
if(valKey.matches(regex)) {
retMap.remove(valKey);
}
}
}else if(key.matches(atPattern)) {
//XXX结尾
String regex = ".*" + key.replaceAll("#", "") + "$";
Set<String> keys = retMap.keySet();
for(String valKey : keys) {
if(valKey.matches(regex)) {
retMap.remove(valKey);
}
}
}else if(key.matches(containsPattern)) {
//包含XXX
String regex = "^" + key.substring(0, key.indexOf("#")) + ".*" + key.substring(key.indexOf("#"), key.length()) + "$";
Set<String> keys = retMap.keySet();
for(String valKey : keys) {
if(valKey.matches(regex)) {
retMap.remove(valKey);
}
}
} else {
retMap.remove(key);
}
}
return retMap;
}
/**
* 缓存map值处理器
* @param cacheVal 缓存值
* @return
*/
public static Map<String, String> cacheMapValHandler(Map<String, Object> cacheVal) {
return cacheVal.entrySet().stream()
.filter(entry -> entry.getValue() != null)
.collect(Collectors.toMap(Entry::getKey, entry -> entry.getValue().toString()));
}
}
好了、本期就先介绍到这里,有什么需要交流的,大家可以随时私信我。😊