Spring集成Redis

阳光穿透心脏的1/2处 2022-05-20 01:48 312阅读 0赞

最近在做一个关于群聊的项目,由于聊天要求实时性,不可能直接访问数据库,所以使用了Redis来做缓存,这里将使用过程中遇到的问题记录一下。
使用Redis之前需要合理设计存储在其中的数据结构,比如聊天系统,就需要存储包括用户登录状态信息,可以使用Hash数据结构,以用户Id做键,状态做值,还可以存储每个用户在每个群的未读消息计数,
客户端使用长轮询拉取。
最主要的就是群里的聊天记录的缓存了,这里可以使用Redis的List数据结构来做消息队列,其中按顺序存储消息实体对象(序列化后存入Redis)。还可以维护一个List存储聊天记录的主键便于定位聊天记录,等等的一系列数据都可以缓存到Redis。
Redis在Java中的实现是Jedis,其中封装了连接Redis的连接池实现、对Redis数据库的各种操作等。
在JavaWeb项目中使用Redis做缓存时有两种实现方式,第一种是使用原生的Jedis包,其中封装的方法可以直接操作Redis。第二种方法是使用Spring框架集成的Redis,其中封装Jedis对Redis的各种操作,包括事务、连接、连接池的维护等,其对Redis的操作进行了很多封装。
这个聊天项目一开始采用的是Spring封装的Redis,使用过程中发现,经常会报一些类型转换异常的问题,后来发现这种错误经常在网络连接异常或是长轮询的地方出现。深入底层发现是由于Redis底层的Socket连接使用了缓冲区,导致有些时候如果前面的连接出现问题,后面的请求再使用前面的连接会获取到前面连接的数据,导致类型转换异常的出现,
尤其是长轮询不断请求Redis服务器的时候,问题出现的频率尤其高,而且一旦出现问题,后面的连接都会出问题,这也许是由于Redis连接池中出问题的连接没有完全释放的原因。
因此建议大家如果使用Redis做缓存,最好使用原生的Jedis,这样可以在连接异常时自己实现异常连接的处理,而使用Spring集成的Redis由于连接已经被封装了,限制了自己对异常连接的处理。
下面就将两种集成Redis的方法分别介绍一下。
第一种是使用Spring集成的Redis。

1、首先是Jar包,需要jedis包、Spring集成Redis的包spring-data-redis、Redis连接池需要的commons-pool2包,其中spring-data-redis包和jedis包的版本需要适配,否则会报错,具体要求网上可以查到,这3个包的下载可以从Maven仓库获取。

  1. <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
  2. <dependency>
  3. <groupId>org.apache.commons</groupId>
  4. <artifactId>commons-pool2</artifactId>
  5. <version>2.4.2</version>
  6. </dependency>
  7. <!-- spring-data-redis2.0.7版本下载失败 -->
  8. <dependency>
  9. <groupId>org.springframework.data</groupId>
  10. <artifactId>spring-data-redis</artifactId>
  11. <version>1.8.4.RELEASE</version>
  12. </dependency>
  13. <dependency>
  14. <groupId>redis.clients</groupId>
  15. <artifactId>jedis</artifactId>
  16. <version>2.9.0</version>
  17. </dependency>

2、Spring集成Redis的Xml配置

2.1、Redis.properties配置(相当于jdbc.properties)

  1. #============================#
  2. #==== Redis settings ====#
  3. #============================#
  4. #redis 服务器 IP
  5. redis.host=192.168.1.222
  6. #192.168.1.222
  7. #格式:redis://:[密码]@[服务器地址]:[端口]/[db index]
  8. redis.uri = redis://:12345@127.0.0.1:6379/0
  9. #redis 服务器端口
  10. redis.port=6379
  11. #redis 密码
  12. redis.pass=
  13. #redis#2018
  14. #超时时间
  15. redis.timeOut=2000
  16. #redis 支持16个数据库(相当于不同用户)可以使不同的应用程序数据彼此分开同时又存储在相同的实例上
  17. redis.dbIndex=0
  18. #redis 缓存数据过期时间单位秒
  19. redis.expiration=3000
  20. #连接池中最大连接数。高版本:maxTotal,低版本:maxActive
  21. #控制一个 pool 可分配多少个jedis实例
  22. redis.maxActive=6
  23. #连接池中最少空闲的连接数
  24. redis.minIdle=1
  25. #连接池中最大空闲的连接数,控制一个 pool 最多有多少个状态为 idle 的jedis实例
  26. redis.maxIdle=300
  27. #当连接池资源耗尽时,调用者最大阻塞的时间,超时将抛出异常。单位,毫秒数;默认为-1.表示永不超时。高版本:maxWaitMillis,低版本:maxWait
  28. #当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
  29. redis.maxWait=1000
  30. #连接空闲的最小时间,达到此值后空闲连接将可能会被移除。负值(-1)表示不移除
  31. redis.minEvictableIdleTimeMillis=60000
  32. #对于“空闲链接”检测线程而言,每次检测的链接资源的个数。默认为3
  33. redis.numTestsPerEvictionRun=3
  34. #“空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1
  35. redis.timeBetweenEvictionRunsMillis=30000
  36. #testOnBorrow:向调用者输出“链接”资源时,是否检测是有有效,如果无效则从连接池中移除,并尝试获取继续获取。默认为false。建议保持默认值
  37. #在borrow一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的;
  38. redis.testOnBorrow=true
  39. #testOnReturn:向连接池“归还”链接时,是否检测“链接”对象的有效性。默认为false。建议保持默认值
  40. #testWhileIdle:向调用者输出“链接”对象时,是否检测它的空闲超时;默认为false。如果“链接”空闲超时,将会被移除。建议保持默认值
  41. #whenExhaustedAction:当“连接池”中active数量达到阀值时,即“链接”资源耗尽时,连接池需要采取的手段, 默认为1(0:抛出异常。1:阻塞,直到有可用链接资源。2:强制创建新的链接资源)

2.2、applicationContext.xml引入redis.properties文件

  1. <!-- 这里配置数据库properties文件位置 -->
  2. <!-- 使用PropertyPlaceholderConfigurer加载redis.properties文件失败改用PropertySourcesPlaceholderConfigurer -->
  3. <bean id="propertyConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
  4. <property name="locations">
  5. <list>
  6. <value>classpath:jdbc.properties</value>
  7. <value>classpath:system.properties</value>
  8. <value>classpath:redis.properties</value>
  9. </list>
  10. </property>
  11. </bean>

将Redis相关配置单独放在一个配置文件中:

  1. <import resource="spring-redis-context.xml" />

