You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot多并发请求时出现EOFException及Tomcat请求解析错误的问题求助

Spring Boot多并发请求时出现EOFException及Tomcat请求解析错误的问题求助

我最近碰到了一个棘手的问题:我的Spring Boot Web服务在同时发送多个请求时,部分请求会失败,但如果是单步调试或者逐个发送请求的话,所有请求都能正常通过。

技术栈

  • Spring Boot版本: 2.7.4
  • 嵌入式服务器: Tomcat 9.0.65
  • 开发工具: IntelliJ IDEA
  • 测试客户端: Postman

错误日志

当发送批量请求时,日志里会出现以下错误:

2025-03-31 16:22:29.271 DEBUG 16768 --- [nio-8082-exec-1] o.apache.coyote.http11.Http11Processor   : Error parsing HTTP request header
2025-03-31 16:22:29.272 DEBUG 16768 --- [nio-8082-exec-1] o.apache.coyote.http11.Http11Processor   : Error state [CLOSE_CONNECTION_NOW] reported while processing request

对应的堆栈跟踪:

java.io.EOFException: null
    at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1340) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
    at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1227) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
    at org.apache.coyote.http11.Http11InputBuffer.fill(Http11InputBuffer.java:805) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
    at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:360) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:271) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
    ...

相关代码

Controller端点

@PostMapping("api/synchro/process")
public ResponseEntity<String> processEntries(@RequestBody List<Synchro> entries) {
    try {
        for (Synchro entry : entries) {
            System.out.println(entry.getId());
            synchroService.processSynchroEntry(entry);
        }
        return ResponseEntity.ok("PROCESSED");
    } catch (Exception e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("Error processing entries: " + e.getMessage());
    }
}

Service方法

@Transactional
public void processSynchroEntry(Synchro entry) {
    String eventType = entry.getEventType().toUpperCase();
    String data = entry.getData();
    String tableName = entry.getTableName();
    switch (eventType) {
        case "INSERT" -> this.handleInsert(tableName, data);
        case "UPDATE" -> this.handleUpdate(tableName, data);
        case "DELETE" -> this.handleDelete(tableName, data);
        default -> throw new IllegalArgumentException("Unsupported event type: " + eventType);
    }
}

动态插入查询执行逻辑

protected void handleInsert(String tableName, String data) {
    try {
        JsonNode dataNode = this.objectMapper.readTree(data);
        StringBuilder query = new StringBuilder("INSERT INTO ");
        query.append(tableName).append(" (");
        MapSqlParameterSource params = new MapSqlParameterSource();
        StringBuilder columns = new StringBuilder();
        StringBuilder values = new StringBuilder();
        Iterator<Map.Entry<String, JsonNode>> it = dataNode.fields();

        while(it.hasNext()) {
            Map.Entry<String, JsonNode> field = it.next();
            if (columns.length() > 0) {
                columns.append(", ");
                values.append(", ");
            }
            columns.append(field.getKey());
            values.append(":" + field.getKey());
            params.addValue(field.getKey(), field.getValue().asText());
        }

        query.append(columns).append(") VALUES (").append(values).append(")");
        this.namedParameterJdbcTemplate.update(query.toString(), params);
    } catch (Exception e) {
        throw new RuntimeException("Error processing insert", e);
    }
}

已尝试的排查步骤

  • 检查请求格式:通过Postman发送的请求格式完全正确
  • 开启Spring Boot调试日志:确认部分请求确实因EOFException失败
  • 测试不同并发级别:逐个发送请求全部成功,批量发送则部分失败
  • 验证JSON payload:所有发送的JSON数据格式都没问题
  • 调整Tomcat连接配置:尝试修改了以下参数,但问题依旧
server.tomcat.max-threads=200
server.tomcat.accept-count=100
server.connection-timeout=60000

我的疑问

  • 到底是什么原因导致批量发送请求时出现这些EOFException?
  • 这会不会和事务处理或者Tomcat的连接管理方式有关?
  • Spring Boot/Tomcat有没有已知的类似问题会导致这种行为?
  • 任何相关的见解或建议我都非常感激!

Synchro实体类代码

public class Synchro {
    @Id
    @GeneratedValue(
        strategy = GenerationType.IDENTITY
    )
    @Column(
        name = "id",
        unique = true,
        nullable = false
    )
    private @NotNull(
    message = "Id ne peut pas être null"
) Integer id;
    @Column(
        name = "eventType",
        length = 100,
        nullable = false
    )
    private @NotNull(
    message = "EventType ne peut pas être null"
) String eventType;
    @Column(
        name = "data",
        nullable = false
    )
    private @NotNull(
    message = "Data ne peut pas être null"
) String data;
    @Temporal(TemporalType.TIMESTAMP)
    @Column(
        name = "createdAt"
    )
    private @NotNull(
    message = "CreatedAt ne peut pas être null"
) Date createdAt;
    @Temporal(TemporalType.TIMESTAMP)
    @Column(
        name = "updatedAt"
    )
    private Date updatedAt;
    @Column(
        name = "status",
        length = 20,
        nullable = false
    )
    private @NotNull(
    message = "Status ne peut pas être null"
) String status;
    @Column(
        name = "tableName",
        length = 100,
        nullable = false
    )
    private @NotNull(
    message = "TableName ne peut pas être null"
) String tableName;
    @Transient
    private String source;
}

备注:内容来源于stack exchange,提问作者Wassupppp

火山引擎 最新活动