복붙노트

[REDIS] 트랜잭션이 풀 다음 블록에 연결을 반환하지 봄 RedisConnectionFactory 때 소진

REDIS

트랜잭션이 풀 다음 블록에 연결을 반환하지 봄 RedisConnectionFactory 때 소진

연결 풀과 연결 팩토리를 작성하기위한 내 구성. 나는 연결 풀을해야합니까. 이 코드의 대부분은 내가 특정 이유로 비활성화 봄의 RedisAutoConfiguration에서 복사됩니다.

@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class JedisConfiguration implements RedisConfiguration {

    @Bean
    @Scope("prototype")
    @Override
    public RedisConnectionFactory connectionFactory(RedisProperties redisProperties) {
        return createFactory(redisProperties);
    }

    private static JedisConnectionFactory applyProperties(RedisProperties properties, JedisConnectionFactory factory) {
        factory.setHostName(properties.getHost());
        factory.setPort(properties.getPort());
        factory.setDatabase(properties.getDatabase());
        return factory;
    }

    private static JedisPoolConfig jedisPoolConfig(RedisProperties properties) {
        return Optional.ofNullable(properties.getPool())
                       .map(props -> {
                           JedisPoolConfig config = new JedisPoolConfig();
                           config.setMaxTotal(props.getMaxActive());
                           config.setMaxIdle(props.getMaxIdle());
                           config.setMinIdle(props.getMinIdle());
                           config.setMaxWaitMillis(props.getMaxWait());
                           return config;
                       })
                       .orElseGet(JedisPoolConfig::new);
    }

    public static JedisConnectionFactory createFactory(RedisProperties properties) {
        return applyProperties(properties, new JedisConnectionFactory(jedisPoolConfig(properties)));
    }
}

내가 문자열 키가 "A"는 "B는"해시 "C"매핑 클래스 A, 각각 B와 C에서 직렬화 된 JSON 문자열 해시 키와 해시 값으로 매핑합니다.

>은 :: toString - - B와 C에 대한> JSON (A)와 같은 즉 "A"입니다

@Component
public final class UseCase implements InitializingBean {

    private static final String A_KEY = "A";
    private static final String B_KEY = "B";
    private static final String C_KEY = "C";

    private final RedisConnectionFactory factory;
    private final ObjectMapper objectMapper;
    private HashOperations<String, String, A> aMap;
    private HashOperations<String, String, B> bMap;
    private HashOperations<String, String, C> cMap;
    private RedisTemplate<String, ?> template;

    private UseCase(RedisConnectionFactory factory, ObjectMapper objectMapper) {
        this.factory = factory;
        this.objectMapper = objectMapper;
    }

    private <T> RedisTemplate<String, ?> hashMap(Class<T> vClass) {
        RedisTemplate<String, ?> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(stringSerializer());
        redisTemplate.setHashKeySerializer(stringSerializer());
        redisTemplate.setHashValueSerializer(jacksonSerializer(vClass));
        return configure(redisTemplate);
    }


    private <K, V> RedisTemplate<K, V> configure(RedisTemplate<K, V> redisTemplate) {
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    private <T> RedisSerializer<T> jacksonSerializer(Class<T> clazz) {
        Jackson2JsonRedisSerializer<T> serializer = new Jackson2JsonRedisSerializer<>(clazz);
        serializer.setObjectMapper(objectMapper);
        return serializer;
    }

    private RedisSerializer<String> stringSerializer() {
        return new StringRedisSerializer();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        template = hashMap(String.class);
        aMap = hashMap(A.class).opsForHash();
        bMap = hashMap(B.class).opsForHash();
        cMap = hashMap(C.class).opsForHash();
    }

    void put(A a, B b, C c) {
        template.multi();
        aMap.put(A_KEY, a.toString(), a);
        bMap.put(B_KEY, b.toString(), b);
        cMap.put(C_KEY, c.toString(), c);
        template.exec();
    }

    A getA(String aKey) {
        return aMap.get(A_KEY, aKey);
    }

}

그러나 나는 내가 조금 회의적하지만 내 문제는 지난 2로, 즉 하나 개의 작품을 입증하는 시험이있다. 디버깅 후, 나는 연결이 중 수술 후 풀에 반환되지 않으며이 소진 때 따라서 수영장이 차단됩니다 것을 관찰했다.

반환 시도하지만, 두 가지 아래에 실패하기 때문에 연결에 호출되지 않습니다. RedisConnectionUtils에서 촬영

// release transactional/read-only and non-transactional/non-bound connections.
// transactional connections for read-only transactions get no synchronizer registered
if (isConnectionTransactional(conn, factory)
        && TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
    unbindConnection(factory);
} else if (!isConnectionTransactional(conn, factory)) {
    if (log.isDebugEnabled()) {
        log.debug("Closing Redis Connection");
    }
    conn.close();
}

해결법

  1. ==============================

    1.나는 문제가 간부 인 ()를 호출하는 것은이 풀에 반환 될 수 있도록 실제로 연결을 완료하는 템플릿을 말하지 않는다는 것입니다 생각합니다.

    나는 문제가 간부 인 ()를 호출하는 것은이 풀에 반환 될 수 있도록 실제로 연결을 완료하는 템플릿을 말하지 않는다는 것입니다 생각합니다.

    워드 프로세서에 따르면 당신은 SessionCallback에 코드를 포장하고 RedisTemplate.execute 그것을 실행하는 거 야 (SessionCallback 콜백) 콜백이 실행 된 후 풀에 연결을 반환하는.

    이 같이 :

    template.execute(new SessionCallback<List<Object>>() {
        public List<Object> execute(RedisOperations operations) throws DataAccessException {
            operations.multi();
            aMap.put(A_KEY, a.toString(), a);
            bMap.put(B_KEY, b.toString(), b);
            cMap.put(C_KEY, c.toString(), c);
            return operations.exec();
        }
    });
    

    봄 데이터 레디 스도 @Transactional에 대한 지원이있는 당신을 위해 자동으로 바인드 / 언 바인드 연결되지만, 가로 챌 수 있습니다 (즉, 그것이 마지막이 될 수 없습니다) 및 트랜잭션이있는 경우에만 시작됩니다 빈의 메소드를 구현하도록 요구 빈 외부에서 실행되는 (즉, 동일하지 클래스 또는 하위 / 상위 클래스에서 다른 방법으로).

    이미 redisTemplate.setEnableTransactionSupport (사실)와 템플릿에 트랜잭션 지원을 가능하게하고, 그래서 당신은 갈 수 있어야한다 :

    @Transactional
    public void put(A a, B b, C c) {
        aMap.put(A_KEY, a.toString(), a);
        bMap.put(B_KEY, b.toString(), b);
        cMap.put(C_KEY, c.toString(), c);
    }
    
  2. from https://stackoverflow.com/questions/46238811/spring-redisconnectionfactory-with-transaction-not-returning-connection-to-pool by cc-by-sa and MIT license