redis是一个key-value 存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list( 链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些 数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。 Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了 memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。 [1] Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。 redis的官网地址,非常好记,是redis.io。(特意查了一下,域名后缀io属于国家域名,是british Indian Ocean territory,即英属印度洋领地) 目前,Vmware在资助着redis项目的开发和维护。
一、配置applicationContext-redis.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd ">
<!-- 加载*.properties属性文件 -->
<context:property-placeholder location="classpath:*.properties" />
<context:component-scan base-package="com.cares.mbsis.redis.*"/>
<!-- 分片池 begin
Redis在3.x版本前是不支持服务端集群的,所以Jedis通过采用一致性哈稀分片算法(Shard),将不同的key分配到不同的redis server上,以达到横向扩展的目的。-->
<!-- 连接池配置 最大连接数 最大空闲数 最长等待时间 连接是否可用-->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.maxActive}" />
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
<property name="testOnReturn" value="${redis.testOnReturn}" />
<property name="blockWhenExhausted" value="${redis.blockWhenExhausted}" />
</bean>
<bean id="jedisShardInfo1" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg index="0" value="${redis.host}" />
<constructor-arg index="1" value="${redis.port}" type="int" />
<constructor-arg index="2" value="${redis.instance}"/>
</bean>
<bean id="jedisShardInfo2" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg index="0" value="${redis.host2}" />
<constructor-arg index="1" value="${redis.port2}" type="int"/>
<constructor-arg index="2" value="${redis.instance2}"/>
</bean>
<bean id="jedisShardInfo3" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg index="0" value="${redis.host3}" />
<constructor-arg index="1" value="${redis.port3}" type="int"/>
<constructor-arg index="2" value="${redis.instance3}"/>
</bean>
<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">
<constructor-arg index="0" ref="jedisPoolConfig" />
<constructor-arg index="1">
<list>
<ref bean="jedisShardInfo1"/>
<ref bean="jedisShardInfo2"/>
<ref bean="jedisShardInfo3"/>
</list>
</constructor-arg>
</bean>
<!--同步sentinel的信息,将主从信息同步到客户端来-->
<bean class="redis.clients.jedis.JedisSentinelPool">
<constructor-arg index="0" value="mymaster"/>
<constructor-arg index="1">
<set>
<value>${redis.sentinel.host}</value>
</set>
</constructor-arg>
<constructor-arg index="2" ref="jedisPoolConfig"/>
</bean>
<!-- 分片池 end -->
</beans>
二、redis.properties配置文件
#redis settings
#一个pool可分配多少个jedis实例
redis.maxActive=2048
#一个pool最多可有多少个状态为idle(空闲)的jedis实例
redis.maxIdle=300
#当borrow一个jedis实例时,最大的等待时间,如果超过了等待时间,则直接抛出JedisConnectionException
redis.maxWaitMillis=10000
#在borrow一个jedis实例时,是否提前进行validate操作,如果为true,则得到的jedis实例时可用的
redis.testOnBorrow=true
#连接耗尽时是否阻塞,false报异常,true阻塞直到超时,默认为true
redis.testOnReturn=false
redis.blockWhenExhausted=false
redis.sentinel.host=192.168.22.128:26379
#ip,部署的redis所在服务器的ip
redis.host=192.168.22.128
#端口号
redis.port=6379
#
redis.instance=instance:01
#redis设置的需要认证的密码
redis.pass=mbsis
redis.host2=192.168.22.128
redis.port2=6380
redis.instance2=instance:02
redis.pass2=mbsis
redis.host3=192.168.22.128
redis.port3=6381
redis.instance3=instance:03
redis.pass3=mbsis
三、RedisService.java操作Redis
package com.cares.mbsis.redis.service;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.cares.mbsis.redis.util.ProtobuffSerializationUtil;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
@Service("redisService")
public class RedisService {
private static final Logger log = Logger.getLogger(RedisService.class);
@Autowired
private ShardedJedisPool shardedJedisPool;
/**
* @param key
* @param value
* @return
* 设置单个值
*/
public String set(String key,Object value){
String result = null;
ShardedJedis shardedJedis = shardedJedisPool.getResource();
if(shardedJedis == null){
return result;
}
try {
result = shardedJedis.set(key,new String(ProtobuffSerializationUtil.serialize(value),"ISO-8859-1"));
} catch (Exception e) {
log.error(e.getMessage(),e);
} finally{
shardedJedis.close();
}
return result;
}
/**
* @param key
* @param value
* @return
* 设置List
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public String setList(String key,List value){
String result = null;
ShardedJedis shardedJedis = shardedJedisPool.getResource();
if(shardedJedis == null){
return result;
}
try {
result = shardedJedis.set(key,new String(ProtobuffSerializationUtil.serializeList(value),"ISO-8859-1"));
} catch (Exception e) {
log.error(e.getMessage(),e);
} finally{
shardedJedis.close();
}
return result;
}
/**
* @param key
* @param value
* @return
* 设置单个值和有效时间
*/
public String set(String key,Object value,int seconds){
String result = null;
ShardedJedis shardedJedis = shardedJedisPool.getResource();
if(shardedJedis == null){
return result;
}
try {
result = shardedJedis.set(key,new String(ProtobuffSerializationUtil.serialize(value),"ISO-8859-1"));
expire(key, seconds);
} catch (Exception e) {
log.error(e.getMessage(),e);
} finally{
shardedJedis.close();
}
return result;
}
/**
* @param key
* @param value
* @return
* 设置List和有效时间
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public String setList(String key,List value,int seconds){
String result = null;
ShardedJedis shardedJedis = shardedJedisPool.getResource();
if(shardedJedis == null){
return result;
}
try {
result = shardedJedis.set(key,new String(ProtobuffSerializationUtil.serializeList(value),"ISO-8859-1"));
expire(key, seconds);
} catch (Exception e) {
log.error(e.getMessage(),e);
} finally{
shardedJedis.close();
}
return result;
}
/**
* @param key
* @param value
* @return
* 获取单个值
*/
public <T> T get(String key,Class<T> clazz){
ShardedJedis shardedJedis = shardedJedisPool.getResource();
if(shardedJedis == null){
return null;
}
try {
String resultStr = shardedJedis.get(key);
if(StringUtils.isEmpty(resultStr))
return null;
return ProtobuffSerializationUtil.deserialize(resultStr.getBytes("ISO-8859-1"), clazz);
} catch (Exception e) {
log.error(e.getMessage(),e);
e.printStackTrace();
} finally{
shardedJedis.close();
}
return null;
}
/**
* @param key
* @param value
* @return
* 获取List
*/
public <T> List<T> getList(String key,Class<T> clazz){
ShardedJedis shardedJedis = shardedJedisPool.getResource();
if(shardedJedis == null){
return null;
}
try {
String resultStr = shardedJedis.get(key);
if(StringUtils.isEmpty(resultStr))
return null;
return ProtobuffSerializationUtil.deserializeList(resultStr.getBytes("ISO-8859-1"), clazz);
} catch (Exception e) {
log.error(e.getMessage(),e);
e.printStackTrace();
} finally{
shardedJedis.close();
}
return null;
}
/**
* @param key
* @param value
* @return
* 判断key是否存在
*/
public Boolean exists(String key){
Boolean result = false;
ShardedJedis shardedJedis = shardedJedisPool.getResource();
if(shardedJedis == null){
return result;
}
try {
result = shardedJedis.exists(key);
} catch (Exception e) {
log.error(e.getMessage(),e);
} finally{
shardedJedis.close();
}
return result;
}
/**
* @param key
* @param value
* @return
* 设置key的过期时间段
*/
public Long expire(String key,int seconds){
Long result = null;
ShardedJedis shardedJedis = shardedJedisPool.getResource();
if(shardedJedis == null){
return result;
}
try {
result = shardedJedis.expire(key, seconds);
} catch (Exception e) {
log.error(e.getMessage(),e);
} finally{
shardedJedis.close();
}
return result;
}
/**
* @param key
* @param value
* @return
* 设置key的过期时间点
*/
public Long expire(String key,long unixTime){
Long result = null;
ShardedJedis shardedJedis = shardedJedisPool.getResource();
if(shardedJedis == null){
return result;
}
try {
result = shardedJedis.expireAt(key, unixTime);
} catch (Exception e) {
log.error(e.getMessage(),e);
} finally{
shardedJedis.close();
}
return result;
}
}
四、序列反序列工具类ProtobuffSerializationUtil.java
package com.cares.mbsis.redis.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.objenesis.Objenesis;
import org.springframework.objenesis.ObjenesisStd;
import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtobufIOUtil;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
/**
* @Description:Protobuff插件序列化
*/
public class ProtobuffSerializationUtil {
private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();
private static Objenesis objenesis = new ObjenesisStd(true);
public static<T> Schema<T> getSchema(Class<T> clazz){
@SuppressWarnings("unchecked")
Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);
if (schema == null) {
schema = RuntimeSchema.getSchema(clazz);
if (schema != null) {
cachedSchema.put(clazz, schema);
}
}
return schema;
}
/**
* 序列化
*
* @param obj
* @return
*/
public static <T> byte[] serialize(T obj) {
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) obj.getClass();
LinkedBuffer buffer = LinkedBuffer
.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
try {
Schema<T> schema = getSchema(clazz);
return ProtobufIOUtil.toByteArray(obj, schema, buffer);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
buffer.clear();
}
}
/**
* 序列化List
*
* @param obj
* @return
*/
public static <T> byte[] serializeList(List<T> objList) {
if(objList == null || objList.isEmpty()){
throw new RuntimeException("序列化对象列表("+objList+")参数异常!");
}
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) objList.get(0).getClass();
LinkedBuffer buffer = LinkedBuffer
.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
byte[] protostuff = null;
ByteArrayOutputStream bos = null;
try {
bos = new ByteArrayOutputStream();
Schema<T> schema = getSchema(clazz);
ProtostuffIOUtil.writeListTo(bos,objList,schema,buffer);
protostuff = bos.toByteArray();
return protostuff;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
buffer.clear();
}
}
/**
* 反序列化
*
* @param data
* @param clazz
* @return
*/
public static <T> T deserialize(byte[] data, Class<T> clazz) {
try {
T obj = objenesis.newInstance(clazz);
Schema<T> schema = getSchema(clazz);
ProtobufIOUtil.mergeFrom(data, obj, schema);
return obj;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
/**
* 反序列化List
*
* @param data
* @param clazz
* @return
*/
public static <T> List<T> deserializeList(byte[] data, Class<T> clazz) {
if(data == null || data.length == 0){
throw new RuntimeException("反序列化对象发生异常,byte序列为空!");
}
try {
Schema<T> schema = getSchema(clazz);
List<T> result = null;
result = ProtostuffIOUtil.parseListFrom(new ByteArrayInputStream(data),schema);
return result;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
五、key生成工具类GenerateKeyUtil.java
package com.cares.mbsis.redis.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.log4j.Logger;
import sun.misc.BASE64Encoder;
public class GenerateKeyUtil {
private static final Logger log = Logger.getLogger(GenerateKeyUtil.class);
/**
* @param t
* @param parm
* @return
* @throws NoSuchAlgorithmException
* 生成key供redis入库
**/
@SuppressWarnings("static-access")
public synchronized static String generateKey(Thread t,String parms){
StringBuffer key = new StringBuffer();
String resultKey = null;
key.append(t.currentThread().getStackTrace()[1].getClassName())
.append(t.currentThread().getStackTrace()[1].getMethodName())
.append(t.currentThread().getStackTrace()[1].getLineNumber())
.append(parms);
MessageDigest md;
try {
md = MessageDigest.getInstance("md5");
byte md5[] = md.digest(key.toString().getBytes());
//base64编码--任意二进制编码明文字符
BASE64Encoder encoder = new BASE64Encoder();
resultKey = encoder.encode(md5);
} catch (NoSuchAlgorithmException e) {
log.error(e.getMessage(),e);
}
return resultKey;
}
/**
* @param t
* @param parm
* @return
* @throws NoSuchAlgorithmException
* 生成key供redis入库
**/
public synchronized static String generateKey(Thread t){
return generateKey(t,"");
}
}
六、ReportController.java应用Redis缓存
package com.cares.mbsis.web.controller;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import com.cares.mbsis.redis.service.RedisService;
import com.cares.mbsis.redis.util.GenerateKeyUtil;
import com.cares.mbsis.service.ReportService;
import com.cares.mbsis.util.Constants;
import com.cares.mbsis.util.Util;
@Controller
public class ReportController {
@Autowired
private ReportService reportService;
@Autowired
private RedisService redisService;
@RequestMapping(value="getOrderDistribution.do",method = RequestMethod.POST)
@ResponseBody
public ModelAndView getOrderDistribution() throws IOException{
Map<String, String> map = new HashMap<String, String>();
String jsonStr = null;
String key = GenerateKeyUtil.generateKey(Thread.currentThread());
jsonStr = redisService.get(key,String.class);
if(Util.isNull(jsonStr)){
jsonStr = reportService.getOrderDistribution();
redisService.set(key,jsonStr,1*Constants.HOUR);
}
map.put("msg",jsonStr);
return new ModelAndView(new MappingJackson2JsonView(),map);
}
}