Spring项目中添加Redis服务与缓存同步问题
2017-07-04 09:33
417 查看
一、Redis应用场景
本项目使用redis对图片进行缓存存储,展示图片时,先根据图片ID去Redis缓存中查取,没有再查数据库,同时将该图片set进Redis。
使用spring 容器对Jedis进行管理。
二、Redis与Spring整合
1、新建applicationContext-jedis.xml配置文件,用于配置Redis连接信息和构造方法参数,实例化Jedis对象
[html] view
plain copy
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 连接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="30" />
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="10" />
<!-- 每次释放连接的最大数目 -->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 释放连接的扫描间隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 连接最小空闲时间 -->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
<property name="maxWaitMillis" value="1500" />
<!-- 在获取连接的时候检查有效性, 默认false -->
<property name="testOnBorrow" value="true" />
<!-- 在空闲时检查有效性, 默认false -->
<property name="testWhileIdle" value="true" />
<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="false" />
</bean>
<!-- jedis客户端单机版 -->
<bean id="redisClient" class="redis.clients.jedis.JedisPool">
<constructor-arg name="host" value="192.168.132.128"></constructor-arg>
<constructor-arg name="port" value="6379"></constructor-arg>
</bean>
<!-- 注入单机版bean -->
<bean id="jedisClient" class="com.taotao.rest.dao.JedisClientSingle"/>
<!-- 集群版 -->
<!-- <bean id="redisClient" class="redis.clients.jedis.JedisCluster">
<constructor-arg name="nodes">
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.132.128" />
<constructor-arg name="port" value="7001" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.132.128" />
<constructor-arg name="port" value="7002" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.132.128" />
<constructor-arg name="port" value="7003" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.132.128" />
<constructor-arg name="port" value="7004" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.132.128" />
<constructor-arg name="port" value="7005" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.132.128" />
<constructor-arg name="port" value="7006" />
</bean>
</set>
</constructor-arg>
<constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg>
</bean> -->
<!-- 集群版bean -->
<!-- <bean id="jedisClientCluster" class="com.taotao.rest.dao.JedisClientCluster"/> -->
</beans>
2、编写测试类测试链接并进行数据操作
[html] view
plain copy
public void springSingle() {
ApplicationContext c=new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml");
JedisPool pool=(JedisPool)c.getBean("redisClient");
Jedis jedis =pool.getResource();
String string=jedis.get("key1");
System.out.println(string);
jedis.close();
pool.close();
}
public void springCluster() {
ApplicationContext c=new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml");
JedisCluster cluster=(JedisCluster)c.getBean("redisClient");
String string =cluster.get("key");
System.out.println(string);
cluster.close();
}
三、Redis项目应用
1、单机版和集群版底层抽象
由于Jedis提供的类,单机版考虑到避免每次新建连接,采取RedisPool对象创建redis连接池,在使用getResourse方法获得连接对象。但Cluster集群环境下,可直接通过set、get方法对集群对象进行操作,集群内部节点根据经过hash算法后得到的值匹配存储到指定的节点内存中。
所以为了应对这两种不同的Redis类型,本项目将常用的方法写入接口,提供量靠不同的实现。下面仅展示1个实现
[html] view
plain copy
//接口
public interface JedisClient {
String get(String key);
String set(String key ,String value);
//hash hashkey 对应多个值得key-value
String hget(String hkey,String key);
long hset(String hkey,String key,String value);
//其他常用方法
long incr(String key);
long expire(String key, int second);
long ttl(String key);
long del(String key);
long hdel(String hkey,String key);
}
//集群实现
public class JedisClientCluster implements JedisClient{
@Autowired
private JedisCluster jedisCluster;
@Override
public String get(String key) {
return jedisCluster.get(key);
}
}
//连接池实现
public class JedisClientSingle implements JedisClient{
@Autowired//bytype 注入spring容器中的jedisPool对象
private JedisPool jedisPool;
@Override
public String get(String key) {
Jedis jedis=jedisPool.getResource();
String string=jedis.get(key);
jedis.close();
return string;
}
}
2、在项目中添加缓存处理
在项目中添加缓存处理需要注意,即便缓存操作失败,也不能影响正常业务操作。
[html] view
plain copy
@Autowired
private JedisClient jedisClient;
@Value("${INDEX_CONTENT_REDIS_KEY}")
private String INDEX_CONTENT_REDIS_KEY;
@Override
public List<TbContent> getContentList(long contentId) {
//先查缓存
try {
String result=jedisClient.hget(INDEX_CONTENT_REDIS_KEY, contentId+"");
if(!StringUtils.isEmpty(result))
{
//吧字符串转换成list
List<TbContent> resultList=JsonUtils.jsonToList(result, TbContent);
return resultList;
}
} catch (Exception e) {
// TODO: handle exception
}
/*
* 正常业务操作-查询数据库
* 代码省略
*/
//查询结束后,将数据添加到redis中
try{
//把string转换成字符串
String cacheString=JsonUtils.objectToJson(list);
//这个常量有什么作用?
jedisClient.hset(INDEX_CONTENT_REDIS_KEY, contentId+"", cacheString);
}catch(Exception e1)
{
e1.printStackTrace();
}
return list;
}
3、Redis缓存数据同步问题
不管在任何项目中都使用缓存都存在缓存中数据一致性问题。数据库进行数据更新操作时,如果这些数据在缓存中也有一份,业务设计一般都先查询缓存,没有再查询数据库。这就要求缓存的数据需要实时更新或者在数据库触发更新操作是更新对应数据。
实时更新适用于对数据要求比较高的系统,例如以前做过的一个数据同步系统,传输的数据很重要,为减少数据库压力而添加缓存。这种情况就可以采用timer定时器或者任务调度相关的技术如Quartz,实时根据数据库更新缓存中的数据。
对于数据要求不高的缓存数据同步,则可采用更新哪些数据,同步到缓存即可。下面以此为例,展示一下设计思路。
1、设计编写一个缓存同步服务,在service层更新数据的业务中添加根据修改对象主键,在Redis中删除该对象key-value。
[html] view
plain copy
@Autowired
private JedisClient jedisClient;
@Value("${INDEX_CONTENT_REDIS_KEY}")//常量,用于标识hkey值
private String INDEX_CONTENT_REDIS_KEY;
@Override
public Result syncContent(long contentId) {
try {
jedisClient.hdel(INDEX_CONTENT_REDIS_KEY, contentId+"");//转成字符串
} catch (Exception e) {
e.printStackTrace();
}
return Result.ok();
}
2、在业务逻辑中数据更新时需要进行缓存同步的业务中调用该服务,服务调用方式主要是rest,传入URL即可
[html] view
plain copy
public String insertContent(TbContent content)
{
mapper.insert(content);
//调用缓存同步服务
HttpClientUtil.doget(REST_BASE_URL+REST_CONTENT_SYN_URL);
return "index";
}
本项目使用redis对图片进行缓存存储,展示图片时,先根据图片ID去Redis缓存中查取,没有再查数据库,同时将该图片set进Redis。
使用spring 容器对Jedis进行管理。
二、Redis与Spring整合
1、新建applicationContext-jedis.xml配置文件,用于配置Redis连接信息和构造方法参数,实例化Jedis对象
[html] view
plain copy
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 连接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="30" />
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="10" />
<!-- 每次释放连接的最大数目 -->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 释放连接的扫描间隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 连接最小空闲时间 -->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
<property name="maxWaitMillis" value="1500" />
<!-- 在获取连接的时候检查有效性, 默认false -->
<property name="testOnBorrow" value="true" />
<!-- 在空闲时检查有效性, 默认false -->
<property name="testWhileIdle" value="true" />
<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="false" />
</bean>
<!-- jedis客户端单机版 -->
<bean id="redisClient" class="redis.clients.jedis.JedisPool">
<constructor-arg name="host" value="192.168.132.128"></constructor-arg>
<constructor-arg name="port" value="6379"></constructor-arg>
</bean>
<!-- 注入单机版bean -->
<bean id="jedisClient" class="com.taotao.rest.dao.JedisClientSingle"/>
<!-- 集群版 -->
<!-- <bean id="redisClient" class="redis.clients.jedis.JedisCluster">
<constructor-arg name="nodes">
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.132.128" />
<constructor-arg name="port" value="7001" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.132.128" />
<constructor-arg name="port" value="7002" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.132.128" />
<constructor-arg name="port" value="7003" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.132.128" />
<constructor-arg name="port" value="7004" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.132.128" />
<constructor-arg name="port" value="7005" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.132.128" />
<constructor-arg name="port" value="7006" />
</bean>
</set>
</constructor-arg>
<constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg>
</bean> -->
<!-- 集群版bean -->
<!-- <bean id="jedisClientCluster" class="com.taotao.rest.dao.JedisClientCluster"/> -->
</beans>
2、编写测试类测试链接并进行数据操作
[html] view
plain copy
public void springSingle() {
ApplicationContext c=new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml");
JedisPool pool=(JedisPool)c.getBean("redisClient");
Jedis jedis =pool.getResource();
String string=jedis.get("key1");
System.out.println(string);
jedis.close();
pool.close();
}
public void springCluster() {
ApplicationContext c=new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml");
JedisCluster cluster=(JedisCluster)c.getBean("redisClient");
String string =cluster.get("key");
System.out.println(string);
cluster.close();
}
三、Redis项目应用
1、单机版和集群版底层抽象
由于Jedis提供的类,单机版考虑到避免每次新建连接,采取RedisPool对象创建redis连接池,在使用getResourse方法获得连接对象。但Cluster集群环境下,可直接通过set、get方法对集群对象进行操作,集群内部节点根据经过hash算法后得到的值匹配存储到指定的节点内存中。
所以为了应对这两种不同的Redis类型,本项目将常用的方法写入接口,提供量靠不同的实现。下面仅展示1个实现
[html] view
plain copy
//接口
public interface JedisClient {
String get(String key);
String set(String key ,String value);
//hash hashkey 对应多个值得key-value
String hget(String hkey,String key);
long hset(String hkey,String key,String value);
//其他常用方法
long incr(String key);
long expire(String key, int second);
long ttl(String key);
long del(String key);
long hdel(String hkey,String key);
}
//集群实现
public class JedisClientCluster implements JedisClient{
@Autowired
private JedisCluster jedisCluster;
@Override
public String get(String key) {
return jedisCluster.get(key);
}
}
//连接池实现
public class JedisClientSingle implements JedisClient{
@Autowired//bytype 注入spring容器中的jedisPool对象
private JedisPool jedisPool;
@Override
public String get(String key) {
Jedis jedis=jedisPool.getResource();
String string=jedis.get(key);
jedis.close();
return string;
}
}
2、在项目中添加缓存处理
在项目中添加缓存处理需要注意,即便缓存操作失败,也不能影响正常业务操作。
[html] view
plain copy
@Autowired
private JedisClient jedisClient;
@Value("${INDEX_CONTENT_REDIS_KEY}")
private String INDEX_CONTENT_REDIS_KEY;
@Override
public List<TbContent> getContentList(long contentId) {
//先查缓存
try {
String result=jedisClient.hget(INDEX_CONTENT_REDIS_KEY, contentId+"");
if(!StringUtils.isEmpty(result))
{
//吧字符串转换成list
List<TbContent> resultList=JsonUtils.jsonToList(result, TbContent);
return resultList;
}
} catch (Exception e) {
// TODO: handle exception
}
/*
* 正常业务操作-查询数据库
* 代码省略
*/
//查询结束后,将数据添加到redis中
try{
//把string转换成字符串
String cacheString=JsonUtils.objectToJson(list);
//这个常量有什么作用?
jedisClient.hset(INDEX_CONTENT_REDIS_KEY, contentId+"", cacheString);
}catch(Exception e1)
{
e1.printStackTrace();
}
return list;
}
3、Redis缓存数据同步问题
不管在任何项目中都使用缓存都存在缓存中数据一致性问题。数据库进行数据更新操作时,如果这些数据在缓存中也有一份,业务设计一般都先查询缓存,没有再查询数据库。这就要求缓存的数据需要实时更新或者在数据库触发更新操作是更新对应数据。
实时更新适用于对数据要求比较高的系统,例如以前做过的一个数据同步系统,传输的数据很重要,为减少数据库压力而添加缓存。这种情况就可以采用timer定时器或者任务调度相关的技术如Quartz,实时根据数据库更新缓存中的数据。
对于数据要求不高的缓存数据同步,则可采用更新哪些数据,同步到缓存即可。下面以此为例,展示一下设计思路。
1、设计编写一个缓存同步服务,在service层更新数据的业务中添加根据修改对象主键,在Redis中删除该对象key-value。
[html] view
plain copy
@Autowired
private JedisClient jedisClient;
@Value("${INDEX_CONTENT_REDIS_KEY}")//常量,用于标识hkey值
private String INDEX_CONTENT_REDIS_KEY;
@Override
public Result syncContent(long contentId) {
try {
jedisClient.hdel(INDEX_CONTENT_REDIS_KEY, contentId+"");//转成字符串
} catch (Exception e) {
e.printStackTrace();
}
return Result.ok();
}
2、在业务逻辑中数据更新时需要进行缓存同步的业务中调用该服务,服务调用方式主要是rest,传入URL即可
[html] view
plain copy
public String insertContent(TbContent content)
{
mapper.insert(content);
//调用缓存同步服务
HttpClientUtil.doget(REST_BASE_URL+REST_CONTENT_SYN_URL);
return "index";
}
相关文章推荐
- Spring项目中添加Redis服务与缓存同步问题
- 如何给web项目添加redis服务 JAVA几种缓存技术 ehcache和redis哪个更好
- 关于spring redis 缓存配置错误的问题
- redis+spring+maven构建项目(带有缓存)
- SpringBoot+Mybatis项目使用Redis做Mybatis的二级缓存的方法
- 使用Redis作为SpringBoot项目数据缓存
- Spring-Boot 集成Redis实现查询缓存提高查询效率减轻数据库访问压力(涉及key的添加和删除)
- 【新手总结】在.Net项目中使用Redis作为缓存服务
- Spring boot 关于在Maven下的pom.xml添加对redis的支持报错解决问题
- 【redis】5.spring boot项目中,直接在spring data jpa的Repository层使用redis +redis注解@Cacheable直接在Repository层使用,报错问题处理Null key returned for cache operation
- SSM入门项目-4-Redis解决缓存问题
- SpringBoot+Mybatis项目使用Redis做Mybatis的二级缓存
- Spring-利用InitializingBean接口和zookeeper实现项目初始化缓存以及同步监听
- 在.Net项目中使用Redis作为缓存服务
- 【新手总结】在.Net项目中使用Redis作为缓存服务
- 在.Net项目中使用Redis作为缓存服务
- 【ITOO项目中遇到的问题】——为 MT_HOTEL_SERVICE 添加持久化单元服务失败
- 解决maven同步的eclipse中的springboot项目无法启动问题
- Spring Cloud Spring Boot mybatis分布式微服务云架构(三十五)使用Redis做集中式缓存(1)
- shiro+cas+spring-data-redis实现多系统单点登录和分布式项目的session同步