'spring-could-gateway: reactor.netty.channel.AbortedException: Connection has been closed BEFORE send operation

I am getting the following exception in the spring gateway. This exception appears when I have configured Redis Sentinel in the gateway, it throughs exception after a couple of requests in the gateway. If I remove the redis config the gatway is fine.

2022-04-25 15:11:13.475 INFO 1 --- [oundedElastic-8] c.y.c.g.GlobalErrorWebExceptionHandler : Global catch for ex=Connection has been closed BEFORE send operation
    2022-04-25 15:11:13.478 ERROR 1 --- [oundedElastic-7] c.y.c.g.GlobalErrorWebExceptionHandler : Unhandled throwable=
    reactor.netty.channel.AbortedException: Connection has been closed BEFORE send operation
    at reactor.netty.channel.AbortedException.beforeSend(AbortedException.java:59) ~[reactor-netty-core-1.0.17.jar!/:1.0.17]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
    Error has been observed at the following site(s):
    __checkpoint ⇢ Handler org.springframework.boot.actuate.endpoint.web.reactive.AbstractWebFluxEndpointHandlerMapping$ReadOperationHandler#handle(ServerWebExchange) [DispatcherHandler] checkpoint ⇢ com.demo.gateway.config.CorsConfig$$Lambda$1120/0x00000008014c8730 [DefaultWebFilterChain]
    *checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
    __checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain] __checkpoint ⇢ HTTP GET "/actuator/health/liveness" [ExceptionHandlingWebHandler]
    Original Stack Trace:
    at reactor.netty.channel.AbortedException.beforeSend(AbortedException.java:59) ~[reactor-netty-core-1.0.17.jar!/:1.0.17]
    at reactor.netty.http.HttpOperations.then(HttpOperations.java:171) ~[reactor-netty-http-1.0.17.jar!/:1.0.17]
    at reactor.netty.ReactorNetty$OutboundThen.<init>(ReactorNetty.java:702) ~[reactor-netty-core-1.0.17.jar!/:1.0.17]
    at reactor.netty.ReactorNetty$OutboundThen.<init>(ReactorNetty.java:695) ~[reactor-netty-core-1.0.17.jar!/:1.0.17]
    at reactor.netty.NettyOutbound.then(NettyOutbound.java:358) ~[reactor-netty-core-1.0.17.jar!/:1.0.17]
    at reactor.netty.http.HttpOperations.send(HttpOperations.java:109) ~[reactor-netty-http-1.0.17.jar!/:1.0.17]
    at org.springframework.http.server.reactive.ReactorServerHttpResponse.writeWithInternal(ReactorServerHttpResponse.java:92) ~[spring-web-5.3.18.jar!/:5.3.18]
    at org.springframework.http.server.reactive.AbstractServerHttpResponse.lambda$null$2(AbstractServerHttpResponse.java:221) ~[spring-web-5.3.18.jar!/:5.3.18]
    at org.springframework.http.server.reactive.AbstractServerHttpResponse.doCommit(AbstractServerHttpResponse.java:297) ~[spring-web-5.3.18.jar!/:5.3.18]
    at org.springframework.http.server.reactive.AbstractServerHttpResponse.lambda$writeWith$5(AbstractServerHttpResponse.java:218) ~[spring-web-5.3.18.jar!/:5.3.18]
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:125) ~[reactor-core-3.4.16.jar!/:3.4.16]
    at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107) ~[reactor-core-3.4.16.jar!/:3.4.16]
    at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2398) ~[reactor-core-3.4.16.jar!/:3.4.16]
    at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.request(FluxContextWrite.java:136) ~[reactor-core-3.4.16.jar!/:3.4.16]
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:110) ~[reactor-core-3.4.16.jar!/:3.4.16]
    at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onSubscribe(FluxContextWrite.java:101) ~[reactor-core-3.4.16.jar!/:3.4.16]
    at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55) ~[reactor-core-3.4.16.jar!/:3.4.16]
    at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.4.16.jar!/:3.4.16]
    at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:157) ~[reactor-core-> >3.4.16.jar!/:3.4.16]
    at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[reactor-core-3.4.16.jar!/:3.4.16]
    at reactor.core.publisher.Operators$MonoInnerProducerBase.complete(Operators.java:2664) ~[reactor-core-3.4.16.jar!/:3.4.16]
    at reactor.core.publisher.MonoSingle$SingleSubscriber.onComplete(MonoSingle.java:180) ~[reactor-core-3.4.16.jar!/:3.4.16]
    at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onCo