spring-redis-context.xml中的配置包括,连接池的配置、连接工厂的配置以及RedisTemplate类的相关配置。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context.xsd">
  9. <description>Spring容器管理redis连接和使用 </description>
  10. <!--载入 redis 配置文件-->
  11. <!-- Spring容器仅允许最多定义一个PropertyPlaceholderConfigurer 或 <content:property-placeholder>其余的会被Spring忽略 -->
  12. <!-- <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/> -->
  13. <!-- 高版本Jedis没有maxActive变量,具体有哪些可以看JedisPoolConfig继承的类GenericObjectPoolConfig -->
  14. <!-- 连接池配置 -->
  15. <!-- 配置 JedisPoolConfig 实例 -->
  16. <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
  17. <property name="maxIdle" value="${redis.maxIdle}"/>
  18. <property name="maxTotal" value="${redis.maxActive}"/>
  19. <property name="maxWaitMillis" value="${redis.maxWait}"/>
  20. <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
  21. </bean>
  22. <!-- <bean id="clusterRedisNodes1" class="org.springframework.data.redis.connection.RedisNode">
  23. <constructor-arg value="${redis.host}" />
  24. <constructor-arg value="${redis.port}" type="int"/>
  25. </bean> -->
  26. <!-- ERR This instance has cluster support disabled -->
  27. <!-- <bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">
  28. <property name="maxRedirects" value="3"></property>
  29. 节点配置
  30. <property name="clusterNodes">
  31. <set>
  32. <bean class="org.springframework.data.redis.connection.RedisClusterNode">
  33. <constructor-arg name="host" value="${redis.host}"/>
  34. <constructor-arg name="port" value="${redis.port}"/>
  35. </bean>
  36. <bean class="org.springframework.data.redis.connection.RedisClusterNode">
  37. <constructor-arg name="host" value="192.168.1.111"/>
  38. <constructor-arg name="port" value="6380"/>
  39. </bean>
  40. </set>
  41. </property>
  42. </bean> -->
  43. <!-- Spring提供的Redis连接工厂 -->
  44. <!-- 配置JedisConnectionFactory -->
  45. <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
  46. <property name="hostName" value="${redis.host}"/>
  47. <property name="port" value="${redis.port}"/>
  48. <property name="password" value="${redis.pass}"/>
  49. <property name="database" value="${redis.dbIndex}"/>
  50. <property name="poolConfig" ref="poolConfig"/>
  51. <!-- <constructor-arg ref="redisClusterConfiguration"></constructor-arg>
  52. <constructor-arg ref="poolConfig"></constructor-arg> -->
  53. <!-- <property name="clusterConfig" ref="redisClusterConfiguration"></property> -->
  54. </bean>
  55. <bean id="redisConfig" class="com.teriste.redis.util.RedisConfig"></bean>
  56. <!-- Spring提供的访问Redis类 -->
  57. <!-- 配置RedisTemplate -->
  58. <!-- <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" scope="prototype">
  59. <property name="connectionFactory" ref="jedisConnectionFactory"/>
  60. 下面是设置key和value序列化的方式,不同方式导致存入的字节序列不一样,什么样的序列化方式就要使用相应的反序列化方式查询,否则差不多redis存入的数据
  61. 由此也可以知道spring对redis序列化有很多种方式
  62. <property name="keySerializer">
  63. <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
  64. </property>
  65. <property name="valueSerializer">
  66. defaultSerializer是JdkSerializationRedisSerializer在调试时看不出来键是什么样的
  67. <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
  68. <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" />
  69. fastjson序列化
  70. <bean class="com.teriste.redis.util.FastJson2JsonRedisSerializer">
  71. <constructor-arg name="clazz" value=Object.class></constructor-arg>
  72. </bean>
  73. </property>
  74. <property name="hashKeySerializer">
  75. <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
  76. </property>
  77. 配置true可以使用transactional控制事务,spring已经提供支持
  78. <property name="enableTransactionSupport" value="true"/>
  79. </bean> -->
  80. <!-- 配置RedisCacheManager -->
  81. <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
  82. <constructor-arg name="redisOperations" ref="redisTemplate"/>
  83. <property name="defaultExpiration" value="${redis.expiration}"/>
  84. </bean>
  85. <!-- 配置RedisCacheConfig -->
  86. <!-- <bean id="redisCacheConfig" class="com.teriste.redis.util.RedisCacheConfig">
  87. <constructor-arg ref="jedisConnectionFactory"/>
  88. <constructor-arg ref="redisTemplate"/>
  89. <constructor-arg ref="redisCacheManager"/>
  90. </bean> -->
  91. <!-- Redis sentinel集群配置 -->
  92. <!-- <bean id="sentinelConfig" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
  93. <constructor-arg index="0" type="java.lang.String" value="host6379" />
  94. <constructor-arg index="1" type="java.util.Set">
  95. <set>
  96. <value>192.168.1.111:6380</value>
  97. <value>192.168.1.111:6381</value>
  98. </set>
  99. </constructor-arg>
  100. </bean> -->
  101. <!-- Spring提供的Redis连接工厂 -->
  102. <!-- <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
  103. Redis sentinel集群配置
  104. <constructor-arg index="0" type="org.springframework.data.redis.connection.RedisSentinelConfiguration" ref="sentinelConfig" />
  105. 连接池配置.
  106. <constructor-arg index="1" type="redis.clients.jedis.JedisPoolConfig" ref="poolConfig" />
  107. Redis服务主机.
  108. <property name="hostName" value="192.168.1.111" />
  109. Redis服务端口号.
  110. <property name="port" value="26379" />
  111. Redis服务连接密码.
  112. <property name="password" value="${redis.pass}" />
  113. 连超时设置.
  114. <property name="timeout" value="15000" />
  115. 是否使用连接池.
  116. <property name="usePool" value="true" />
  117. </bean> -->
  118. </beans>

RedisTemplate相关的配置在RedisConfig类中统一管理,再由容器注入,其中主要包括的是Java对象序列化存入Redis和从Redis反序列化到java对象的配置,因为Spring集成的redis默认是

JdkSerializationRedisSerializer,序列化后是字节数组,存入Redis不便于查看,所以需要自己配置键值的序列化方式,这里使用的是alibaba的fastjson2:

  1. //RedisConfig.java
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.data.redis.connection.RedisConnectionFactory;
  5. import org.springframework.data.redis.core.RedisTemplate;
  6. import org.springframework.data.redis.serializer.RedisSerializer;
  7. import org.springframework.data.redis.serializer.StringRedisSerializer;
  8. import com.alibaba.fastjson.parser.ParserConfig;
  9. @Configuration
  10. public class RedisConfig
  11. {
  12. /**
  13. * 重写Redis序列化方式,使用Json方式:
  14. * 当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer。
  15. * Spring Data JPA为我们提供了下面的Serializer:
  16. * GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。
  17. * 在此我们将自己配置RedisTemplate并定义Serializer。
  18. * com.teriste.redis.util.FastJson2JsonRedisSerializer
  19. * @param redisConnectionFactory
  20. * @return
  21. */
  22. @Bean(name="fastJson2JsonRedisSerializer")
  23. @SuppressWarnings("rawtypes")
  24. public RedisSerializer fastJson2JsonRedisSerializer(){
  25. return new FastJson2JsonRedisSerializer<Object>(Object.class);
  26. }
  27. @Bean(name="redisTemplate")
  28. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){
  29. RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
  30. redisTemplate.setConnectionFactory(factory);
  31. FastJson2JsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJson2JsonRedisSerializer<Object>(Object.class);
  32. // 全局开启AutoType,不建议使用
  33. // ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
  34. // 建议使用这种方式,小范围指定白名单
  35. ParserConfig.getGlobalInstance().addAccept("com.teriste.");
  36. //设置键值序列化方式
  37. redisTemplate.setKeySerializer(new StringRedisSerializer());
  38. redisTemplate.setValueSerializer(fastJsonRedisSerializer);
  39. //设置hash键值序列化方式
  40. redisTemplate.setHashKeySerializer(new StringRedisSerializer());
  41. redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
  42. redisTemplate.setEnableTransactionSupport(true);
  43. redisTemplate.afterPropertiesSet();
  44. return redisTemplate;
  45. }
  46. }
  47. //FastJson2JsonRedisSerializer.java
  48. import java.nio.charset.Charset;
  49. import org.springframework.data.redis.serializer.RedisSerializer;
  50. import org.springframework.data.redis.serializer.SerializationException;
  51. import com.alibaba.fastjson.JSON;
  52. import com.alibaba.fastjson.serializer.SerializerFeature;
  53. /**
  54. * 自定义序列化配置类
  55. * @author Administrator
  56. * @version 2018年5月24日
  57. * @see FastJson2JsonRedisSerializer
  58. * @since
  59. */
  60. public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
  61. {
  62. public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
  63. private Class<T> clazz;
  64. public FastJson2JsonRedisSerializer(Class<T> clazz) {
  65. super();
  66. this.clazz = clazz;
  67. }
  68. @Override
  69. public byte[] serialize(T t)
  70. throws SerializationException
  71. {
  72. if (null==t)
  73. {
  74. return new byte[0];
  75. }
  76. return JSON.toJSONString(t,SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
  77. }
  78. @Override
  79. public T deserialize(byte[] bytes)
  80. throws SerializationException
  81. {
  82. if (null==bytes||bytes.length<=0)
  83. {
  84. return null;
  85. }
  86. String str=new String(bytes,DEFAULT_CHARSET);
  87. return (T)JSON.parseObject(str, clazz);
  88. }
  89. }

还可以根据需要配置Redis键生成规则:

  1. //RedisCacheConfig.java
  2. import org.springframework.cache.CacheManager;
  3. import org.springframework.cache.annotation.CachingConfigurerSupport;
  4. import org.springframework.cache.annotation.EnableCaching;
  5. import org.springframework.cache.interceptor.KeyGenerator;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.data.redis.cache.RedisCacheManager;
  9. import org.springframework.data.redis.connection.RedisConnectionFactory;
  10. import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
  11. import org.springframework.data.redis.core.RedisTemplate;
  12. import org.slf4j.Logger;
  13. import org.slf4j.LoggerFactory;
  14. import java.lang.reflect.Method;
  15. /**
  16. *
  17. * spring-context since 4.1 才有CachingConfigurerSupport类
  18. * @author Administrator
  19. * @version 2018年5月21日
  20. * @see RedisCacheConfig
  21. * @since
  22. */
  23. @Configuration
  24. @EnableCaching
  25. public class RedisCacheConfig extends CachingConfigurerSupport {
  26. protected final static Logger log = LoggerFactory.getLogger(RedisCacheConfig.class);
  27. private volatile JedisConnectionFactory mJedisConnectionFactory;
  28. private volatile RedisTemplate<String, String> mRedisTemplate;
  29. private volatile RedisCacheManager mRedisCacheManager;
  30. public RedisCacheConfig() {
  31. super();
  32. }
  33. public RedisCacheConfig(JedisConnectionFactory mJedisConnectionFactory, RedisTemplate<String, String> mRedisTemplate, RedisCacheManager mRedisCacheManager) {
  34. super();
  35. this.mJedisConnectionFactory = mJedisConnectionFactory;
  36. this.mRedisTemplate = mRedisTemplate;
  37. this.mRedisCacheManager = mRedisCacheManager;
  38. }
  39. public JedisConnectionFactory redisConnectionFactory() {
  40. return mJedisConnectionFactory;
  41. }
  42. public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) {
  43. return mRedisTemplate;
  44. }
  45. public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) {
  46. return mRedisCacheManager;
  47. }
  48. /**
  49. * 该类告诉 spring 当前使用的缓存服务为 redis 并自定义了缓存 key 生成的规则。
  50. */
  51. @Bean
  52. public KeyGenerator keyGenerator() {
  53. return new KeyGenerator() {
  54. public Object generate(Object o, Method method, Object... objects) {
  55. StringBuilder sb = new StringBuilder();
  56. sb.append(o.getClass().getName());
  57. sb.append(method.getName());
  58. for (Object obj : objects) {
  59. sb.append(obj.toString());
  60. }
  61. return sb.toString();
  62. }
  63. };
  64. }
  65. }

