source

Spring RedisConnectionFactory 트랜잭션이 Pool에 연결을 반환하지 않은 다음 소진 시 차단됩니다.

lovecheck 2023. 9. 9. 09:38
반응형

Spring RedisConnectionFactory 트랜잭션이 Pool에 연결을 반환하지 않은 다음 소진 시 차단됩니다.

연결 풀을 사용하여 연결 팩토리를 만들기 위한 내 구성입니다.접속 풀이 있습니다.이 코드의 대부분은 스프링스에서 복사한 것입니다.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"문자열 해시 키와 클래스에서 직렬화된 해시 값 json으로 해시 맵에 매핑A,B,그리고.C각각 다음과 같다.

그것은"A"->A::toString->json(A)마찬가지로B그리고.C.

@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);
    }

}

기대들

  1. 연결이 손실되거나 손상된 경우 한 번의 연결만으로 풋 작업이 실행되고 실패해야 합니다.
  2. Put 작업의 경우 다중 호출 시 연결을 얻고 exec 호출 후 Pool로 돌아갑니다.
  3. getA 작업의 경우 실행 후 연결이 풀로 반환됩니다.

1개가 효과가 있다는 것을 증명할 수 있는 테스트가 있지만, 조금 회의적이지만 문제는 나머지 2개입니다.디버깅 후 어느 작업을 해도 Pool로 연결이 돌아가지 않아 Pool이 소진되면 차단되는 것을 관찰했습니다.

반환이 시도되었지만 아래의 두 분기가 실패했기 때문에 연결에서 호출되지 않습니다.Taken fromRedisConnectionUtils

// 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. 내가 뭘 잘못하고 있는 거지?
  2. 연결이 풀에 반환되지 않는 이유는 무엇입니까?
  3. 연결을 풀로 되돌리려면 어떻게 해야 합니까?

제 생각에 문제는 그 전화가exec()템플릿에 실제로 연결이 완료되었음을 알려주지 않으므로 풀에 반환할 수 없습니다.

코드를 세션 콜백에 싸서 실행해야 하는 문서에 따라 콜백이 실행된 후에 풀에 연결이 반환되는 세션 콜백을 실행합니다.

다음과 같은 경우:

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();
    }
});

Spring Data Redis는 또한 @Transactional을 지원합니다. @Transactional은 연결을 자동으로 바인딩/결합 해제하지만, 이 메서드를 가로챌 수 있는 빈에 구현해야 합니다(즉, 그럴 수 없습니다).final) 및 거래는 빈 외부에서 실행되는 경우에만 시작됩니다(즉, 동일 클래스 또는 하위/부모 클래스의 다른 메서드에서 실행되지 않음).

을 하도록 하고 하고 템플릿에서 트랜잭션 지원을 이미 활성화하고 .redisTemplate.setEnableTransactionSupport(true);그러니 당신은 가도 좋습니다.

@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);
}

언급URL : https://stackoverflow.com/questions/46238811/spring-redisconnectionfactory-with-transaction-not-returning-connection-to-pool

반응형