gateway pom dependencies are

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.6</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<groupId>com.demo</groupId>
<artifactId>gateway</artifactId>
<version>1.1.1</version>
<name>Gateway</name>
<description>Gateway</description>

<properties>
    <java.version>17</java.version>
    <spring.cloud-version>2021.0.1</spring.cloud-version>
    <modelmapper-spring-boot-starter.version>1.1.0</modelmapper-spring-boot-starter.version>
    <io.jsonwebtoken.version>0.11.2</io.jsonwebtoken.version>
    <bcpkix-jdk15on.version>1.68</bcpkix-jdk15on.version>
    <yc-common-api.version>1.2.6</yc-common-api.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-bootstrap</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>

    <dependency>
        <groupId>com.demo</groupId>
        <artifactId>yc-common-api</artifactId>
        <version>${yc-common-api.version}</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>org.flywaydb</groupId>
        <artifactId>flyway-core</artifactId>
       <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>com.github.jmnarloch</groupId>
        <artifactId>modelmapper-spring-boot-starter</artifactId>
        <version>${modelmapper-spring-boot-starter.version}</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcpkix-jdk15on</artifactId>
        <version>${bcpkix-jdk15on.version}</version>
    </dependency>

    <!--  JSON WEB Token dependencies -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>${io.jsonwebtoken.version}</version>
        <scope>compile</scope>
    </dependency>

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>${io.jsonwebtoken.version}</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>${io.jsonwebtoken.version}</version>
        <scope>runtime</scope>
    </dependency>

    <!-- only required for XML error response of legacy /api/v2/phone/{phone} endpoint -->
    <dependency>
        <groupId>jakarta.xml.bind</groupId>
        <artifactId>jakarta.xml.bind-api</artifactId>
    </dependency>

Redis config class

@Slf4j
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Autowired
    private RedisProperties redisProperties;

    @Bean(name = "redisConnectionFactory")
    public RedisConnectionFactory redisConnectionFactory() {

        RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration();
        sentinelConfig.setMaster(redisProperties.getSentinel().getMaster());
        redisProperties.getSentinel().getNodes().forEach(s -> sentinelConfig.sentinel(s, Integer.valueOf(redisProperties.getPort())));
        sentinelConfig.setPassword(RedisPassword.of(redisProperties.getPassword()));

        GenericObjectPoolConfig<LettuceConnection> genericObjectPoolConfig = new GenericObjectPoolConfig<>();
        genericObjectPoolConfig.setMaxIdle(redisProperties.getLettuce().getPool().getMaxIdle());
        genericObjectPoolConfig.setMaxTotal(redisProperties.getLettuce().getPool().getMaxActive());

        LettucePoolingClientConfiguration poolConfig = LettucePoolingClientConfiguration.builder()
                .clientName(redisProperties.getClientName())
                .poolConfig(genericObjectPoolConfig)
                .clientOptions(ClientOptions.builder()
                        .socketOptions(SocketOptions.builder().connectTimeout(redisProperties.getTimeout()).build()).build())
                .build();

        LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(sentinelConfig, poolConfig);
        connectionFactory.setPipeliningFlushPolicy(PipeliningFlushPolicy.buffered(1000));
        connectionFactory.setDatabase(redisProperties.getDatabase());
        return connectionFactory;
    }

    @Bean(name = "cachingRedisTemplate")
    public RedisTemplate<String, String> redisTemplate(@Qualifier("redisConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, String> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer(StandardCharsets.UTF_8));
        template.setValueSerializer(new StringRedisSerializer(StandardCharsets.UTF_8));
        return template;
    }

Redis properties

spring.cache.type=redis
spring.redis.client-name=${spring.application.name}
spring.redis.database=0
spring.redis.password=password
spring.redis.timeout=1000
spring.redis.host=redis.host
spring.redis.port=26379
spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=redis.cluster
spring.redis.sentinel.password=password
spring.redis.lettuce.pool.max-active=30
spring.redis.lettuce.pool.max-idle=30


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source