欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot使用redis缓存List
程序员文章站 2022-07-24 12:17:05
一、概述 最近在做性能优化,之前有一个业务是这样实现的: 1.温度报警后第三方通讯管理机直接把报警信息保存到数据库 2.我们在数据库中添加触发器,(BEFORE INSERT)根据这条报警信息处理业务逻辑,在数据库中插入“其他业务数据” 3.前端setTimeout每隔5秒ajax去后端查询“其他业 ......

一、概述

  最近在做性能优化,之前有一个业务是这样实现的:

  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标注的方法不能被本类中其他方法调用,否则缓存不起作用

  修改类方法调用后此问题解决

  错误的方法调用:

SpringBoot使用redis缓存List<Object>

 

  正确的调用:其他类调用该方法

  这其中有个报错: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中存储的数据如下图:

SpringBoot使用redis缓存List<Object>

 

  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