Spring Cloud Gateway 恶意长连接防护:客户端建立连接不发数据?空闲超时强制断开,释放线程资源!

做过网关开发的同学肯定都遇到过这个问题:恶意用户或者异常客户端建立 TCP 连接后,什么请求都不发送,就静静保持着连接不走。这样会导致什么后果呢?

我之前就遇到过这样一个案例:线上网关突然出现大量连接超时,排查后发现是有人在用脚本模拟"占坑"攻击——同时发起几千个连接,但每个连接都只发送一个请求后就再也不发数据了。这些空闲连接占用着 Netty 的 Worker 线程和内存资源,导致正常请求无法处理。

今天我们就来聊聊 Spring Cloud Gateway 的恶意长连接防护方案,让你的网关不再被"僵尸连接"拖垮。

恶意长连接的危害

1. 线程资源耗尽

Spring Cloud Gateway 基于 Netty 实现,Netty 的工作模型是这样的:

Netty 线程模型:

Boss 线程(1个) → 接受连接 → 分配给 Worker 线程池

Worker 线程池(N个) → 处理读写事件 → 如果有耗时操作会阻塞线程

问题场景:恶意长连接占用 Worker 线程
- 1000 个空闲连接
- 每个占用一个 Worker 线程
- 正常请求排队等待可用线程
- 响应超时

2. 内存资源泄漏

每个 TCP 连接都会占用一定的内存:

连接内存占用:

1. Socket 缓冲区:默认 16KB * 2(接收+发送)
2. NIO Channel 对象:约 2KB
3. Netty ChannelHandlerContext:约 1KB
4. 请求/响应缓冲区:初始 8KB,可扩展

单个连接 ≈ 40KB+

10000 个恶意连接 ≈ 400MB+ 内存占用

3. 并发能力下降

正常情况下,网关的并发能力取决于 Worker 线程数量:

Netty 默认配置:
- Worker 线程数 = CPU 核心数 * 2

比如 8 核 CPU:
- Worker 线程 = 16
- 同时处理的连接 ≈ 16(因为每个连接可能占用多个事件循环)

如果被恶意长连接占满:
- 正常请求无线程可用
- 请求堆积
- 最终导致服务不可用

解决方案:空闲超时 + 强制断开

1. 核心设计思想

我们的方案核心是三个关键机制:

  1. 空闲检测:检测连接多长时间没有数据传输
  2. 超时断开:超过设定时间后主动关闭连接
  3. 资源清理:确保连接关闭后释放所有资源

架构图如下:

┌─────────────────────────────────────────────────────────────────┐
│                    恶意长连接防护架构                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌──────────┐    ┌────────────────┐    ┌────────────────┐      │
│  │  客户端  │───→│  Netty Server  │───→│ Idle Handler  │      │
│  │          │    │                │    │ (空闲检测)     │      │
│  └──────────┘    └────────────────┘    └────────────────┘      │
│                                                      │         │
│                                                      ▼         │
│  ┌──────────┐    ┌────────────────┐    ┌────────────────┐      │
│  │          │◀───│  Channel       │◀───│ 超时断开       │      │
│  │          │    │  (连接管理)     │    │ (force close) │      │
│  └──────────┘    └────────────────┘    └────────────────┘      │
│                                                      │         │
│                                                      ▼         │
│                                              ┌────────────────┐ │
│                                              │ 资源清理       │ │
│                                              │ (释放线程/内存) │ │
│                                              └────────────────┘ │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2. 空闲检测的原理

Netty 提供了 IdleStateHandler 来检测连接空闲:

IdleStateHandler 参数说明:

1. readerIdleTime:读取空闲时间(没有收到数据)
2. writerIdleTime:写入空闲时间(没有发送数据)
3. allIdleTime:任意方向空闲时间

检测逻辑:

when connection.isActive():
    lastReadTime = now()
    
    if now() - lastReadTime > readerIdleTime:
        trigger(READER_IDLE)
        
    if now() - lastWriteTime > writerIdleTime:
        trigger(WRITER_IDLE)
        
    if now() - max(lastReadTime, lastWriteTime) > allIdleTime:
        trigger(ALL_IDLE)

3. 空闲处理器实现

// 空闲连接处理
class IdleDisconnectHandler extends ChannelInboundHandlerAdapter {
    
    // 读取空闲时间(秒)
    private final int readerIdleTimeSeconds;
    
    // 强制断开连接
    private final boolean forceClose;
    
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleEvent = (IdleStateEvent) evt;
            
            String idleType = switch (idleEvent.state()) {
                case READER_IDLE -> "读取空闲";
                case WRITER_IDLE -> "写入空闲";
                case ALL_IDLE -> "全部空闲";
            };
            
            log.warn("检测到{}连接, channel={}, idleTime={}s", 
                    idleType, ctx.channel(), readerIdleTimeSeconds);
            
