第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第七天】(redis缓存)
https://pan.baidu.com/s/1bptYGAb#list/path=%2F&parentPath=%2Fsharelink389619878-229862621083040
第04项目:淘淘商城(SpringMVC+Spring+Mybatis) 的学习实践总结【第五天】
第04项目:淘淘商城(SpringMVC+Spring+Mybatis) 的学习实践总结【第六天】
第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第七天】(redis缓存)
第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第八天】(solr服务器搭建、搜索功能实现)
第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第七天】(redis缓存)
课程计划:
1、内容管理功能添加缓存。
a) 使用redis做缓存工具
b) 实现系统高可用,redis需要做主备。使用redis做分片集群。Redis集群的搭建。
c) 向业务逻辑中添加缓存。
2.1 访问taotao-portal服务406问题
1、查看是否Jackson包是否存在。
2、如果后缀是html是不能响应json数据的。需要修改后缀名。
2.2 Post乱码问题
produces=MediaType.TEXT_PLAIN_VALUE+";charset=utf-8"
3 使用redis做缓存安装的步骤详情见点击跳转
Redis 是完全开源免费的,是一个高性能的key-value数据库。
Redis 与其他 key - value 缓存产品有以下三个特点:
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
- Redis支持数据的备份,即master-slave模式的数据备份。
2.7.3 Jedis客户端
使用前先引入依赖坐标
版本可以选用3.1.0
<!-- Redis客户端 --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>${jedis.version}</version> </dependency>
RedisDesktopManager的使用注意事项:
第一步:在redis的配置文件(redis.conf)里面是否设置了requirepass 表示连接的密码,如果没有设置,则连接不上。或者关闭保护模式。
第二步:在redis的配置文件里面,在第69行左右是要已经把bind 127.0.0.1 给注释掉。如果没有,连接不上,。
package com.taotao.rest.jedis; import java.util.HashSet; import org.junit.Test; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPool; public class JedisTest { @Test public void testJedisSingle() { //创建一个jedis的对象 Jedis jedis = new Jedis("192.168.179.128",6379); jedis.auth("12345678"); //调用jedis对象的方法,方法名称和redis的命令一致 jedis.set("key1", "testJedisSingle"); String string = jedis.get("key1"); System.out.println(string); //关闭jedis jedis.close(); } /** * 使用连接池 */ @Test public void testJedisPool() { //创建jedis连接池 JedisPool pool = new JedisPool("192.168.179.128", 6379); //从连接池中获得Jedis对象 Jedis jedis = pool.getResource(); jedis.auth("12345678"); String string = jedis.get("key1"); System.out.println(string); //关闭jedis对象 jedis.close(); pool.close(); } //测试集群redis @Test public void testJedisCluster() throws Exception { HashSet<HostAndPort> nodes = new HashSet<>(); nodes.add(new HostAndPort("192.168.179.128", 7001)); nodes.add(new HostAndPort("192.168.179.128", 7002)); nodes.add(new HostAndPort("192.168.179.128", 7003)); nodes.add(new HostAndPort("192.168.179.128", 7004)); nodes.add(new HostAndPort("192.168.179.128", 7005)); nodes.add(new HostAndPort("192.168.179.128", 7006)); JedisCluster cluster = new JedisCluster(nodes); cluster.set("key2", "kang"); String string = cluster.get("key2"); System.out.println(string); cluster.close(); } }
利用spring的IOC自动注入jedis的bean(参考资料:redis设置密码以及jedisPool设置密码)
redis.properties
#redis单机版JedisPool配置信息 redis.hostName=192.168.179.128 redis.port=6379 redis.timeout=60 redis.password=12345678
application-jedis.xml
<?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"> <!-- 加载属性文件 --> <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true" /> <!-- 连接池配置 --> <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="jedisPool" class="redis.clients.jedis.JedisPool"> <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg> <constructor-arg name="host" value="${redis.hostName}"></constructor-arg> <constructor-arg name="port" value="${redis.port}"></constructor-arg> <constructor-arg name="timeout" value="${redis.timeout}" type="int"/> <constructor-arg name="password" value="${redis.password}"></constructor-arg> </bean> <bean id="jedisClientSingle" class="com.taotao.rest.dao.impl.JedisClientSingle"/> <!-- jedis集群版配置 --> <!-- <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.179.128"></constructor-arg> <constructor-arg name="port" value="7001"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.179.128"></constructor-arg> <constructor-arg name="port" value="7002"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.179.128"></constructor-arg> <constructor-arg name="port" value="7003"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.179.128"></constructor-arg> <constructor-arg name="port" value="7004"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.179.128"></constructor-arg> <constructor-arg name="port" value="7005"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.179.128"></constructor-arg> <constructor-arg name="port" value="7006"></constructor-arg> </bean> </set> </constructor-arg> <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg> </bean> <bean id="jedisClientCluster" class="com.taotao.rest.dao.impl.JedisClientCluster" /> --> </beans>
测试类的test方法
@Test public void testSpringJedisCluster() throws IOException { //1.获取spring的IOC容器对象 ApplicationContext applicationContext = new ClassPathXmlApplicationContext( "classpath:spring/applicationContext-*.xml"); //2.从IOC容器对象中取出bean的id为jedisCluster的那个 JedisCluster jedisCluster = (JedisCluster) applicationContext.getBean("redisClient"); jedisCluster.set("name", "kang"); String value = jedisCluster.get("name"); System.out.println(value); //console打印出kang就测试通过 /* 最后要关闭jedis客户端 */ jedisCluster.close(); }
/taotao-rest/src/main/java/com/taotao/rest/dao/JedisClient.java
package com.taotao.rest.dao; public interface JedisClient { String get(String key); String set(String key,String 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 ); }
package com.taotao.rest.dao.impl; import org.springframework.beans.factory.annotation.Autowired; import com.taotao.rest.dao.JedisClient; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; public class JedisClientSingle implements JedisClient { @Autowired private JedisPool jedisPool; @Override public String get(String key) { Jedis jedis = jedisPool.getResource(); String string = jedis.get(key); jedis.close(); return string; } @Override public String set(String key, String value) { Jedis jedis = jedisPool.getResource(); String string = jedis.set(key, value); jedis.close(); return string; } @Override public String hget(String hkey, String key) { Jedis jedis = jedisPool.getResource(); String string = jedis.hget(hkey, key); jedis.close(); return string; } @Override public long hset(String hkey, String key, String value) { Jedis jedis = jedisPool.getResource(); Long result = jedis.hset(hkey, key, value); jedis.close(); return result; } @Override public long incr(String key) { Jedis jedis = jedisPool.getResource(); Long result = jedis.incr(key); jedis.close(); return result; } @Override public long expire(String key, int second) { Jedis jedis = jedisPool.getResource(); Long result = jedis.expire(key, second); jedis.close(); return result; } @Override public long ttl(String key) { Jedis jedis = jedisPool.getResource(); Long result = jedis.ttl(key); jedis.close(); return result; } @Override public long del(String key) { Jedis jedis = jedisPool.getResource(); Long result = jedis.del(key); jedis.close(); return result; }
/taotao-rest/src/main/java/com/taotao/rest/dao/impl/JedisClientSingle.java
package com.taotao.rest.dao.impl; import org.springframework.beans.factory.annotation.Autowired; import com.taotao.rest.dao.JedisClient; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; public class JedisClientSingle implements JedisClient { @Autowired private JedisPool jedisPool; @Override public String get(String key) { Jedis jedis = jedisPool.getResource(); String string = jedis.get(key); jedis.close(); return string; } @Override public String set(String key, String value) { Jedis jedis = jedisPool.getResource(); String string = jedis.set(key, value); jedis.close(); return string; } @Override public String hget(String hkey, String key) { Jedis jedis = jedisPool.getResource(); String string = jedis.hget(hkey, key); jedis.close(); return string; } @Override public long hset(String hkey, String key, String value) { Jedis jedis = jedisPool.getResource(); Long result = jedis.hset(hkey, key, value); jedis.close(); return result; } @Override public long incr(String key) { Jedis jedis = jedisPool.getResource(); Long result = jedis.incr(key); jedis.close(); return result; } @Override public long expire(String key, int second) { Jedis jedis = jedisPool.getResource(); Long result = jedis.expire(key, second); jedis.close(); return result; } @Override public long ttl(String key) { Jedis jedis = jedisPool.getResource(); Long result = jedis.ttl(key); jedis.close(); return result; } @Override public long del(String key) { Jedis jedis = jedisPool.getResource(); Long result = jedis.del(key); jedis.close(); return result; } @Override public long hdel(String hkey,String key) { Jedis jedis = jedisPool.getResource(); Long result = jedis.hdel(hkey,key); jedis.close(); return result; } }
/taotao-rest/src/main/java/com/taotao/rest/dao/impl/JedisClientCluster.java
package com.taotao.rest.dao.impl; import org.springframework.beans.factory.annotation.Autowired; import com.taotao.rest.dao.JedisClient; import redis.clients.jedis.JedisCluster; public class JedisClientCluster implements JedisClient { @Autowired private JedisCluster jedisCluster; @Override public String get(String key) { return jedisCluster.get(key); } @Override public String set(String key, String value) { return jedisCluster.set(key, value); } @Override public String hget(String hkey, String key) { return jedisCluster.hget(hkey, key); } @Override public long hset(String hkey, String key, String value) { return jedisCluster.hset(hkey, key, value); } @Override public long incr(String key) { return jedisCluster.incr(key); } @Override public long expire(String key, int second) { return jedisCluster.expire(key, second); } @Override public long ttl(String key) { return jedisCluster.ttl(key); } @Override public long del(String key) { return jedisCluster.del(key); } @Override public long hdel(String hkey,String key) { return jedisCluster.hdel(hkey,key); } }
package com.taotao.rest.dao.impl; import org.springframework.beans.factory.annotation.Autowired; import com.taotao.rest.dao.JedisClient; import redis.clients.jedis.JedisCluster; public class JedisClientCluster implements JedisClient { @Autowired private JedisCluster jedisCluster; @Override public String get(String key) { return jedisCluster.get(key); } @Override public String set(String key, String value) { return jedisCluster.set(key, value); } @Override public String hget(String hkey, String key) { return jedisCluster.hget(hkey, key); } @Override public long hset(String hkey, String key, String value) { return jedisCluster.hset(hkey, key, value); } @Override public long incr(String key) { return jedisCluster.incr(key); } @Override public long expire(String key, int second) { return jedisCluster.expire(key, second); } @Override public long ttl(String key) { return jedisCluster.ttl(key); } @Override public long del(String key) { return jedisCluster.del(key)
5.3 把缓存添加到业务逻辑
注意:缓存的添加不能影响正常的业务逻辑。
/** * 内容管理 * @author kangy * */ @Service public class ContentServiceImpl implements ContentService { @Autowired private TbContentMapper contentMapper; //在XML配置文件中注入了bean @Autowired private JedisClient JedisClient; //从.properties文件读取常量 @Value("${INDEX_CONTENT_REDIS_KEY}") private String INDEX_CONTENT_REDIS_KEY; @Override public List<TbContent> getContentList(long contentCid) { //b从缓存中取内容 try { String result = JedisClient.hget(INDEX_CONTENT_REDIS_KEY, contentCid + ""); if (!StringUtils.isBlank(result)) { //把字符串转换成list List<TbContent> resultList = JsonUtils.jsonToList(result, TbContent.class); return resultList; } } catch (Exception e) { e.printStackTrace(); } //a根据内容分类id查询内容列表 TbContentExample example = new TbContentExample(); TbContentExample.Criteria criteria = example.createCriteria(); criteria.andCategoryIdEqualTo(contentCid); //a执行查询 List<TbContent> list = contentMapper.selectByExampleWithBLOBs(example); //b向缓存中添加内容 try { //把list转换成字符串 String cacheString = JsonUtils.objectToJson(list); JedisClient.hset(INDEX_CONTENT_REDIS_KEY, contentCid + "", cacheString); } catch (Exception e) { e.printStackTrace(); } //a返回结果 return list; } }
课后作业:商品类目展示添加缓存
6 缓存同步
当后台管理系统,修改内容之后需要通知redis把修改的内容对应的分类id的key删除。
6.1 添加缓存后的系统架构
6.2 解决方案
在taotao-rest工程中发布一个服务。当后台管理系统修改内容后,调用此服务,同步缓存。
6.3 Dao层
使用JedisClient接口对应的实现类。
6.4 Service层
接收内容分类id,调用dao删除redis中对应的hash中key为分类id的项。
参数:内容分类id
返回值:TaotaoResult
com.taotao.rest.service.impl.RedisServiceImpl
View Codepackage com.taotao.rest.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.taotao.common.pojo.TaotaoResult; import com.taotao.common.utils.ExceptionUtil; import com.taotao.rest.dao.JedisClient; import com.taotao.rest.service.RedisService; @Service public class RedisServiceImpl implements RedisService { @Autowired private JedisClient jedisClient; //从.properties文件读取常量 @Value("${INDEX_CONTENT_REDIS_KEY}") private String INDEX_CONTENT_REDIS_KEY; @Override public TaotaoResult syncContent(long contentCid) { try { jedisClient.hdel(INDEX_CONTENT_REDIS_KEY, contentCid + ""); } catch (Exception e) { e.printStackTrace(); return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e)); } return TaotaoResult.ok(); } }
6.5 Controller层
接收内容分类id,调用Service返回taotaoResult。
com.taotao.rest.controller.RedisController
View Code@RestController @RequestMapping("/cache/sync") public class RedisController { @Autowired private RedisService redisService; @RequestMapping("/content/{contentCid}") public TaotaoResult contentCacheSync(@PathVariable Long contentCid) { TaotaoResult result = redisService.syncContent(contentCid); return result; } }
6.6 同步缓存服务的调用
需要在后台管理系统中添加一个服务调用的逻辑。当修改内容信息后,需要调用此服务同步缓存。
com.taotao.service.impl.ContentServiceImpl
//内容管理 @Service public class ContentServiceImpl implements ContentService { @Value("${REST_BASE_URL}") private String REST_BASE_URL; @Value("REST_CONTENT_SYNC_URL") private String REST_CONTENT_SYNC_URL; @Autowired private TbContentMapper contentMapper; @Override public TaotaoResult insertContent(TbContent content) { // 补全pojo内容 content.setCreated(new Date()); content.setUpdated(new Date()); contentMapper.insert(content); // 添加缓存同步逻辑 try { HttpClientUtil.doGet(REST_BASE_URL + REST_CONTENT_SYNC_URL + content.getCategoryId()); } catch (Exception e) { // 通知管理员处理异常 e.printStackTrace(); } return TaotaoResult.ok(); }
参考资料:
redis学习笔记(二)JedisCluster + redis 3.2.5集群
redis-4.0.14.gem集群创建用到的脚本 下载地址
Redis免费客户端 Another Redis DeskTop Manager 下载地址
RedisDesktopManager连接不上redis的解决方法
redis.clients. NOAUTH Authentication required 解决办法
end