Spring data redis, проблема многопоточности с Jedis

Я использую Redis в многопоточном Java-приложении и получаю прерывистые ClassCastExceptions. Чтение различных обсуждений показало, что это может быть связано с тем, что экземпляр соединения Jedis используется несколькими потоками (https://github.com/xetorthio/jedis/issues/359). Предлагаемое решение состоит в использовании потокобезопасного JedisPool.

Я настроил Redis через поддержку Spring Redis с помощью RedisTemplate. Следует отметить, что я использую несколько шаблонов (для поддержки разных моделей сериализации и десериализации). Вот фрагмент моей конфигурации -

<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$3.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$19.doInRedis(DefaultZSetOperations.java:283)
    at org.springframework.data.redis.core.DefaultZSetOperations$19.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

person user2111500    schedule 02.03.2015    source источник
comment
Есть ли шанс, что вы можете дать немного больше информации о том, что вы именно делаете, о трассировке стека, которая у вас есть, и откуда вы получаете ClassCastExeption? Или еще лучше суть или репо с тестом, воспроизводящим ошибку...   -  person Christoph Strobl    schedule 03.03.2015
comment
В вопрос добавлена ​​трассировка стека из двух потоков, я получаю аналогичную ошибку из других потоков для различных операций Redis.   -  person user2111500    schedule 03.03.2015
comment
Вы даже нашли решение или причину? Я получаю такие же исключения.   -  person Adriaan    schedule 29.01.2019


Ответы (1)


Забыл опубликовать обновление по этому поводу здесь.

В моем случае мой анализ заключался в том, что пул соединений по умолчанию использовал ForkJoinPool, который работает в режиме work-stealing. Это заставило другой поток взять на себя часть результатов операции и выдало ClassCastException, когда было несоответствие в типе результата.

Это решило проблему, которая у меня была.

person user2111500    schedule 31.01.2019
comment
Как вы решили проблему? столкнулся с тем же - person Dmitry Shabalin; 26.03.2021