SpringBoot使用redis缓存List
一、概述
最近在做性能优化,之前有一个业务是这样实现的:
1.温度报警后第三方通讯管理机直接把报警信息保存到数据库
2.我们在数据库中添加触发器,(before insert)根据这条报警信息处理业务逻辑,在数据库中插入“其他业务数据”
3.前端settimeout每隔5秒ajax去后端查询“其他业务数据”(查库)
优化后这样实现:
两个微服务,消息中间件专门一个服务,接收消息存入数据库,存入redis;业务服务直接从redis获取
1.mqtt订阅通讯管理机报警事件主题
2.发生报警后,java中根据报警信息保存“其他业务数据”到数据库并放入redis缓存
3.前端settimeout每隔5秒ajax去后端查询“其他业务数据”(改为从redis中获取)
4.下一步计划使用websocekt,去掉前端settimeout
二、springboot配置redis
pom.xml、application.properties、@enablecaching等等这些配置就不列出来了,大家可以百度,提一下redistemplate的配置
redistemplate<string, object>可以直接存直接存list、map等,使用jackson2jsonredisserializer序列化,
package ;
import java.lang.reflect.method;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.cache.cachemanager;
import org.springframework.cache.interceptor.keygenerator;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.data.redis.cache.rediscachemanager;
import org.springframework.data.redis.connection.redisconnectionfactory;
import org.springframework.data.redis.core.redistemplate;
import org.springframework.data.redis.serializer.jackson2jsonredisserializer;
import org.springframework.data.redis.serializer.stringredisserializer;
import org.springframework.http.converter.json.jackson2objectmapperbuilder;
import com.fasterxml.jackson.annotation.jsonautodetect;
import com.fasterxml.jackson.annotation.jsoninclude.include;
import com.fasterxml.jackson.annotation.propertyaccessor;
import com.fasterxml.jackson.databind.deserializationfeature;
import com.fasterxml.jackson.databind.objectmapper;
@configuration
public class redisconfiguration {
@bean("jsonrediscache")
public cachemanager cachemanager(@autowired redistemplate<string, object> redistemplate) {
return new rediscachemanager(redistemplate);
}
@bean
public keygenerator keygenerator() {
return new keygenerator() {
@override
public object generate(object target, method method, object... params) {
stringbuilder sb = new stringbuilder();
sb.append(target.getclass().getname());
sb.append(method.getname());
for (object obj : params) {
sb.append(obj.tostring());
}
return sb.tostring();
}
};
}
@bean
public redistemplate<string, object> redistemplate(@autowired redisconnectionfactory cf) {
redistemplate<string, object> redistemplate = new redistemplate<string, object>();
redistemplate.setkeyserializer(new stringredisserializer());
redistemplate.sethashkeyserializer(jackson2jsonredisserializer());
redistemplate.setvalueserializer(jackson2jsonredisserializer());
redistemplate.setconnectionfactory(cf);
redistemplate.afterpropertiesset();
return redistemplate;
}
@suppresswarnings({ "unchecked", "rawtypes" })
@bean
public jackson2jsonredisserializer<object> jackson2jsonredisserializer() {
final jackson2jsonredisserializer<object> jackson2jsonredisserializer = new jackson2jsonredisserializer(
object.class);
final objectmapper objectmapper = jackson2objectmapperbuilder.json().build();
objectmapper.disable(deserializationfeature.fail_on_ignored_properties);
objectmapper.disable(deserializationfeature.fail_on_unknown_properties);
objectmapper.setserializationinclusion(include.non_null);
objectmapper.setvisibility(propertyaccessor.all, jsonautodetect.visibility.any);
objectmapper.enabledefaulttyping(objectmapper.defaulttyping.non_final);
jackson2jsonredisserializer.setobjectmapper(objectmapper);
return jackson2jsonredisserializer;
}
}
三、list对象存入redis遇到的问题
1.@cacheable不起作用问题
刚开始,计划在service层方法上使用注解@cacheable进行缓存,但是redis没有保存,最后百度得到答案:一个类中@cacheable标注的方法不能被本类中其他方法调用,否则缓存不起作用
修改类方法调用后此问题解决
错误的方法调用:
正确的调用:其他类调用该方法
这其中有个报错:no cache could be resolved for 'builder[public java.util.list com.es.service.evralarm.evralarmcacheservice.getevralarmbyaccountid(java.lang.string)] caches=[] | key=''evralarm-'+#accountid' | keygenerator='' | cachemanager='' | cacheresolver='' | condition='' | unless='' | sync='false'' using resolver 'org.springframework.cache.interceptor.simplecacheresolver@7fbfc31a'. at least one cache should be provided per cache operation.
@cacheable注解中添加cachenames即可
package ;
import java.util.hashmap;
import java.util.list;
import java.util.map;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.cache.annotation.cacheable;
import org.springframework.stereotype.service;
import com.es.entity.evralarm.evralarm;
import com.es.repository.evralarm.evralarmdao;
@service
public class evralarmcacheservice {
@autowired
private evralarmdao evralarmdao;
@cacheable(cachenames="evralarms",key="'evralarm-'+#accountid")
public list<evralarm> getevralarmbyaccountid(string accountid){
map<string,object> params = new hashmap<>();
params.put("accountid", accountid);
params.put("limit", 1);
list<evralarm> evralarms = evralarmdao.selectevralarmbyaccount(params);
return evralarms;
}
}
redis中存储的数据如下图:
2.could not resolve type id 'com.es.xx.evralarm.evralarm' into a subtype of [simple type, class java.lang.object]: no such class found
at [source: [b@29a6e242; line: 1, column: 60] (through reference chain: java.util.arraylist[0])
业务服务中原代码:
@cacheable(cachenames="evralarms",key="'evralarm-'+#accountid")
public list<evralarm> selectevralarmbyaccount(string accountid){
map<string,object> params = new hashmap<>();
params.put("accountid", accountid);
return evralarmdao.selectevralarmbyaccount(params);
}
看到一遍文档后明白了,根本原因是:两个微服务,实体类内容虽然一样,但是类路径不一样
四、使用stringredistemplate、redistemplate<string, object>
进一步分析发现使用@cacheable有问题,消息中间件收到第二条报警消息,如果业务系统没有处理第一条报警消息(redis中未删除,同样的key redis中已有一条)则redis中的信息不会更新
应该是:消息中间件每次接收消息,处理后都往redis中更新
使用redistemplate<string, object>直接保存list对象,redis存储中会携带一个类路径信息("com.es.xx.evralarm.evralarm"),业务服务获取的时候无法解析(两个实体类内容相同,类路径不同),只能使用stringredistemplate了,只能是在redis存取前后自己手动对象转json
使用gson直接把要保存的list<>对象转成json再保存到redis
中间件所在服务存入redis:
package com.xx.service.evralarm;
import java.util.hashmap;
import java.util.list;
import java.util.map;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.data.redis.core.stringredistemplate;
import org.springframework.data.redis.core.valueoperations;
import org.springframework.stereotype.service;
import com.es.entity.evralarm.evralarm;
import com.es.repository.evralarm.evralarmdao;
import com.google.gson.gson;
@service
public class evralarmcacheservice {
@autowired
private evralarmdao evralarmdao;
@autowired
private stringredistemplate redistemplate;
public list<evralarm> getevralarmbyaccountid(string accountid){
map<string,object> params = new hashmap<>();
params.put("accountid", accountid);
params.put("limit", 1);
list<evralarm> evralarms = evralarmdao.selectevralarmbyaccount(params);
//redis缓存
valueoperations<string,string> vo = redistemplate.opsforvalue();
gson gson = new gson();
vo.set("evralarm-"+accountid, gson.tojson(evralarms));
return evralarms;
}
}
业务服务从redis中取:
从redis中获取key对应的value,得到string类型的value,使用gson转成list<>对象
查询:
/**
* 根据账户id查询最新告警信息
* */
public list<evralarm> selectevralarmbyaccount(string accountid){
//redis缓存中获取
valueoperations<string,string> vo = redistemplate.opsforvalue();
string value = vo.get("evralarm-"+accountid);
gson gson = new gson();
list<evralarm> evralarms = gson.fromjson(value, list.class);
return evralarms == null ? new arraylist<>() : evralarms;
}
业务操作删除、同时删除redis:
public void deleteaccountevralarm(string accountid, string evralarmid){
map<string, object> querymap = new hashmap<>();
querymap.put("accountid", accountid);
querymap.put("evralarmid", evralarmid);
accountevralarmdao.deletebyprimarykey(querymap);
//redis删除缓存
redistemplate.delete("evralarm-"+accountid);
}
最后问题解决
参考文档:
http://www.mamicode.com/info-detail-2267905.html
https://blog.csdn.net/ranweizheng/article/details/42267803
https://yq.aliyun.com/ziliao/444278
https://blog.csdn.net/hanchao5272/article/details/79051364
上一篇: 027.3 反射技术 简单应用
下一篇: 犀牛拉的大便
推荐阅读
-
redis之django-redis的简单缓存使用
-
springboot中使用redis的方法代码详解
-
缓存管理之MemoryCache与Redis的使用
-
Laravel 下配置 Redis 让缓存、Session 各自使用不同的 Redis 数据库
-
springboot redis-cache 自动刷新缓存
-
【转载】在AspNetCore 中 使用Redis实现分布式缓存
-
Django使用redis缓存服务器
-
windows安装redis缓存使用图文教程
-
SpringBoot使用Redis缓存的实现方法
-
SpringBoot2.0 基础案例(13):基于Cache注解模式,管理Redis缓存