图形化界面
图形界面的客户端Redis Desktop Manager,可以看到每个redis里边存了多少key
jedis的使用
在content-service的pom中添加对jedis的依赖
建一个测试类cn.e3mall.jedis.JedisTest
public class JedisTest {
/**
* 连接单机版
* @throws Exception
*/
@Test
public void testJedis() throws Exception{
//创建一个链接Jedis对象,参数:host,port
Jedis jedis=new Jedis("192.168.25.130",6379);
//直接使用jedis操作redis,所有jedis的命令都对应一个方法
jedis.set("test123","my first jedis test");
String string=jedis.get("test123");
System.out.println(string);
//关闭连接
jedis.close();
}
/**
* 连接单机版,用连接池
* @throws Exception
*/
@Test
public void testJedisPool() throws Exception{
//创建一个连接池对象,两个参数host,port
JedisPool jedisPool=new JedisPool("192.168.25.130",6379);
//从连接池获得一个连接,就是一个jedis对象
Jedis jedis=jedisPool.getResource();
//使用jedis操作redis
String string=jedis.get("test123");
System.out.println(string);
//关闭连接,每次使用完后关闭连接。连接池回收资源
jedis.close();
//关闭连接池
jedisPool.close();
}
/**
* 操作redis集群
* @throws Exception
*/
@Test
public void testJedisCluster()throws Exception{
//创建一个JedisCluster对象。他自带连接池。有一个参数nodes是一个set类型。set中包含若干个HostAndPort对象
Set<HostAndPort> nodes=new HashSet<>();
nodes.add(new HostAndPort("192.168.25.130",7001));
nodes.add(new HostAndPort("192.168.25.130",7002));
nodes.add(new HostAndPort("192.168.25.130",7003));
nodes.add(new HostAndPort("192.168.25.130",7004));
nodes.add(new HostAndPort("192.168.25.130",7005));
nodes.add(new HostAndPort("192.168.25.130",7006));
JedisCluster jedisCluster=new JedisCluster(nodes);
//直接使用jedisCluster对象操作jedis
jedisCluster.set("test","123");
String string=jedisCluster.get("test");
System.out.println(string);
//关闭JedisCluster对象
jedisCluster.close();
}
}
现在有单机版和集群版,那我们平时开发的时候用单机版还是集群版呢,平时开发的时候用单机版就可以了,没必要弄六七台服务器就为连个redis,等上线的时候再用集群版。
但发现操作集群版和单机版用的代码不一样啊,到时候切换的时候会很麻烦。
怎么才能不改代码呢?
面向接口编程
之前写dao层的时候为啥要先写一个接口再写实现类呢,就是为了以后如果要改实现类的话直接切换过去就行了。
现在定义一个接口,我们用的时候用这个接口,到时候具体用哪个实现类在spring的配置文件中配置一下就行了。以后切换单机和集群的收代码就不用动了,修改配置文件就可以了。
接口JedisClient
public interface JedisClient {
String set(String key, String value);
String get(String key);
Boolean exists(String key);
Long expire(String key, int seconds);
Long ttl(String key);
Long incr(String key);
Long hset(String key, String field, String value);
String hget(String key, String field);
Long hdel(String key, String... field);
}
单机版工具类,JedisClientPool
public class JedisClientPool implements JedisClient {
private JedisPool jedisPool;
public void setJedisPool(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
@Override
public String set(String key, String value) {
Jedis jedis = jedisPool.getResource();
String result = jedis.set(key, value);
jedis.close();
return result;
}
@Override
public String get(String key) {
Jedis jedis = jedisPool.getResource();
String result = jedis.get(key);
jedis.close();
return result;
}
@Override
public Boolean exists(String key) {
Jedis jedis = jedisPool.getResource();
Boolean result = jedis.exists(key);
jedis.close();
return result;
}
@Override
public Long expire(String key, int seconds) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.expire(key, seconds);
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 incr(String key) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.incr(key);
jedis.close();
return result;
}
@Override
public Long hset(String key, String field, String value) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.hset(key, field, value);
jedis.close();
return result;
}
@Override
public String hget(String key, String field) {
Jedis jedis = jedisPool.getResource();
String result = jedis.hget(key, field);
jedis.close();
return result;
}
@Override
public Long hdel(String key, String... field) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.hdel(key, field);
jedis.close();
return result;
}
}
集群版工具类
public class JedisClientCluster implements JedisClient {
private JedisCluster jedisCluster;
public void setJedisCluster(JedisCluster jedisCluster) {
this.jedisCluster = jedisCluster;
}
@Override
public String set(String key, String value) {
return jedisCluster.set(key, value);
}
@Override
public String get(String key) {
return jedisCluster.get(key);
}
@Override
public Boolean exists(String key) {
return jedisCluster.exists(key);
}
@Override
public Long expire(String key, int seconds) {
return jedisCluster.expire(key, seconds);
}
@Override
public Long ttl(String key) {
return jedisCluster.ttl(key);
}
@Override
public Long incr(String key) {
return jedisCluster.incr(key);
}
@Override
public Long hset(String key, String field, String value) {
return jedisCluster.hset(key, field, value);
}
@Override
public String hget(String key, String field) {
return jedisCluster.hget(key, field);
}
@Override
public Long hdel(String key, String... field) {
return jedisCluster.hdel(key, field);
}
}
把这三个类添加到common的cn.e3mall.common.jedis包中
然后把content的pom文件中对jedis的依赖剪切到common的pom中
再在content的cn.e3mall.jedis中建立一个测试类JedisClientTest
public class JedisClientTest {
@Test
public void testJedisClient()throws Exception{
//初始化spring容器
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:spring/applicationContext-redis.xml");
//从容器中获得JedisClient对象
JedisClient jedisClient=applicationContext.getBean(JedisClient.class);
jedisClient.set("mytest","jedisClient");
String string=jedisClient.get("mytest");
System.out.println(string);
}
}
在其spring目录下建立一个配置文件applicationContent-reids.xml,用哪个实现类就把哪个的注释解开,代码不用变。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<!--连接jedis单机版-->
<!--<bean id="jedisClientPool" class="cn.e3mall.common.jedis.JedisClientPool">-->
<!--<property name="jedisPool" ref="jedisPool"/>-->
<!--</bean>-->
<!--<bean id="jedisPool" class="redis.clients.jedis.JedisPool">-->
<!--<constructor-arg name="host" value="192.168.25.130"/>-->
<!--<constructor-arg name="port" value="6379"/>-->
<!--</bean>-->
<!--连接redis集群-->
<bean id="jedisClientCluster" class="cn.e3mall.common.jedis.JedisClientCluster">
<property name="jedisCluster" ref="jedisCluster"/>
</bean>
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg name="nodes">
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.130"></constructor-arg>
<constructor-arg name="port" value="7001"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.130"></constructor-arg>
<constructor-arg name="port" value="7002"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.130"></constructor-arg>
<constructor-arg name="port" value="7003"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.130"></constructor-arg>
<constructor-arg name="port" value="7004"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.130"></constructor-arg>
<constructor-arg name="port" value="7005"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.130"></constructor-arg>
<constructor-arg name="port" value="7006"></constructor-arg>
</bean>
</set>
</constructor-arg>
</bean>
</beans>
在项目中加缓存
推荐加在服务层,因为以后只要调用了这个服务就有缓存。
修改content-service的contentServiceImpl.java中的getContentListByCid方法
@Autowired
private JedisClient jedisClient;
@Value("${CONTENT_LIST}")
private String CONTENT_LIST;
@Override
public List<TbContent> getContentListByCid(long cid) {
/*
查询缓存和添加结果到缓存出错了都不应该报错,应该try-catch
因为不用缓存也可以正常使用。出错之后不应该影响正常的逻辑。但应该打印一下日志
*/
//查询缓存
try {
//如果缓存中有直接响应结果
String json=jedisClient.hget(CONTENT_LIST,cid+"");
if(StringUtil.isNotEmpty(json)){
List<TbContent> list=JsonUtils.jsonToList(json,TbContent.class);
return list;
}
}catch (Exception e){
e.printStackTrace();
}
//如果没有查询数据库
TbContentExample example=new TbContentExample();
TbContentExample.Criteria criteria=example.createCriteria();
criteria.andCategoryIdEqualTo(cid);
List<TbContent> tbContents = contentMapper.selectByExampleWithBLOBs(example);
//把结果添加到缓存
/*
我会将一堆东西都存入redis中。我应该分一下类。
用哈希,大key为CONTENT_LIST,field为cid,value为List<TbContent>
这里把CONTENT_LIST存到了配置文件中resource.properties,然后我要加载这个配置文件,但
<context:property-placeholder location="classpath:conf/*.properties" />,这个只能写一个,
于是就改成了*.properties
*/
try {
jedisClient.hset(CONTENT_LIST,cid+"", JsonUtils.objectToJson(tbContents));
}catch (Exception e){
e.printStackTrace();
}
return tbContents;
}
添加一个conf/resource.properties
#内容列表在redis中缓存的key
CONTENT_LIST=CONTENT_LIST
修改applicationContent-dao.xml
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:conf/*.properties" />
缓存同步
问题,比如我更改了 数据库,但是缓存中没改,下次查还是从缓存中查,那查出来的结果还是以前的。
解决方案:每次更改了数据库后都把相应的缓存中的东西删除。这样缓存中没有了,下次查的时候就会去数据库中查了,然后再添加新的缓存数据。
更改content-service中ContentServiceImpl.java中的addContent方法
@Override
public TaotaoResult addContent(TbContent content) {
//将内容插入到内容表
content.setCreated(new Date());
content.setUpdated(new Date());
//插入到数据库
contentMapper.insert(content);
//缓存同步,删除缓存中对应的数据
jedisClient.hdel(CONTENT_LIST,content.getCategoryId().toString());
return TaotaoResult.ok();
}