            if (forceClose) {
                // 强制关闭连接
                ctx.close();
                log.info("已强制关闭空闲连接: {}", ctx.channel());
            }
        }
    }
}

4. Spring Cloud Gateway 配置

# 网关空闲超时配置
spring:
  cloud:
    gateway:
      httpclient:
        response-timeout: 30s
        connect-timeout: 10s
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"

server:
  port: 8080
  
# Netty 配置
reactor:
  netty:
    ioWorkerCount: 16  # Worker 线程数
    connectionTimeout: 10s  # 连接超时

5. 自定义过滤器实现

// 网关空闲检测过滤器
class IdleTimeoutFilter implements GlobalFilter {
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        HttpServerVersion version = HttpServerVersion.forVersion(1, 1);
        
        return chain.filter(exchange)
                .doOnCancel(() -> {
                    log.debug("连接已取消: {}", exchange.getRequest().getURI());
                })
                .doOnError(error -> {
                    log.error("连接异常: {}", exchange.getRequest().getURI(), error);
                });
    }
}

最佳实践与配置建议

1. 合理设置超时时间

超时时间配置原则:

1. 读取空闲(readerIdleTime):
   - 建议:30-60 秒
   - 太短:正常请求可能被误杀(如弱网环境)
   - 太长:恶意连接占用资源时间过长

2. 写入空闲(writerIdleTime):
   - 建议:60-120 秒
   - 用于检测"只建立不发送请求"的连接

3. 全部空闲(allIdleTime):
   - 建议:60 秒
   - 综合检测读写两个方向

2. 分级防护策略

分级防护配置:

轻度防护(正常业务):
  - readerIdleTime: 60s
  - 仅警告,不断开

中度防护(可疑行为):
  - readerIdleTime: 30s
  - 发送心跳探测
  - 如果无响应则断开

重度防护(攻击行为):
  - readerIdleTime: 10s
  - 直接断开
  - 记录日志并触发告警

3. 监控与告警

需要监控的指标:

1. 当前空闲连接数
2. 每分钟断开的空闲连接数
3. 被强制关闭的连接数
4. 断开连接的平均空闲时间

告警规则:

- 每分钟断开连接数 > 100:可能存在攻击
- 空闲连接占比 > 30%:资源配置异常
- 单个 IP 空闲连接数 > 50:可疑 IP

4. 防御纵深

多层防护策略:

第一层:网关层(Netty IdleHandler)
  - 快速检测,快速断开
  - 保护下游服务

第二层:负载均衡层(Nginx/LVS)
  - 连接数限制
  - 单 IP 限流

第三层:防火墙层
  - SYN Flood 防护
  - 连接速率限制

第四层:应用层
  - 请求频率限制
  - 恶意行为识别

5. 日志埋点

日志规范:

1. 检测到空闲连接:
   level=INFO, msg="检测到空闲连接", channelId=xxx, idleTime=60s

2. 发送心跳探测:
   level=INFO, msg="发送心跳探测", channelId=xxx, remoteIp=xxx

3. 强制关闭连接:
   level=WARN, msg="强制关闭空闲连接", channelId=xxx, idleTime=60s, reason=idle_timeout

4. 连接异常:
   level=ERROR, msg="连接异常", channelId=xxx, error=xxx

配置参数建议

# 完整的网关安全配置
spring:
  cloud:
    gateway:
      httpclient:
        response-timeout: 30s
        connect-timeout: 10s
        pool:
          max-idle-time: 60s        # 空闲连接最大存活时间
          min-idle-time: 5s         # 最小空闲时间
          max-connections: 1000    # 最大连接数
          max-connections-per-route: 100  # 单路由最大连接

server:
  port: 8080
  netty:
    connection-timeout: 60s        # 连接超时

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics
  metrics:
    tags:
      application: ${spring.application.name}

logging:
  level:
    reactor.netty: INFO
    com.example.gateway: DEBUG

效果对比

方案防护效果资源占用误杀率适用场景
无防护0%测试环境
简单超时⚠️简单场景
IdleHandler生产环境
分级防护极低高安全要求

总结

恶意长连接防护的核心原则:

  1. 及时检测:使用 Netty IdleHandler 检测空闲连接
  2. 合理超时:根据业务场景设置合适的超时时间
  3. 强制断开:检测到恶意行为后立即关闭连接
  4. 资源清理:确保断开后释放所有资源
  5. 分级防护:不同级别采用不同的防护策略

记住:连接是资源,不是越多越好。通过合理的空闲超时配置,可以让网关自动清理"僵尸连接",保护宝贵的线程和内存资源。


源码获取

文章已同步至小程序博客栏目,需要源码的请关注小程序博客。

公众号:服务端技术精选

小程序码:


标题:Spring Cloud Gateway 恶意长连接防护:客户端建立连接不发数据?空闲超时强制断开,释放线程资源!
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/05/26/1779205573694.html
公众号:服务端技术精选
    评论
    0 评论
avatar

取消