Spring和Jedis结合配置:
Jedis连接池的配置:
<!-- 连接池配置 --> <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.59.129"></constructor-arg> <constructor-arg name="port" value="6379"></constructor-arg> <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg> </bean> <bean id="jedisClient" class="com.newstyles.rest.dao.Impl.JedisClientSingle"> </bean>
集群版的配置:
<!-- jedis集群版 --> <bean id="redisClient" class="redis.clients.jedis.JedisCluster"> <constructor-arg name="nodes"> <set> <bean id="redis01" class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.59.129"></constructor-arg> <constructor-arg name="port" value="7001"></constructor-arg> </bean> <bean id="redis02" class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.59.129"></constructor-arg> <constructor-arg name="port" value="7002"></constructor-arg> </bean> <bean id="redis03" class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.59.129"></constructor-arg> <constructor-arg name="port" value="7003"></constructor-arg> </bean> <bean id="redis04" class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.59.129"></constructor-arg> <constructor-arg name="port" value="7004"></constructor-arg> </bean> <bean id="redis05" class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.59.129"></constructor-arg> <constructor-arg name="port" value="7005"></constructor-arg> </bean> <bean id="redis06" class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.59.129"></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.newstyles.rest.dao.Impl.JedisClientCluster"> </bean>
当然,上述两种方式只能有一种存在,但是连接池时必须保留的,其规定了Jedis运行时候的相关的设置,在这个测试实例中,我们采用了Jedis单机版的方式完成这个工程,所以,应该讲集群版的注释掉。
单机版测试案例:
public void testSpringJedisSingle() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml"); JedisPool pool = (JedisPool) applicationContext.getBean("redisClient"); Jedis jedis = pool.getResource(); String string = jedis.get("key1"); System.out.println(string); jedis.close(); pool.close(); }
集群版测试案例:
public void testSpringJedisCluster() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml"); JedisCluster jedisCluster = (JedisCluster) applicationContext.getBean("redisClient"); String string = jedisCluster.get("key1"); System.out.println(string); jedisCluster.close(); }
在这里我们测试过后,以Jedis单机版为例,整合到Spring框架中:
Dao层:
首先定义了一个接口,虽说以Jedis单机版为例子,但是我们同时也实现了Redis的集群版的实例的完成:
首先在JedisClient接口中定义了Jedis客户端常用到的操作:
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); }
首先,单机版的Jedis客户端的实现:
/** * 单机版 * @author *********** * */ public class JedisClientSingle implements JedisClient { //需要JedisPool这个类型的对象来进行操作 @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; } }
集群版Redis客户端的实现:
/** * 集群版 * @author *************** * */ 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); } }
两者实现的最主要的区别在于单机版利用的是JedisPool来获取Jedis客户端的实例,而集群版则是利用JedisCluster来获取,获取使用的实例,其方法的包装和调用东较为相似,虽然实现原理不同,但是对于Jedis的使用,使用上述接口定义的操作,则能够满足呀大部分的操作。
Service层:
定义一个接口ContentService接口,其中定义了获取ContentList的操作:
public interface ContentService { List<TbContent> getContentList(long contentCid); }
随后实现这个接口:
@Service public class ContentServiceImpl implements ContentService { @Autowired private JedisClient jedisClient; @Autowired private TbContentMapper contentMapper; @Value("${INDEX_CONTENT_REDIS_KEY}") private String INDEX_CONTENT_REDIS_KEY; @Override public List<TbContent> getContentList(long contentCid) { //缓存的添加不能影响正常的业务逻辑 //1.从缓存中取内容 try{ String reslut = jedisClient.hget(INDEX_CONTENT_REDIS_KEY,contentCid+""); if(!StringUtils.isBlank(reslut)){ List<TbContent> resultList = JsonUtils.jsonToList(reslut, TbContent.class); return resultList; } }catch(Exception e){ e.printStackTrace(); } //根据内容分类id查询列表 TbContentExample example = new TbContentExample(); Criteria criteria = example.createCriteria(); criteria.andCategoryIdEqualTo(contentCid); List<TbContent> list = contentMapper.selectByExample(example); //2.向缓存中添加内容 try{ //将list转换成字符串 String cacheString = JsonUtils.objectToJson(list); jedisClient.hset(INDEX_CONTENT_REDIS_KEY, contentCid+"", cacheString); }catch(Exception e){ e.printStackTrace(); } return list; } }
其中那些使用Value获取的值都统一定义在一个properties文件中:
#首页内容信息在redis中存储保存的key INDEX_CONTENT_REDIS_KEY = INDEX_CONTENT_REDIS_KEY
Controller层:
@Controller @RequestMapping("/content") public class ContentController { @Autowired private ContentService contentService; @RequestMapping("/list/{contentCategoryId}") @ResponseBody public NewstylesResult getContentList(@PathVariable long contentCategoryId){ try{ List<TbContent> list = contentService.getContentList(contentCategoryId); return NewstylesResult.ok(list); }catch(Exception e){ e.printStackTrace(); return NewstylesResult.build(500, ExceptionUtil.getStackTrace(e)); } } }
测试一下:
可以看到,发布的rest服务能够正常的调用。
缓存同步:
先观察下面的流程图:
可以看到,我们所调用的服务都经过rest层,那么如何才能够实现在整个工程中既不影响正常的业务逻辑,又能够很好的进行同步更新,也是实说,在taotao-rest工程中发布一个服务。当后台管理系统修改内容后,调用此服务,同步缓存。要求是当后台中对某些有关于redis缓存中的内容进行更新时,也要更新redis缓存。
Dao层:
使用上一步实现的JedisClient来实现(以单机版Jedis为例)。
Service层:
接收内容分类id,调用dao删除redis中对应的hash中key为分类id的项。
参数:内容分类id
返回值:NewstylesResult
定义一个RedisService接口,定义一个同步的操作:
public interface RedisService { NewstylesResult syncContent(long contentCid); }
实现这个接口:
@Service public class RedisServiceImpl implements RedisService { @Autowired private JedisClient jedisClient; @Value("${INDEX_CONTENT_REDIS_KEY}") private String INDEX_CONTENT_REDIS_KEY; @Override public NewstylesResult syncContent(long contentCid) { try{ jedisClient.hdel(INDEX_CONTENT_REDIS_KEY, contentCid+""); }catch(Exception e){ e.printStackTrace(); return NewstylesResult.build(500, ExceptionUtil.getStackTrace(e)); } return NewstylesResult.ok(); } }
Controller层:
/** * 缓存同步Controller * @author *********** * */ @Controller @RequestMapping("/cache/sync") public class RedisController { @Autowired private RedisService redisService; @RequestMapping("/content/{contentCid}") @ResponseBody public NewstylesResult contentCacheSync(@PathVariable long contentCid ){ NewstylesResult result = redisService.syncContent(contentCid); return result; } }
测试后,发现服务可以进行,但是我们还需要我们的后台当发生适应的条件时进行调用此服务,所以我们还需要在后台的工程中添加一个调用的服务,此服务的调用则是在后台的插入的方法中添加的:所以当有变化的时候删除原来的缓存,添加变化后的新元素,删除的时候也会调用,删除原来的缓存。newstyles-manager-web工程Service层中的有关缓存的修改:
首先插入:
@Override public NewstylesResult 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 NewstylesResult.ok(); }删除原来的缓存:
@Override public NewstylesResult deleteContentById(String ids){ String a[] =ids.split(","); if(a.length != 0){ for(int i=0;i<a.length;i++){ TbContent tbContent = contentMapper.selectByPrimaryKey(Long.parseLong(a[i].trim())); try { pictureService.deleteFilesPic(tbContent.getPic()); pictureService.deleteFilesPic(tbContent.getPic2()); HttpClientUtil.doGet(REST_BASE_URL+REST_CONTENT_SYNC_URL + tbContent.getCategoryId()); } catch (Exception e) { e.printStackTrace(); } contentMapper.deleteByPrimaryKey(Long.parseLong(a[i].trim())); } }else{ return NewstylesResult.build(500,"Misserror"); } return NewstylesResult.ok(); }此时完成了对于展示大广告业务的Redis的缓存的存储和更新。