下面就可以使用RedisTemplate类来操作Redis数据库了。
redisTemplate有两个方法经常用到,一个是opsForXXX一个是boundXXXOps,XXX是value的类型,前者获取到一个Opercation,但是没有指定操作的key,可以在一个连接(事务)内操作多个key以及对应的value;后者会获取到一个指定了key的operation,在一个连接内只操作这个key对应的value.
使用中发现一个问题,如果方法使用了Spring的@Transactional注解,那么在该方法里使用读取redis数据的方法获取不到数据,写入redis数据库的方法可以生效。
下面开始介绍第二种使用Redis的方式:原生Jedis。
1、同样还是需要redis.properties,以及在applicationContext.xml中引入redis.properties和单独的spring集成redis的配置文件。

2、配置jedis连接池

spring-redis-context.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context.xsd">
  9. <description>Spring容器管理redis连接和使用 </description>
  10. <!--载入 redis 配置文件-->
  11. <!-- Spring容器仅允许最多定义一个PropertyPlaceholderConfigurer 或 <content:property-placeholder>其余的会被Spring忽略 -->
  12. <!-- <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/> -->
  13. <!-- 高版本Jedis没有maxActive变量,具体有哪些可以看JedisPoolConfig继承的类GenericObjectPoolConfig -->
  14. <!-- 连接池配置 -->
  15. <!--shardedJedisPool的相关配置-->
  16. <!-- <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
  17. 新版是maxTotal,旧版是maxActive
  18. <property name="maxTotal">
  19. <value>${redis.maxActive}</value>
  20. </property>
  21. <property name="maxIdle">
  22. <value>${redis.maxIdle}</value>
  23. </property>
  24. <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
  25. <property name="testOnReturn" value="true"/>
  26. </bean>
  27. <bean id="sharedJedisPool" class="redis.clients.jedis.ShardedJedisPool" scope="="singleton">
  28. <constructor-arg index="0" ref="jedisPoolConfig"></constructor-arg>
  29. <constructor-arg index="1">
  30. <list>
  31. <bean class="redis.clients.jedis.JedisShardInfo">
  32. <constructor-arg name="host" value="${redis.uri}"></constructor-arg>
  33. </bean>
  34. </list>
  35. </constructor-arg>
  36. </bean> -->
  37. <!-- 高版本Jedis没有maxActive变量,具体有哪些可以看JedisPoolConfig继承的类GenericObjectPoolConfig -->
  38. <!-- 连接池配置 -->
  39. <!-- 配置 JedisPoolConfig 实例 -->
  40. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
  41. <property name="maxIdle" value="${redis.maxIdle}"/>
  42. <property name="maxTotal" value="${redis.maxActive}"/>
  43. <property name="maxWaitMillis" value="${redis.maxWait}"/>
  44. <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
  45. </bean>
  46. <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
  47. <constructor-arg name="poolConfig" ref="jedisPoolConfig" />
  48. <constructor-arg name="host" value="${redis.host}" />
  49. <constructor-arg name="port" value="${redis.port}" type="int" />
  50. <constructor-arg name="timeout" value="${redis.timeOut}" type="int" />
  51. <!-- <constructor-arg name="password" value="${redis.pass}" />
  52. <constructor-arg name="database" value="${redis.dbIndex}" type="int" /> -->
  53. </bean>
  54. <bean id="jedisClient" class="com.teriste.redis.util.JedisClient">
  55. <constructor-arg name="jedisPool" ref="jedisPool"></constructor-arg>
  56. <constructor-arg name="jedisPoolConfig" ref="jedisPoolConfig"></constructor-arg>
  57. <constructor-arg name="host" value="${redis.host}" />
  58. <constructor-arg name="port" value="${redis.port}" type="int" />
  59. <constructor-arg name="timeout" value="${redis.timeOut}" type="int" />
  60. </bean>
  61. <bean id="redisClient" class="com.teriste.redis.util.RedisClient">
  62. <constructor-arg name="jedisClient" ref="jedisClient"></constructor-arg>
  63. </bean>
  64. </beans>

其中的JedisClient.java类封装了连接和释放redis连接的方法,RedisClient类封装了对redis中各种数据类型的操作。
同样涉及到Java对象存入redis的问题,键是String类型的,所以直接使用String类的getBytes(charset)和new String(bytes, charset)来序列化和反序列化。值使用fastjson2。

StringSerializer.java实现键的序列化

  1. import java.nio.charset.Charset;
  2. /**
  3. * 读写Hash数据结构数据的序列化与发序列化工具类
  4. * @author Administrator
  5. * @version 2018年7月9日
  6. * @see StringSerializer
  7. * @since
  8. */
  9. public class StringSerializer
  10. {
  11. private static final Charset charset=Charset.forName("UTF-8");
  12. /*public StringSerializer(Charset charset) {
  13. Assert.notNull(charset, "Charset must not be null!");
  14. this.charset = charset;
  15. }*/
  16. public static String deserialize(byte[] bytes) {
  17. return bytes == null ? null : new String(bytes, charset);
  18. }
  19. public static byte[] serialize(String string) {
  20. return string == null ? null : string.getBytes(charset);
  21. }
  22. }

