8

Spring 4 プロジェクトでは、Redis と Hibernate を含むデータベース トランザクションが必要です。楽観的ロックなどで Hibernate が失敗するたびに、Redis トランザクションも中止する必要があります。

これはうまくいくようです

  1. シングルスレッドのトランザクション実行。
  2. トランザクションに単一の Redis 呼び出しのみが含まれている限り、マルチスレッド トランザクションの実行。
  3. Hibernate が構成から除外されている場合、複数の Redis 呼び出しによるマルチスレッド トランザクションの実行。

トランザクションに複数の Redis 呼び出しが含まれ、Hibernate がトランザクションに参加するように設定されるとすぐに、接続バインディングとマルチスレッドに問題があるようです。RedisConnectionUtils.bindConnection()の接続が不足しているため、スレッドが でスタックしてJedisPoolいます。

これは次のように再現できます。

@Service
public class TransactionalService {

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate<String, Object> redisTemplate;

    @Transactional
    public void processTask(int i){

        redisTemplate.convertAndSend("testChannel", new Message());
        redisTemplate.convertAndSend("testChannel", new Message());
    }
}

ThreadPoolTaskExecutorマルチスレッド トランザクションをシミュレートするために、50 のコア プール サイズを使用します。

@Service
public class TaskRunnerService {

    @Autowired
    private TaskExecutor taskExecutor;

    @Autowired
    private TransactionalService transactionalService;

    public void runTasks() {

        for (int i = 0; i < 100; i++) {

            final int j = i;

            taskExecutor.execute(new Runnable() {

                @Override
                public void run() {
                    transactionalService.processTask(j);
                }
            });
        }
    }
}

これを実行すると、すべての taskExecutor スレッドが JedisPool.getResource() でハングします。

  "taskExecutor-1" - Thread t@18
   java.lang.Thread.State: WAITING
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for <1b83c92c> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    at org.apache.commons.pool2.impl.LinkedBlockingDeque.takeFirst(LinkedBlockingDeque.java:524)
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:438)
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:361)
    at redis.clients.util.Pool.getResource(Pool.java:40)
    at redis.clients.jedis.JedisPool.getResource(JedisPool.java:84)
    at redis.clients.jedis.JedisPool.getResource(JedisPool.java:10)
    at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:90)
    at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:143)
    at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:41)
    at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:128)
    at org.springframework.data.redis.core.RedisConnectionUtils.bindConnection(RedisConnectionUtils.java:66)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:175)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:152)
    at org.springframework.data.redis.core.RedisTemplate.convertAndSend(RedisTemplate.java:675)
    at test.TransactionalService.processTask(TransactionalService.java:23)
    at test.TransactionalService$$FastClassBySpringCGLIB$$9b3de279.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:708)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
  at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
  at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
  at test.TransactionalService$$EnhancerBySpringCGLIB$$a1b3ba03.processTask(<generated>)
  at test.TaskRunnerService$1.run(TaskRunnerService.java:28)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
    - locked <7d528cf7> (a java.util.concurrent.ThreadPoolExecutor$Worker)   

Redis 構成

@Configuration
public class RedisConfig {

    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setPoolConfig(new JedisPoolConfig());
        return jedisConnectionFactory;
    }

    @Bean
    public Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new     Jackson2JsonRedisSerializer(Object.class);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper());
        return jackson2JsonRedisSerializer;
    }

    @Bean
    public StringRedisSerializer stringRedisSerializer() {
        return new StringRedisSerializer();
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        redisTemplate.setKeySerializer(stringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
        redisTemplate.setEnableTransactionSupport(true);
        return redisTemplate;
    }

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        return objectMapper;
    }
}

ハイバネート構成

@EnableTransactionManagement
@Configuration
public class HibernateConfig {

    @Bean
    public LocalContainerEntityManagerFactoryBean admin() {

        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new     LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        entityManagerFactoryBean.setPersistenceUnitName("test");

        return entityManagerFactoryBean;
    }

    @Bean
    public JpaTransactionManager transactionManager(
            @Qualifier("admin") LocalContainerEntityManagerFactoryBean     entityManagerFactoryBean) {

        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactoryBean.getObject());
        transactionManager.setDataSource(entityManagerFactoryBean.getDataSource());

        return transactionManager;
    }
}

これは spring-data-redis のバグですか、それとも構成に問題がありますか?

4

2 に答える 2

0

私は非常によく似た問題を抱えていましたが、スレッドが実際に解放されていない場合、maxTotal スレッドをぶつけることは私を悩ませました。代わりに、get と set をすばやく実行するコードがいくつかありました。これを SessionCallback に入れると、動作が大幅に改善されました。それが役立つことを願っています。

于 2015-09-09T01:51:22.833 に答える