在我们的Spring 4项目中,我们希望有涉及Redis和Hibernate的数据库事务.每当Hibernate失败时,例如由于乐观锁定,Redis事务也应该中止.
这似乎有用
单线程事务执行.
多线程事务执行,只要事务只包含一个Redis调用.
如果从我们的配置中排除Hibernate,则使用多个Redis调用执行多线程事务.
一旦事务包含多个Redis调用,并且Hibernate配置为参与事务,连接绑定和多线程似乎就会出现问题.线程被卡住RedisConnectionUtils.bindConnection()
,可能是因为JedisPool
连接耗尽.
这可以如下再现.
@Service public class TransactionalService { @Autowired @Qualifier("redisTemplate") private RedisTemplateredisTemplate; @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() 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( ) 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
Hibernate配置
@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中的错误还是我们的配置有问题?