惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
Netflix TechBlog - Medium
Microsoft Azure Blog
Microsoft Azure Blog
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
aimingoo的专栏
aimingoo的专栏
B
Blog RSS Feed
V
Visual Studio Blog
P
Proofpoint News Feed
云风的 BLOG
云风的 BLOG
博客园 - 【当耐特】
大猫的无限游戏
大猫的无限游戏
Application and Cybersecurity Blog
Application and Cybersecurity Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
The Cloudflare Blog
B
Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Apple Machine Learning Research
Apple Machine Learning Research
M
MIT News - Artificial intelligence
Know Your Adversary
Know Your Adversary
I
InfoQ
T
The Exploit Database - CXSecurity.com
V
Vulnerabilities – Threatpost
C
Cisco Blogs
Spread Privacy
Spread Privacy
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
P
Palo Alto Networks Blog
Simon Willison's Weblog
Simon Willison's Weblog
月光博客
月光博客
博客园 - Franky
Project Zero
Project Zero
G
Google Developers Blog
S
SegmentFault 最新的问题
博客园 - 聂微东
P
Privacy & Cybersecurity Law Blog
The GitHub Blog
The GitHub Blog
阮一峰的网络日志
阮一峰的网络日志
P
Privacy International News Feed
T
Threat Research - Cisco Blogs
S
Schneier on Security
Microsoft Security Blog
Microsoft Security Blog
G
GRAHAM CLULEY
S
Security @ Cisco Blogs
Martin Fowler
Martin Fowler
A
Arctic Wolf
T
Tenable Blog
L
LINUX DO - 最新话题
TaoSecurity Blog
TaoSecurity Blog
Hugging Face - Blog
Hugging Face - Blog
有赞技术团队
有赞技术团队
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com

博客园 - wastonl

Spring ResolvableType说明 Jvm内存以及垃圾回收相关知识 RocketMQ如何保证消息可靠性 RocketMQ整体架构 mybatis-plus易忘点笔记 SpringMVC使用Resource实现二进制传输(下载) Spring事件异步执行设计与实现 Spring Bean销毁机制 Spring Lifecycle组件 Zipkin Brave使用 Spring Boot日志系统简要介绍 spring cloud sleuth基本使用 Spring懒加载与@Lazy注解 https碎碎念 ES脚本使用 SpringMVC静态资源处理 Maven插件运行方式 如何使用Maven将项目中的依赖打进jar包 时区以及时区对于Java时间类格式化的影响 SpringMVC处理请求头、响应头、编码行为 tomcat连接处理机制和线程模型 树组件实现 JWT示例与原理 方法句柄API使用
tomcat自动刷新响应输出流缓冲区
wastonl · 2025-08-04 · via 博客园 - wastonl

小知识点

如下代码没有主动调用flush或者close方法时,浏览器也能拿到响应数据,是因为tomcat内部的Servlet中的service方法执行完毕后,会自动调用close方法刷新缓冲区的数据。

@GetMapping("/hello")
public void hello(HttpServletResponse response) throws IOException {
    // 只是写到响应流内部的缓冲区(ByteBuffer)中,当写入的内容超过缓冲区大小时会自动刷新数据
    response.getOutputStream().write("hello world".getBytes(StandardCharsets.UTF_8));
}

tomcat中的响应输出流实现为CoyoteOutputStream

CoyoteOutputStream.java

@Override
public void write(byte[] b, int off, int len) throws IOException {
    boolean nonBlocking = checkNonBlockingWrite();
    ob.write(b, off, len);
    if (nonBlocking) {
        checkRegisterForWrite();
    }
}

@Override
public void flush() throws IOException {
    boolean nonBlocking = checkNonBlockingWrite();
    ob.flush();
    if (nonBlocking) {
        checkRegisterForWrite();
    }
}

@Override
public void close() throws IOException {
    ob.close();
}


OutputBuffer.java

public void write(byte b[], int off, int len) throws IOException {
    if (suspended) {
        return;
    }
    writeBytes(b, off, len);
}

private void writeBytes(byte b[], int off, int len) throws IOException {
    if (closed) {
        return;
    }
    // 追加到内部的ByteBuffer中,没有剩余空间时会写入到底层的socket中,同时清空ByteBuffer
    append(b, off, len);
    bytesWritten += len;

    // if called from within flush(), then immediately flush
    // remaining bytes
    if (doFlush) {
        flushByteBuffer();
    }
}

接下来是tomcat内部执行顺序

Http11Processor.service -> CoyoteAdapter.service -> org.apache.coyote.Response.finishResponse -> OutputBuffer.close

其中CoyoteAdapter.service会调用Servlet的service方法执行响应逻辑

CoyoteAdapter.service

// 这一行会触发Servlet的执行
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);