FastJson2JsonSerializer.java实现值的序列化

  1. import java.nio.charset.Charset;
  2. import org.springframework.data.redis.serializer.SerializationException;
  3. import com.alibaba.fastjson.JSON;
  4. import com.alibaba.fastjson.serializer.SerializerFeature;
  5. public class FastJson2JsonSerializer
  6. {
  7. public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
  8. public static byte[] serialize(Object t)
  9. throws SerializationException
  10. {
  11. if (null==t)
  12. {
  13. return new byte[0];
  14. }
  15. return JSON.toJSONString(t,SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
  16. }
  17. @SuppressWarnings(value={"unchecked","rawtypes"})
  18. public static Object deserialize(byte[] bytes, Class clazz)
  19. throws SerializationException
  20. {
  21. if (null==bytes||bytes.length<=0)
  22. {
  23. return null;
  24. }
  25. String str=new String(bytes,DEFAULT_CHARSET);
  26. return JSON.parseObject(str, clazz);
  27. }
  28. }

下面是对redis连接的封装:JedisClient.java

  1. import org.apache.log4j.Logger;
  2. import com.teriste.core.utils.SpringContextHolder;
  3. import redis.clients.jedis.Jedis;
  4. import redis.clients.jedis.JedisPool;
  5. import redis.clients.jedis.JedisPoolConfig;
  6. import redis.clients.jedis.exceptions.JedisConnectionException;
  7. import redis.clients.jedis.exceptions.JedisDataException;
  8. import redis.clients.jedis.exceptions.JedisException;
  9. public class JedisClient
  10. {
  11. /**
  12. * 日志
  13. */
  14. private static Logger logger = Logger.getLogger(JedisClient.class);
  15. private JedisPool jedisPool;//注入JedisPool
  16. private JedisPoolConfig jedisPoolConfig;
  17. private String host;
  18. private int port;
  19. private int timeout;
  20. public JedisClient(JedisPool jedisPool,JedisPoolConfig jedisPoolConfig,String host, int port,int timeout){
  21. this.jedisPool=jedisPool;
  22. this.jedisPoolConfig=jedisPoolConfig;
  23. this.host=host;
  24. this.port=port;
  25. this.timeout=timeout;
  26. }
  27. //获取Redis资源
  28. public synchronized Jedis getJedis(){
  29. try
  30. {
  31. if (jedisPool==null)
  32. {
  33. jedisPool=(JedisPool)SpringContextHolder.getApplicationContext().getBean("jedisPool");
  34. }
  35. Jedis jedis=jedisPool.getResource();
  36. return jedis;
  37. }
  38. catch (Exception e)
  39. {
  40. e.printStackTrace();
  41. //获取Redis连接时如果发生异常就重置连接池
  42. if (null!=jedisPool)
  43. {
  44. jedisPool.destroy();
  45. jedisPool=new JedisPool(jedisPoolConfig,host,port,timeout);
  46. }
  47. System.out.println(jedisPool.isClosed()+"----------------");
  48. if (jedisPool!=null)
  49. {
  50. Jedis jedis=jedisPool.getResource();
  51. return jedis;
  52. }
  53. }
  54. return null;
  55. }
  56. //释放redis资源
  57. public synchronized void releaseConn(Jedis jedis,Exception exception){
  58. /*if (null!=exception)
  59. {
  60. if (exception instanceof JedisConnectionException) {
  61. logger.error("Redis connection " + jedisPool.getResource().getClient().getHost() + " lost.", exception);
  62. } else if (exception instanceof JedisDataException) {
  63. if ((exception.getMessage() != null) && (exception.getMessage().indexOf("READONLY") != -1)) {
  64. logger.error("Redis connection " + jedisPool.getResource().getClient().getHost() + " are read-only slave.", exception);
  65. } else {
  66. // dataException, isBroken=false
  67. }
  68. } else {
  69. logger.error("Jedis exception happen.", exception);
  70. }
  71. }*/
  72. try
  73. {
  74. if (null!=jedis)
  75. {
  76. //Jedis的close()会先判断连接是否损毁如果是就走释放损毁连接的方法,否则走正常连接的方法。
  77. jedis.close();
  78. }
  79. }
  80. catch (Exception e)
  81. {
  82. logger.error("return back jedis failed, will fore close the jedis.", e);
  83. //如果close()方法执行失败就直接将jedis引用置空
  84. jedis=null;
  85. }
  86. /*try
  87. {
  88. if (jedis!=null)
  89. {
  90. jedisPool.returnResource(jedis);
  91. }
  92. }
  93. catch (Exception e)
  94. {
  95. if (jedis!=null)
  96. {
  97. try
  98. {
  99. jedisPool.returnBrokenResource(jedis);
  100. }
  101. catch (Exception e2)
  102. {
  103. jedis=null;
  104. }
  105. }
  106. }*/
  107. }
  108. /**
  109. * Handle jedisException, write log and return whether the connection is broken.
  110. */
  111. protected boolean handleJedisException(JedisException jedisException) {
  112. if (jedisException instanceof JedisConnectionException) {
  113. logger.error("Redis connection " + jedisPool.getResource().getClient().getHost() + " lost.", jedisException);
  114. } else if (jedisException instanceof JedisDataException) {
  115. if ((jedisException.getMessage() != null) && (jedisException.getMessage().indexOf("READONLY") != -1)) {
  116. logger.error("Redis connection " + jedisPool.getResource().getClient().getHost() + " are read-only slave.", jedisException);
  117. } else {
  118. // dataException, isBroken=false
  119. return false;
  120. }
  121. } else {
  122. logger.error("Jedis exception happen.", jedisException);
  123. }
  124. return true;
  125. }
  126. /**
  127. * Return jedis connection to the pool, call different return methods depends on the conectionBroken status.
  128. */
  129. @SuppressWarnings("deprecation")
  130. protected void closeResource(Jedis jedis, boolean conectionBroken) {
  131. try {
  132. if (conectionBroken) {
  133. jedisPool.returnBrokenResource(jedis);
  134. } else {
  135. jedisPool.returnResource(jedis);
  136. }
  137. } catch (Exception e) {
  138. logger.error("return back jedis failed, will fore close the jedis.", e);
  139. jedis=null;
  140. }
  141. }
  142. public JedisPool getJedisPool()
  143. {
  144. return jedisPool;
  145. }
  146. public void setJedisPool(JedisPool jedisPool)
  147. {
  148. this.jedisPool = jedisPool;
  149. }
  150. public JedisPoolConfig getJedisPoolConfig()
  151. {
  152. return jedisPoolConfig;
  153. }
  154. public void setJedisPoolConfig(JedisPoolConfig jedisPoolConfig)
  155. {
  156. this.jedisPoolConfig = jedisPoolConfig;
  157. }
  158. public String getHost()
  159. {
  160. return host;
  161. }
  162. public void setHost(String host)
  163. {
  164. this.host = host;
  165. }
  166. public int getPort()
  167. {
  168. return port;
  169. }
  170. public void setPort(int port)
  171. {
  172. this.port = port;
  173. }
  174. public int getTimeout()
  175. {
  176. return timeout;
  177. }
  178. public void setTimeout(int timeout)
  179. {
  180. this.timeout = timeout;
  181. }
  182. public static Logger getLogger()
  183. {
  184. return logger;
  185. }
  186. public static void setLogger(Logger logger)
  187. {
  188. JedisClient.logger = logger;
  189. }
  190. }

下面是对部分操作Redis数据的封装:RedisClient.java

  1. import java.util.ArrayList;
  2. import java.util.Collection;
  3. import java.util.Collections;
  4. import java.util.Iterator;
  5. import java.util.LinkedHashMap;
  6. import java.util.LinkedHashSet;
  7. import java.util.List;
  8. import java.util.Map;
  9. import java.util.Set;
  10. import java.util.concurrent.TimeUnit;
  11. import redis.clients.jedis.BinaryClient.LIST_POSITION;
  12. import org.springframework.data.redis.connection.jedis.JedisConverters;
  13. import org.springframework.data.redis.core.TimeoutUtils;
  14. import org.springframework.util.CollectionUtils;
  15. import org.springframework.util.Assert;
  16. import redis.clients.jedis.Jedis;
  17. public class RedisClient
  18. {
  19. private JedisClient jedisClient;
  20. public RedisClient(JedisClient jedisClient){
  21. this.jedisClient=jedisClient;
  22. }
  23. //键序列化与反序列化:StringSerializer
  24. //值序列化与反序列化:FastJson2JsonSerializer
  25. /**
  26. * 数据结构之外的处理
  27. */
  28. /**
  29. * 删除Redis中指定的键
  30. * @param key
  31. * @see
  32. */
  33. public void del(String key) {
  34. final byte[] rawKey = rawString(key);
  35. Jedis jedis=null;
  36. try
  37. {
  38. jedis=jedisClient.getJedis();
  39. jedis.del(new byte[][] { rawKey });
  40. }
  41. catch (Exception e)
  42. {
  43. e.printStackTrace();
  44. }finally{
  45. jedisClient.releaseConn(jedis,null);
  46. }
  47. }
  48. /**
  49. * 删除Redis中多个键
  50. * @param keys
  51. * @see
  52. */
  53. public void del(Collection<String> keys) {
  54. if (CollectionUtils.isEmpty(keys)) {
  55. return;
  56. }
  57. final byte[][] rawKeys = rawKeys(keys);
  58. Jedis jedis=null;
  59. try
  60. {
  61. jedis=jedisClient.getJedis();
  62. jedis.del(rawKeys);
  63. }
  64. catch (Exception e)
  65. {
  66. e.printStackTrace();
  67. }finally{
  68. jedisClient.releaseConn(jedis,null);
  69. }
  70. }
  71. /**
  72. * 判断Redis中键是否存在
  73. * @param key
  74. * @return
  75. * @see
  76. */
  77. public Boolean exists(String key){
  78. final byte[] rawKey = rawString(key);
  79. Jedis jedis=null;
  80. Boolean exists=null;
  81. try
  82. {
  83. jedis=jedisClient.getJedis();
  84. exists=jedis.exists(rawKey);
  85. return exists;
  86. }
  87. catch (Exception e)
  88. {
  89. e.printStackTrace();
  90. return false;
  91. }finally{
  92. jedisClient.releaseConn(jedis,null);
  93. }
  94. }
  95. private byte[][] rawKeys(Collection<String> keys) {
  96. byte[][] rawKeys = new byte[keys.size()][];
  97. int i = 0;
  98. for (String key : keys) {
  99. rawKeys[(i++)] = rawString(key);
  100. }
  101. return rawKeys;
  102. }
  103. /**
  104. * ################Hash##################
  105. */
  106. /**
  107. * Hash数据结构判断值的键是否存在
  108. * @param key
  109. * @param hashKey
  110. * @return
  111. * @see
  112. */
  113. public Boolean hExists(String key, String hashKey) {
  114. final byte[] rawKey = rawString(key);
  115. final byte[] rawHashKey = rawHashKey(hashKey);
  116. Jedis jedis=null;
  117. Boolean exists=null;
  118. try
  119. {
  120. jedis=jedisClient.getJedis();
  121. exists=jedis.hexists(rawKey, rawHashKey);
  122. }
  123. catch (Exception e)
  124. {
  125. e.printStackTrace();
  126. }finally{
  127. jedisClient.releaseConn(jedis,null);
  128. }
  129. return exists;
  130. }
  131. /**
  132. * Hash数据结构删除值键
  133. * @param key
  134. * @param hashKeys
  135. * @return
  136. * @see
  137. */
  138. public Long hDel(String key,Object... hashKeys){
  139. final byte[] rawKey = rawString(key);
  140. final byte[][] rawHashKeys = rawHashKeys(hashKeys);
  141. Jedis jedis=null;
  142. Long delNum=null;
  143. try
  144. {
  145. jedis=jedisClient.getJedis();
  146. delNum=jedis.hdel(rawKey, rawHashKeys);
  147. }
  148. catch (Exception e)
  149. {
  150. e.printStackTrace();
  151. }finally{
  152. jedisClient.releaseConn(jedis,null);
  153. }
  154. return delNum;
  155. }
  156. /**
  157. * Hash数据结构获取键对应的值键的集合
  158. * @param key
  159. * @return
  160. * @see
  161. */
  162. public Set<String> hKeys(String key){
  163. final byte[] rawKey = rawString(key);
  164. Jedis jedis=null;
  165. Set<byte[]> rawValues=null;
  166. try
  167. {
  168. jedis=jedisClient.getJedis();
  169. rawValues=jedis.hkeys(rawKey);
  170. }
  171. catch (Exception e)
  172. {
  173. e.printStackTrace();
  174. }finally{
  175. jedisClient.releaseConn(jedis,null);
  176. }
  177. return deserializeHashKeys(rawValues);
  178. }
  179. /**
  180. * Hash数据结构获取值键对应的值的集合
  181. * @param key
  182. * @param clazz
  183. * @return
  184. * @see
  185. */
  186. public List<Object> values(String key,Class clazz){
  187. final byte[] rawKey = rawString(key);
  188. Jedis jedis=null;
  189. List<byte[]> rawValues=null;
  190. try
  191. {
  192. jedis=jedisClient.getJedis();
  193. rawValues=jedis.hvals(rawKey);
  194. }
  195. catch (Exception e)
  196. {
  197. e.printStackTrace();
  198. }finally{
  199. jedisClient.releaseConn(jedis,null);
  200. }
  201. return deserializeHashValues(rawValues,clazz);
  202. }
  203. /**
  204. * Hash数据结构获取键对应的值键的个数
  205. * @param key
  206. * @return
  207. * @see
  208. */
  209. public Long hLen(String key) {
  210. final byte[] rawKey = rawString(key);
  211. Jedis jedis=null;
  212. Long len=null;
  213. try
  214. {
  215. jedis=jedisClient.getJedis();
  216. len=jedis.hlen(rawKey);
  217. }
  218. catch (Exception e)
  219. {
  220. e.printStackTrace();
  221. }finally{
  222. jedisClient.releaseConn(jedis,null);
  223. }
  224. return len;
  225. }
  226. /**
  227. * 获取hash数据结果的值
  228. * @param key
  229. * @param hashKey
  230. * @param clazz
  231. * @return
  232. * @see
  233. */
  234. public Object hGet(String key,String hashKey,Class clazz){
  235. final byte[] rawKey=rawString(key);
  236. final byte[] rawHashKey=rawHashKey(hashKey);
  237. byte[] rawHashValue =null;
  238. Jedis jedis=null;
  239. try
  240. {
  241. jedis=jedisClient.getJedis();
  242. rawHashValue=jedis.hget(rawKey,rawHashKey);
  243. }
  244. catch (Exception e)
  245. {
  246. e.printStackTrace();
  247. }
  248. finally{
  249. jedisClient.releaseConn(jedis,null);
  250. }
  251. return FastJson2JsonSerializer.deserialize(rawHashValue,clazz);
  252. }
  253. /**
  254. * Hash数据结构获取多个键的值列表
  255. * @param key
  256. * @param fields
  257. * @param clazz
  258. * @return
  259. * @see
  260. */
  261. public List<Object> hMGet(String key,List<String> fields,Class clazz){
  262. if (fields.isEmpty()) {
  263. return Collections.emptyList();
  264. }
  265. final byte[] rawKey = rawString(key);
  266. final byte[][] rawHashKeys = new byte[fields.size()][];
  267. int counter = 0;
  268. for (String hashKey : fields) {
  269. rawHashKeys[(counter++)] = rawHashKey(hashKey);
  270. }
  271. Jedis jedis=null;
  272. List<byte[]> rawValues=null;
  273. try
  274. {
  275. jedis=jedisClient.getJedis();
  276. rawValues=jedis.hmget(rawKey, rawHashKeys);
  277. }
  278. catch (Exception e)
  279. {
  280. e.printStackTrace();
  281. }finally{
  282. jedisClient.releaseConn(jedis,null);
  283. }
  284. return deserializeHashValues(rawValues,clazz);
  285. }
  286. /**
  287. * 根据键获取hash数据结构所有数据
  288. * @param key
  289. * @param hashKey
  290. * @param clazz:值的类型
  291. * @return
  292. * @see
  293. */
  294. public Map<Object, Object> hGetAll(String key,Class clazz){
  295. final byte[] rawKey=rawString(key);
  296. Map<byte[], byte[]> rawHashValue =null;
  297. Jedis jedis=null;
  298. try
  299. {
  300. jedis=jedisClient.getJedis();
  301. rawHashValue=jedis.hgetAll(rawKey);
  302. }
  303. catch (Exception e)
  304. {
  305. e.printStackTrace();
  306. }finally{
  307. jedisClient.releaseConn(jedis,null);
  308. }
  309. return deserializeHashMap(rawHashValue,clazz);
  310. }
  311. /**
  312. * 保存或更新hash数据
  313. * @param key
  314. * @param hashKey
  315. * @param value
  316. * @return
  317. * @see
  318. */
  319. public Boolean hSet(String key,String hashKey,Object value){
  320. final byte[] rawKey = rawString(key);
  321. final byte[] rawHashKey = rawHashKey(hashKey);
  322. final byte[] rawHashValue = rawValue(value);
  323. Jedis jedis=jedisClient.getJedis();
  324. try
  325. {
  326. Boolean result=JedisConverters.toBoolean(jedis.hset(rawKey, rawHashKey, rawHashValue));
  327. jedisClient.releaseConn(jedis,null);
  328. return result;
  329. }
  330. catch (Exception e)
  331. {
  332. jedisClient.releaseConn(jedis,e);
  333. return false;
  334. }
  335. }
  336. /**
  337. * hash数据结构保存多个键值对
  338. * @param key
  339. * @param m
  340. * @return
  341. * @see
  342. */
  343. public Boolean hMSet(String key,Map<String, Object> m){
  344. if (m.isEmpty()) {
  345. return false;
  346. }
  347. final byte[] rawKey = rawString(key);
  348. final Map<byte[], byte[]> hashes = new LinkedHashMap<byte[], byte[]>(m.size());
  349. for (Map.Entry<String, Object> entry : m.entrySet()) {
  350. hashes.put(rawHashKey(entry.getKey()), rawValue(entry.getValue()));
  351. }
  352. Jedis jedis=null;
  353. try
  354. {
  355. jedis=jedisClient.getJedis();
  356. jedis.hmset(rawKey, hashes);
  357. }
  358. catch (Exception e)
  359. {
  360. e.printStackTrace();
  361. }finally{
  362. jedisClient.releaseConn(jedis,null);
  363. }
  364. return true;
  365. }
  366. //key序列化
  367. public byte[] rawString(String key){
  368. return StringSerializer.serialize(key);
  369. }
  370. //hashkey序列化
  371. public byte[] rawHashKey(Object hashKey){
  372. return StringSerializer.serialize(hashKey.toString());
  373. }
  374. //多个值键序列化
  375. public byte[][] rawHashKeys(Object... hashKeys) {
  376. byte[][] rawHashKeys = new byte[hashKeys.length][];
  377. int i = 0;
  378. for (Object hashKey : hashKeys) {
  379. rawHashKeys[(i++)] = rawHashKey(hashKey);
  380. }
  381. return rawHashKeys;
  382. }
  383. //值序列化
  384. public byte[] rawValue(Object value){
  385. return FastJson2JsonSerializer.serialize(value);
  386. }
  387. //hash键反序列化
  388. public Set<String> deserializeHashKeys(Set<byte[]> rawKeys){
  389. if (rawKeys == null) {
  390. return null;
  391. }
  392. Set<String> values =new LinkedHashSet<String>(rawKeys.size());
  393. for (byte[] bs : rawKeys) {
  394. values.add(StringSerializer.deserialize(bs));
  395. }
  396. return values;
  397. }
  398. //hash结构值是String类型的反序列化
  399. public List<Object> deserializeHashValues(List<byte[]> rawValues,Class clazz){
  400. if (rawValues == null) {
  401. return null;
  402. }
  403. List<Object> values =new ArrayList<Object>();
  404. for (byte[] bs : rawValues) {
  405. values.add(FastJson2JsonSerializer.deserialize(bs,clazz));
  406. }
  407. return values;
  408. }
  409. /**
  410. * ################List##################
  411. */
  412. /**
  413. * List数据结构获取指定索引位置的值
  414. * @param key
  415. * @param index
  416. * @param clazz
  417. * @return
  418. * @see
  419. */
  420. public Object lIndex(String key, final long index,Class clazz) {
  421. final byte[] rawKey = rawString(key);
  422. Jedis jedis=null;
  423. byte[] value=null;
  424. try
  425. {
  426. jedis=jedisClient.getJedis();
  427. value=jedis.lindex(rawKey, index);
  428. return deserializeValue(value,clazz);
  429. }
  430. catch (Exception e)
  431. {
  432. e.printStackTrace();
  433. return null;
  434. }finally{
  435. jedisClient.releaseConn(jedis,null);
  436. }
  437. }
  438. /**
  439. * List数据结构获取键对应的值集合大小
  440. * @param key
  441. * @return
  442. * @see
  443. */
  444. public Long lLen(String key)
  445. {
  446. final byte[] rawKey = rawString(key);
  447. Jedis jedis=null;
  448. Long len=null;
  449. try
  450. {
  451. jedis=jedisClient.getJedis();
  452. len=jedis.llen(rawKey);
  453. }
  454. catch (Exception e)
  455. {
  456. e.printStackTrace();
  457. }finally{
  458. jedisClient.releaseConn(jedis,null);
  459. }
  460. return len;
  461. }
  462. /**
  463. * List数据结构获取指定范围的值集合
  464. * @param key
  465. * @param start
  466. * @param end
  467. * @param clazz
  468. * @return
  469. * @see
  470. */
  471. public List<Object> lRange(String key, final long start, long end,Class clazz)
  472. {
  473. final byte[] rawKey = rawString(key);
  474. Jedis jedis=null;
  475. List<byte[]> rawValues=null;
  476. try
  477. {
  478. jedis=jedisClient.getJedis();
  479. rawValues=jedis.lrange(rawKey, start, end);
  480. return deserializeValues(rawValues,clazz);
  481. }
  482. catch (Exception e)
  483. {
  484. e.printStackTrace();
  485. return null;
  486. }finally{
  487. jedisClient.releaseConn(jedis,null);
  488. }
  489. }
  490. /**
  491. * List数据结构移除指定值
  492. * @param key
  493. * @param count
  494. * @param value
  495. * @return
  496. * @see
  497. */
  498. public Long lRem(String key, final long count, Object value)
  499. {
  500. final byte[] rawKey = rawString(key);
  501. byte[] rawValue = rawValue(value);
  502. Jedis jedis=null;
  503. Long remNum=null;
  504. try
  505. {
  506. jedis=jedisClient.getJedis();
  507. remNum=jedis.lrem(rawKey, count, rawValue);
  508. }
  509. catch (Exception e)
  510. {
  511. e.printStackTrace();
  512. }finally{
  513. jedisClient.releaseConn(jedis,null);
  514. }
  515. return remNum;
  516. }
  517. /**
  518. * List数据结构从左侧弹出值对象
  519. * @param key
  520. * @param clazz
  521. * @return
  522. * @see
  523. */
  524. public Object lLPop(String key,Class clazz){
  525. final byte[] rawKey = rawString(key);
  526. Jedis jedis=null;
  527. byte[] value=null;
  528. try
  529. {
  530. jedis=jedisClient.getJedis();
  531. value=jedis.lpop(rawKey);
  532. return deserializeValue(value,clazz);
  533. }
  534. catch (Exception e)
  535. {
  536. e.printStackTrace();
  537. return null;
  538. }finally{
  539. jedisClient.releaseConn(jedis,null);
  540. }
  541. }
  542. /**
  543. * List数据结构从左侧弹出值的字节数组
  544. * @param key
  545. * @param timeout
  546. * @param unit
  547. * @return
  548. * @see
  549. */
  550. public byte[] lLPop(String key, long timeout, TimeUnit unit)
  551. {
  552. final int tm = (int)TimeoutUtils.toSeconds(timeout, unit);
  553. final byte[] rawKey = rawString(key);
  554. Jedis jedis=null;
  555. try
  556. {
  557. jedis=jedisClient.getJedis();
  558. List<byte[]> bLPop=jedis.blpop(tm, new byte[][] { rawKey });
  559. return CollectionUtils.isEmpty(bLPop) ? null : (byte[])bLPop.get(1);
  560. }
  561. catch (Exception e)
  562. {
  563. e.printStackTrace();
  564. return null;
  565. }finally{
  566. jedisClient.releaseConn(jedis,null);
  567. }
  568. }
  569. /**
  570. * List数据结构从左侧插入数据
  571. * @param key
  572. * @param value
  573. * @return
  574. * @see
  575. */
  576. public Long lLeftPush(String key, Object value)
  577. {
  578. final byte[] rawKey = rawString(key);
  579. final byte[] rawValue = rawValue(value);
  580. Jedis jedis=null;
  581. Long pushNum=null;
  582. try
  583. {
  584. jedis=jedisClient.getJedis();
  585. pushNum=jedis.lpush(rawKey, new byte[][] { rawValue });
  586. }
  587. catch (Exception e)
  588. {
  589. e.printStackTrace();
  590. }finally{
  591. jedisClient.releaseConn(jedis,null);
  592. }
  593. return pushNum;
  594. }
  595. /**
  596. * List数据结构左侧插入值数组
  597. * @param key
  598. * @param values
  599. * @return
  600. * @see
  601. */
  602. public Long lLeftPushAll(String key, Object[] values)
  603. {
  604. final byte[] rawKey = rawString(key);
  605. final byte[][] rawValues = rawValues(values);
  606. Jedis jedis=null;
  607. Long pushNum=null;
  608. try
  609. {
  610. jedis=jedisClient.getJedis();
  611. pushNum=jedis.lpush(rawKey, rawValues);
  612. }
  613. catch (Exception e)
  614. {
  615. e.printStackTrace();
  616. }finally{
  617. jedisClient.releaseConn(jedis,null);
  618. }
  619. return pushNum;
  620. }
  621. /**
  622. * List数据结构左侧插入值集合
  623. * @param key
  624. * @param values
  625. * @return
  626. * @see
  627. */
  628. public Long lLeftPushAll(String key, Collection<Object> values)
  629. {
  630. final byte[] rawKey = rawString(key);
  631. final byte[][] rawValues = rawValues(values);
  632. Jedis jedis=null;
  633. Long pushNum=null;
  634. try
  635. {
  636. jedis=jedisClient.getJedis();
  637. pushNum=jedis.lpush(rawKey, rawValues);
  638. }
  639. catch (Exception e)
  640. {
  641. e.printStackTrace();
  642. }finally{
  643. jedisClient.releaseConn(jedis,null);
  644. }
  645. return pushNum;
  646. }
  647. /**
  648. * List数据结构如果值存在从左侧插入
  649. * @param key
  650. * @param value
  651. * @return
  652. * @see
  653. */
  654. public Long lLeftPushIfPresent(String key, Object value)
  655. {
  656. final byte[] rawKey = rawString(key);
  657. final byte[] rawValue = rawValue(value);
  658. Jedis jedis=null;
  659. Long pushNum=null;
  660. try
  661. {
  662. jedis=jedisClient.getJedis();
  663. pushNum=jedis.lpushx(rawKey, rawValue);
  664. }
  665. catch (Exception e)
  666. {
  667. e.printStackTrace();
  668. }finally{
  669. jedisClient.releaseConn(jedis,null);
  670. }
  671. return pushNum;
  672. }
  673. /**
  674. * List数据结构按优先级从左侧插入
  675. * @param key
  676. * @param pivot
  677. * @param value
  678. * @return
  679. * @see
  680. */
  681. public Long lLeftPush(String key, Object pivot, Object value)
  682. {
  683. final byte[] rawKey = rawString(key);
  684. final byte[] rawPivot = rawValue(pivot);
  685. final byte[] rawValue = rawValue(value);
  686. Jedis jedis=null;
  687. Long pushNum=null;
  688. try
  689. {
  690. jedis=jedisClient.getJedis();
  691. pushNum=jedis.linsert(rawKey, LIST_POSITION.BEFORE, rawPivot, rawValue);
  692. }
  693. catch (Exception e)
  694. {
  695. e.printStackTrace();
  696. }finally{
  697. jedisClient.releaseConn(jedis,null);
  698. }
  699. return pushNum;
  700. }
  701. /**
  702. * List数据结构从右侧弹出值对象
  703. * @param key
  704. * @param clazz
  705. * @return
  706. * @see
  707. */
  708. public Object lRPop(String key,Class clazz){
  709. final byte[] rawKey = rawString(key);
  710. Jedis jedis=null;
  711. byte[] value=null;
  712. try
  713. {
  714. jedis=jedisClient.getJedis();
  715. value=jedis.rpop(rawKey);
  716. return deserializeValue(value,clazz);
  717. }
  718. catch (Exception e)
  719. {
  720. e.printStackTrace();
  721. return null;
  722. }finally{
  723. jedisClient.releaseConn(jedis,null);
  724. }
  725. }
  726. /**
  727. * List数据结构从右侧弹出值的字节数组
  728. * @param key
  729. * @param timeout
  730. * @param unit
  731. * @return
  732. * @see
  733. */
  734. public byte[] lRPop(String key, long timeout, TimeUnit unit)
  735. {
  736. final int tm = (int)TimeoutUtils.toSeconds(timeout, unit);
  737. final byte[] rawKey = rawString(key);
  738. Jedis jedis=null;
  739. try
  740. {
  741. jedis=jedisClient.getJedis();
  742. List<byte[]> bRPop=jedis.brpop(tm, new byte[][] { rawKey });
  743. return CollectionUtils.isEmpty(bRPop) ? null : (byte[])bRPop.get(1);
  744. }
  745. catch (Exception e)
  746. {
  747. e.printStackTrace();
  748. return null;
  749. }finally{
  750. jedisClient.releaseConn(jedis,null);
  751. }
  752. }
  753. /**
  754. * List数据结构右侧插入值
  755. * @param key
  756. * @param values
  757. * @return
  758. * @see
  759. */
  760. public Long lRightPush(String key, Object value)
  761. {
  762. final byte[] rawKey = rawString(key);
  763. final byte[] rawValue = rawValue(value);
  764. Jedis jedis=null;
  765. Long pushNum=null;
  766. try
  767. {
  768. jedis=jedisClient.getJedis();
  769. pushNum=jedis.rpush(rawKey, new byte[][] { rawValue });
  770. }
  771. catch (Exception e)
  772. {
  773. e.printStackTrace();
  774. }finally{
  775. jedisClient.releaseConn(jedis,null);
  776. }
  777. return pushNum;
  778. }
  779. /**
  780. * List数据结构右侧插入值数组
  781. * @param key
  782. * @param values
  783. * @return
  784. * @see
  785. */
  786. public Long lRightPushAll(String key, Object[] values)
  787. {
  788. final byte[] rawKey = rawString(key);
  789. final byte[][] rawValues = rawValues(values);
  790. Jedis jedis=null;
  791. Long pushNum=null;
  792. try
  793. {
  794. jedis=jedisClient.getJedis();
  795. pushNum=jedis.rpush(rawKey, rawValues);
  796. }
  797. catch (Exception e)
  798. {
  799. e.printStackTrace();
  800. }finally{
  801. jedisClient.releaseConn(jedis,null);
  802. }
  803. return pushNum;
  804. }
  805. /**
  806. * List数据结构右侧插入值集合
  807. * @param key
  808. * @param values
  809. * @return
  810. * @see
  811. */
  812. public Long lRightPushAll(String key, Collection<Object> values)
  813. {
  814. final byte[] rawKey = rawString(key);
  815. final byte[][] rawValues = rawValues(values);
  816. Jedis jedis=null;
  817. Long pushNum=null;
  818. try
  819. {
  820. jedis=jedisClient.getJedis();
  821. pushNum=jedis.rpush(rawKey, rawValues);
  822. }
  823. catch (Exception e)
  824. {
  825. e.printStackTrace();
  826. }finally{
  827. jedisClient.releaseConn(jedis,null);
  828. }
  829. return pushNum;
  830. }
  831. /**
  832. * List数据结构如果值存在从右侧插入
  833. * @param key
  834. * @param value
  835. * @return
  836. * @see
  837. */
  838. public Long lRightPushIfPresent(String key, Object value)
  839. {
  840. final byte[] rawKey = rawString(key);
  841. final byte[] rawValue = rawValue(value);
  842. Jedis jedis=null;
  843. Long pushNum=null;
  844. try
  845. {
  846. jedis=jedisClient.getJedis();
  847. pushNum=jedis.rpushx(rawKey, rawValue);
  848. }
  849. catch (Exception e)
  850. {
  851. e.printStackTrace();
  852. }finally{
  853. jedisClient.releaseConn(jedis,null);
  854. }
  855. return pushNum;
  856. }
  857. /**
  858. * List数据结构按优先级从右侧插入
  859. * @param key
  860. * @param pivot
  861. * @param value
  862. * @return
  863. * @see
  864. */
  865. public Long lRightPush(String key, Object pivot, Object value)
  866. {
  867. final byte[] rawKey = rawString(key);
  868. final byte[] rawPivot = rawValue(pivot);
  869. final byte[] rawValue = rawValue(value);
  870. Jedis jedis=null;
  871. Long pushNum=null;
  872. try
  873. {
  874. jedis=jedisClient.getJedis();
  875. pushNum=jedis.linsert(rawKey, LIST_POSITION.AFTER, rawPivot, rawValue);
  876. }
  877. catch (Exception e)
  878. {
  879. e.printStackTrace();
  880. }finally{
  881. jedisClient.releaseConn(jedis,null);
  882. }
  883. return pushNum;
  884. }
  885. /**
  886. * List数据结构从一个列表的右侧弹出值从另一个列表的左侧插入
  887. * @param sourceKey
  888. * @param destinationKey
  889. * @return
  890. * @see
  891. */
  892. public byte[] rightPopAndLeftPush(String sourceKey, String destinationKey){
  893. final byte[] rawSourceKey = rawString(sourceKey);
  894. final byte[] rawDestKey = rawString(destinationKey);
  895. Jedis jedis=null;
  896. try
  897. {
  898. jedis=jedisClient.getJedis();
  899. return jedis.rpoplpush(rawSourceKey, rawDestKey);
  900. }
  901. catch (Exception e)
  902. {
  903. e.printStackTrace();
  904. return null;
  905. }finally{
  906. jedisClient.releaseConn(jedis,null);
  907. }
  908. }
  909. /**
  910. * List数据结构从一个列表的右侧弹出值从另一个列表的左侧插入
  911. * @param sourceKey
  912. * @param destinationKey
  913. * @param timeout
  914. * @param unit
  915. * @return
  916. * @see
  917. */
  918. public byte[] rightPopAndLeftPush(String sourceKey, String destinationKey, long timeout, TimeUnit unit){
  919. final int tm = (int)TimeoutUtils.toSeconds(timeout, unit);
  920. final byte[] rawSourceKey = rawString(sourceKey);
  921. final byte[] rawDestKey = rawString(destinationKey);
  922. Jedis jedis=null;
  923. try
  924. {
  925. jedis=jedisClient.getJedis();
  926. return jedis.brpoplpush(rawSourceKey, rawDestKey,tm);
  927. }
  928. catch (Exception e)
  929. {
  930. e.printStackTrace();
  931. return null;
  932. }finally{
  933. jedisClient.releaseConn(jedis,null);
  934. }
  935. }
  936. /**
  937. * List数据结构替换掉在键的指定索引处的值
  938. * @param key
  939. * @param index
  940. * @param value
  941. * @see
  942. */
  943. public void set(String key, final long index, Object value){
  944. final byte[] rawKey = rawString(key);
  945. final byte[] rawValue = rawValue(value);
  946. Jedis jedis=null;
  947. try
  948. {
  949. jedis=jedisClient.getJedis();
  950. jedis.lset(rawKey, index, rawValue);
  951. }
  952. catch (Exception e)
  953. {
  954. e.printStackTrace();
  955. }finally{
  956. jedisClient.releaseConn(jedis,null);
  957. }
  958. }
  959. /**
  960. * List数据结构按索引位置截取指定键的列表
  961. * @param key
  962. * @param start
  963. * @param end
  964. * @see
  965. */
  966. public void trim(String key, final long start, long end){
  967. final byte[] rawKey = rawString(key);
  968. Jedis jedis=null;
  969. try
  970. {
  971. jedis=jedisClient.getJedis();
  972. jedis.ltrim(rawKey, start, end);
  973. }
  974. catch (Exception e)
  975. {
  976. e.printStackTrace();
  977. }finally{
  978. jedisClient.releaseConn(jedis,null);
  979. }
  980. }
  981. /**
  982. * List数据结构序列化值
  983. * @param values
  984. * @return
  985. * @see
  986. */
  987. public byte[][] rawValues(Collection<Object> values)
  988. {
  989. Assert.notEmpty(values, "Values must not be 'null' or empty.");
  990. Assert.noNullElements(values.toArray(), "Values must not contain 'null' value.");
  991. byte[][] rawValues = new byte[values.size()][];
  992. int i = 0;
  993. for (Iterator<Object> localIterator = values.iterator(); localIterator.hasNext(); ) {
  994. Object value = localIterator.next();
  995. rawValues[(i++)] = rawValue(value);
  996. }
  997. return rawValues;
  998. }
  999. /**
  1000. * List数据结构值反序列化
  1001. * @param value
  1002. * @param clazz
  1003. * @return
  1004. * @see
  1005. */
  1006. public Object deserializeValue(byte[] value,Class clazz)
  1007. {
  1008. if (value == null) {
  1009. return null;
  1010. }
  1011. return FastJson2JsonSerializer.deserialize(value,clazz);
  1012. }
  1013. /**
  1014. * List数据结构值集合反序列化
  1015. * @param rawValues
  1016. * @param clazz
  1017. * @return
  1018. * @see
  1019. */
  1020. public List<Object> deserializeValues(List<byte[]> rawValues,Class clazz){
  1021. if (rawValues == null) {
  1022. return null;
  1023. }
  1024. List<Object> values =new ArrayList<Object>();
  1025. for (byte[] bs : rawValues) {
  1026. values.add(FastJson2JsonSerializer.deserialize(bs,clazz));
  1027. }
  1028. return values;
  1029. }
  1030. /**
  1031. * ################Set##################
  1032. */
  1033. /**
  1034. * Set数据结构中是否存在某个值
  1035. * @param key
  1036. * @param o
  1037. * @return
  1038. * @see
  1039. */
  1040. public Boolean sIsMember(String key, Object o)
  1041. {
  1042. final byte[] rawKey = rawString(key);
  1043. final byte[] rawValue = rawValue(o);
  1044. Jedis jedis=null;
  1045. Boolean isMember=null;
  1046. try
  1047. {
  1048. jedis=jedisClient.getJedis();
  1049. isMember=jedis.sismember(rawKey, rawValue);
  1050. return isMember;
  1051. }
  1052. catch (Exception e)
  1053. {
  1054. e.printStackTrace();
  1055. return false;
  1056. }finally{
  1057. jedisClient.releaseConn(jedis,null);
  1058. }
  1059. }
  1060. /**
  1061. * Set数据结构中值的集合
  1062. * @param key
  1063. * @param clazz
  1064. * @return
  1065. * @see
  1066. */
  1067. public Set<Object> sMembers(String key,Class clazz)
  1068. {
  1069. final byte[] rawKey = rawString(key);
  1070. Jedis jedis=null;
  1071. Set<byte[]> rawValues=null;
  1072. try
  1073. {
  1074. jedis=jedisClient.getJedis();
  1075. rawValues=jedis.smembers(rawKey);
  1076. }
  1077. catch (Exception e)
  1078. {
  1079. e.printStackTrace();
  1080. }finally{
  1081. jedisClient.releaseConn(jedis,null);
  1082. }
  1083. return deserializeValues(rawValues,clazz);
  1084. }
  1085. /**
  1086. * Set数据结构移除指定值
  1087. * @param key
  1088. * @param values
  1089. * @return
  1090. * @see
  1091. */
  1092. public Long sRem(String key, Object[] values) {
  1093. final byte[] rawKey = rawString(key);
  1094. final byte[][] rawValues = rawValues(values);
  1095. Jedis jedis=null;
  1096. Long remNum=null;
  1097. try
  1098. {
  1099. jedis=jedisClient.getJedis();
  1100. remNum=jedis.srem(rawKey, rawValues);
  1101. }
  1102. catch (Exception e)
  1103. {
  1104. e.printStackTrace();
  1105. }finally{
  1106. jedisClient.releaseConn(jedis,null);
  1107. }
  1108. return remNum;
  1109. }
  1110. /**
  1111. * Set数据结构获取键包含的值集合大小
  1112. * @param key
  1113. * @return
  1114. * @see
  1115. */
  1116. public Long sCard(String key) {
  1117. final byte[] rawKey = rawString(key);
  1118. Jedis jedis=null;
  1119. Long size=null;
  1120. try
  1121. {
  1122. jedis=jedisClient.getJedis();
  1123. size=jedis.scard(rawKey);
  1124. }
  1125. catch (Exception e)
  1126. {
  1127. e.printStackTrace();
  1128. }finally{
  1129. jedisClient.releaseConn(jedis,null);
  1130. }
  1131. return size;
  1132. }
  1133. /**
  1134. * Set数据结构反序列化值集合
  1135. * @param rawValues
  1136. * @param clazz
  1137. * @return
  1138. * @see
  1139. */
  1140. public Set<Object> deserializeValues(Set<byte[]> rawValues,Class clazz){
  1141. if (rawValues == null) {
  1142. return null;
  1143. }
  1144. Set<Object> values =new LinkedHashSet<Object>(rawValues.size());
  1145. for (byte[] bs : rawValues) {
  1146. values.add(FastJson2JsonSerializer.deserialize(bs,clazz));
  1147. }
  1148. return values;
  1149. }
  1150. //Set值序列化
  1151. public byte[][] rawValues(Object[] values){
  1152. if (null==values)
  1153. {
  1154. return null;
  1155. }
  1156. byte[][] rawValues = new byte[values.length][];
  1157. int i = 0;
  1158. for (Object value : values) {
  1159. rawValues[(i++)] = rawValue(value);
  1160. }
  1161. return rawValues;
  1162. }
  1163. //序列化Map数据结构
  1164. public Map<Object, Object> deserializeHashMap(Map<byte[], byte[]> entries,Class clazz){
  1165. if (entries == null) {
  1166. return null;
  1167. }
  1168. Map<Object, Object> map = new LinkedHashMap<Object, Object>(entries.size());
  1169. for (Map.Entry<byte[], byte[]> entry : entries.entrySet()) {
  1170. map.put(StringSerializer.deserialize((byte[])entry.getKey()), FastJson2JsonSerializer.deserialize((byte[])entry.getValue(), clazz));
  1171. }
  1172. return map;
  1173. }
  1174. public JedisClient getJedisClient()
  1175. {
  1176. return jedisClient;
  1177. }
  1178. public void setJedisClient(JedisClient jedisClient)
  1179. {
  1180. this.jedisClient = jedisClient;
  1181. }
  1182. }

发表评论

表情:
评论列表 (有 0 条评论,312人围观)

还没有评论,来说两句吧...

相关阅读

    相关 spring集成redis

        sprig集成redis,我的项目架构是springMVC+spring+ibatis 用maven管理 ,下面看代码 1. 在pom文件引入redis依赖包

    相关 Spring集成Redis

    最近在做一个关于群聊的项目,由于聊天要求实时性,不可能直接访问数据库,所以使用了Redis来做缓存,这里将使用过程中遇到的问题记录一下。 使用Redis之前需要合理设计存储