Spring data redis,Jedis 的多线程问题
Spring data redis, multi-threading issue with Jedis
我在大量多线程的 java 应用程序中使用 Redis,并出现间歇性的 ClassCastException
。通过阅读各种讨论似乎指出这可能是因为 Jedis 连接实例在多个线程之间共享 (https://github.com/xetorthio/jedis/issues/359)。建议的解决方案是使用线程安全的 JedisPool。
我已经通过使用 RedisTemplate Spring redis 支持配置了 redis。需要注意的是我正在使用多个模板(以支持不同的序列化和反序列化模型)。这是我的配置片段 -
<bean id="jedisConnFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
redis:usePool="true" redis:poolConfig-ref="jedisPoolConfig" redis:hostName="${redis.datasource.hostName}"
redis:database="${redis.database.index}" redis:port="${redis.datastore.port}"/>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.conn.maxIdle}"/>
<property name="maxTotal" value="${redis.conn.maxTotal}"/>
<property name="minIdle" value="${redis.conn.minIdle}"/>
<property name="testOnBorrow" value="true"/>
</bean>
<bean id="redisTemplate"
class="org.springframework.data.redis.core.RedisTemplate"
redis:connectionFactory-ref="jedisConnFactory"
redis:keySerializer-ref="redisStringSerializer"
redis:valueSerializer-ref="redisStringSerializer"
redis:defaultSerializer-ref="redisStringSerializer"/>
注意 usePool=true
的使用提示 spring 使用 JedisPool。查看 spring 代码还表明 spring 正在正确处理资源分配和释放。
如果您能帮助识别问题,我们将不胜感激。
编辑:堆栈跟踪 -
Thread 1:
[ERROR] [03/01/2015 07:05:32.044] [events-system-akka.actor.default-dispatcher-2281] [akka://events-system/user/$YN/$b/$b/$b] java.lang.Long cannot be cast to java.util.List
java.lang.ClassCastException: java.lang.Long cannot be cast to java.util.List
at redis.clients.jedis.Connection.getRawObjectMultiBulkReply(Connection.java:230)
at redis.clients.jedis.Connection.getObjectMultiBulkReply(Connection.java:236)
at redis.clients.jedis.BinaryJedis.zscan(BinaryJedis.java:3608)
at org.springframework.data.redis.connection.jedis.JedisConnection.doScan(JedisConnection.java:2998)
at org.springframework.data.redis.core.KeyBoundCursor.doScan(KeyBoundCursor.java:39)
at org.springframework.data.redis.core.ScanCursor.scan(ScanCursor.java:85)
at org.springframework.data.redis.core.ScanCursor.hasNext(ScanCursor.java:168)
at org.springframework.data.redis.core.ConvertingCursor.hasNext(ConvertingCursor.java:56)
...
application specific stack trace
...
Thread 2:
[ERROR] [03/01/2015 07:03:07.295] [events-system-akka.actor.default-dispatcher-2273] [akka://events-system/user/$VN/$b/$b/$b] Unknown redis exception; nested exception is java.lang.ClassCastException: [B cannot be cast to java.lang.Long
org.springframework.data.redis.RedisSystemException: Unknown redis exception; nested exception is java.lang.ClassCastException: [B cannot be cast to java.lang.Long
at org.springframework.data.redis.FallbackExceptionTranslationStrategy.getFallback(FallbackExceptionTranslationStrategy.java:48)
at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:38)
at org.springframework.data.redis.connection.jedis.JedisConnection.convertJedisAccessException(JedisConnection.java:195)
at org.springframework.data.redis.connection.jedis.JedisConnection.zRem(JedisConnection.java:2321)
at org.springframework.data.redis.core.DefaultZSetOperations.doInRedis(DefaultZSetOperations.java:283)
at org.springframework.data.redis.core.DefaultZSetOperations.doInRedis(DefaultZSetOperations.java:280)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:190)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:152)
at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:85)
at org.springframework.data.redis.core.DefaultZSetOperations.remove(DefaultZSetOperations.java:280)
...
application specific stack trace
...
Caused by: java.lang.ClassCastException: [B cannot be cast to java.lang.Long
at redis.clients.jedis.Connection.getIntegerReply(Connection.java:210)
at redis.clients.jedis.BinaryJedis.zrem(BinaryJedis.java:1624)
at org.springframework.data.redis.connection.jedis.JedisConnection.zRem(JedisConnection.java:2319)
... 21 more
忘记post在这里更新。
在我的例子中,我的分析是默认情况下连接池使用 ForkJoinPool
,它在 work-stealing
模式下工作。这使得另一个线程接管了操作的结果部分,并在结果类型不匹配时给出 ClassCastException
。
这确实解决了我遇到的问题。
我在大量多线程的 java 应用程序中使用 Redis,并出现间歇性的 ClassCastException
。通过阅读各种讨论似乎指出这可能是因为 Jedis 连接实例在多个线程之间共享 (https://github.com/xetorthio/jedis/issues/359)。建议的解决方案是使用线程安全的 JedisPool。
我已经通过使用 RedisTemplate Spring redis 支持配置了 redis。需要注意的是我正在使用多个模板(以支持不同的序列化和反序列化模型)。这是我的配置片段 -
<bean id="jedisConnFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
redis:usePool="true" redis:poolConfig-ref="jedisPoolConfig" redis:hostName="${redis.datasource.hostName}"
redis:database="${redis.database.index}" redis:port="${redis.datastore.port}"/>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.conn.maxIdle}"/>
<property name="maxTotal" value="${redis.conn.maxTotal}"/>
<property name="minIdle" value="${redis.conn.minIdle}"/>
<property name="testOnBorrow" value="true"/>
</bean>
<bean id="redisTemplate"
class="org.springframework.data.redis.core.RedisTemplate"
redis:connectionFactory-ref="jedisConnFactory"
redis:keySerializer-ref="redisStringSerializer"
redis:valueSerializer-ref="redisStringSerializer"
redis:defaultSerializer-ref="redisStringSerializer"/>
注意 usePool=true
的使用提示 spring 使用 JedisPool。查看 spring 代码还表明 spring 正在正确处理资源分配和释放。
如果您能帮助识别问题,我们将不胜感激。
编辑:堆栈跟踪 -
Thread 1:
[ERROR] [03/01/2015 07:05:32.044] [events-system-akka.actor.default-dispatcher-2281] [akka://events-system/user/$YN/$b/$b/$b] java.lang.Long cannot be cast to java.util.List
java.lang.ClassCastException: java.lang.Long cannot be cast to java.util.List
at redis.clients.jedis.Connection.getRawObjectMultiBulkReply(Connection.java:230)
at redis.clients.jedis.Connection.getObjectMultiBulkReply(Connection.java:236)
at redis.clients.jedis.BinaryJedis.zscan(BinaryJedis.java:3608)
at org.springframework.data.redis.connection.jedis.JedisConnection.doScan(JedisConnection.java:2998)
at org.springframework.data.redis.core.KeyBoundCursor.doScan(KeyBoundCursor.java:39)
at org.springframework.data.redis.core.ScanCursor.scan(ScanCursor.java:85)
at org.springframework.data.redis.core.ScanCursor.hasNext(ScanCursor.java:168)
at org.springframework.data.redis.core.ConvertingCursor.hasNext(ConvertingCursor.java:56)
...
application specific stack trace
...
Thread 2:
[ERROR] [03/01/2015 07:03:07.295] [events-system-akka.actor.default-dispatcher-2273] [akka://events-system/user/$VN/$b/$b/$b] Unknown redis exception; nested exception is java.lang.ClassCastException: [B cannot be cast to java.lang.Long
org.springframework.data.redis.RedisSystemException: Unknown redis exception; nested exception is java.lang.ClassCastException: [B cannot be cast to java.lang.Long
at org.springframework.data.redis.FallbackExceptionTranslationStrategy.getFallback(FallbackExceptionTranslationStrategy.java:48)
at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:38)
at org.springframework.data.redis.connection.jedis.JedisConnection.convertJedisAccessException(JedisConnection.java:195)
at org.springframework.data.redis.connection.jedis.JedisConnection.zRem(JedisConnection.java:2321)
at org.springframework.data.redis.core.DefaultZSetOperations.doInRedis(DefaultZSetOperations.java:283)
at org.springframework.data.redis.core.DefaultZSetOperations.doInRedis(DefaultZSetOperations.java:280)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:190)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:152)
at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:85)
at org.springframework.data.redis.core.DefaultZSetOperations.remove(DefaultZSetOperations.java:280)
...
application specific stack trace
...
Caused by: java.lang.ClassCastException: [B cannot be cast to java.lang.Long
at redis.clients.jedis.Connection.getIntegerReply(Connection.java:210)
at redis.clients.jedis.BinaryJedis.zrem(BinaryJedis.java:1624)
at org.springframework.data.redis.connection.jedis.JedisConnection.zRem(JedisConnection.java:2319)
... 21 more
忘记post在这里更新。
在我的例子中,我的分析是默认情况下连接池使用 ForkJoinPool
,它在 work-stealing
模式下工作。这使得另一个线程接管了操作的结果部分,并在结果类型不匹配时给出 ClassCastException
。
这确实解决了我遇到的问题。