别再滥用 WebSocket!Spring ResponseBodyEmitter 才是某些场景的银弹

Spring Boot Streaming and SSE异步处理 为什么我不完全主张自学?①平台上的大牛基本上都有很多年的工作经验了,你有没有想过之前行业的门槛是什么样的,现在行业门槛是什么样的?以前企业对于程序员能力要求没有这么高,甚至十多年前你只要会写个“Hello World”,你都可以入门这个行业,所以以前要入门是完全可以入门的。②现在也有一些优秀的年轻大牛,他们或许也是自学成才,但是他们一定是具备优秀的学习能力,优秀的自我管理能力(时间管理,静心坚持等方面)以及善于发现问题并总结问题。 阅读详情

嘿,EveryDeveopers,服务器如何实时地将数据推送给客户端?

一提到这个,很多人的第一反应可能是:“上 WebSocket!”。没错,WebSocket 功能强大,提供了全双工通信,非常适合做聊天室、在线游戏这类应用。

但它就像一把牛刀,你总不想用它来切水果吧?在很多“服务器到客户端”的单向推送场景中,引入 WebSocket 不仅增加了复杂性,还可能带来不必要的资源开销。 

那么,有没有更轻量、更原生的解决方案呢?当然有!ResponseBodyEmitter,就是 Spring 框架为我们提供的一把锋利的“水果刀”。它允许我们在一个 HTTP 连接上,异步、分块地向客户端持续推送数据。

准备好了吗?让我们发车!

一. ResponseBodyEmitter 是什么?它能解决什么问题?

简单来说,ResponseBodyEmitter 是 Spring MVC 的一个组件,从Spring4.2 就开始提供了。

它允许控制器方法以流式的方式,分多次向响应体(Response Body)写入数据。

想象一下这个流程:

  • 客户端发起一个普通的 HTTP 请求。

  • 服务器接受请求,但并不立即关闭连接。

  • 服务器的控制器方法返回一个 ResponseBodyEmitter 对象,此时 HTTP 连接保持打开状态。

  • 服务器在后台进行一些耗时的操作(比如处理文件、查询数据库、调用第三方服务)。

  • 每当后台任务产生一块新的数据,就通过 ResponseBodyEmitter 将这块数据“发射”给客户端。

  • 所有数据都发射完毕后,服务器关闭连接。

这种模式非常适合以下场景:

  • 实时进度更新:比如文件上传/下载进度、数据处理任务的完成百分比。用户可以实时看到任务状态,而不是面对一个无尽的加载圈。

  • 流式传输大量数据:当需要返回一个巨大的数据集(如大型报表、日志文件)时,一次性加载到内存再返回可能会导致服务器内存溢出。使用 ResponseBodyEmitter 可以一块一块地读取和发送,极大地降低了内存压力。

  • 简单的实时数据推送:比如体育比赛的实时比分、股票价格的更新。虽然不如 WebSocket 实时,但对于秒级更新的场景已经足够,且实现简单得多。

二. 它是如何工作的?(原理与架构)

为了更好地理解 ResponseBodyEmitter,我们需要了解其背后的 Servlet 3.0 异步请求处理机制。

当一个控制器方法返回 ResponseBodyEmitter 时,Spring MVC 会启动异步处理。它会从应用服务器(如 Tomcat)的线程池中释放当前的工作线程,但保持 HTTP 连接不关闭。然后,Spring 会将 ResponseBodyEmitter 对象保存在内存中,并允许你在另一个线程中调用它的 send() 方法来发送数据。

图接:

核心要点:

  • 异步非阻塞:控制器方法迅速返回,释放了 Tomcat 的主工作线程,使其可以去处理其他请求,提高了服务器的吞吐能力。

  • 长连接:利用了 HTTP 的长连接特性,在一个请求-响应周期内持续推送数据。

  • 线程分离:数据生成和数据发送在不同的线程中,实现了逻辑解耦。

三. 上手实战:Spring Boot 3 代码示例

光说不练假把式。我们来用 Spring Boot 3 构建一个简单的例子:模拟一个耗时 5 秒的任务,每秒向客户端推送一次进度。

3.1:项目搭建

确保你有一个 Spring Boot 3 项目,并加入了 spring-boot-starter-web 依赖。

pom.xml:

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

3.2:创建控制器 (Controller)

StreamingController.java:

package com.example.emitterdemo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@RestController
public class StreamingController {

    private static final Logger logger = LoggerFactory.getLogger(StreamingController.class);

    // 使用一个线程池来处理任务,避免每次请求都创建新线程
    private final ExecutorService executor = Executors.newSingleThreadExecutor();

    @GetMapping(path = "/progress", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public ResponseBodyEmitter streamProgress() {
        // 1. 创建 Emitter,可以设置一个超时时间
        final ResponseBodyEmitter emitter = new ResponseBodyEmitter(60_000L); // 60秒超时

        // 2. 提交一个任务到线程池
        executor.execute(() -> {
            try {
                for (int i = 1; i <= 5; i++) {
                    // 模拟耗时操作
                    Thread.sleep(1000);

                    // 3. 发送数据,可以是一个字符串或一个对象(需要配置 HttpMessageConverter)
                    String message = "Processing step " + i + "/5 ...\n";
                    emitter.send(message, MediaType.TEXT_PLAIN);
                    logger.info("Sent: {}", message.trim());
                }
                // 4. 所有数据发送完毕,调用 complete
                emitter.complete();
                logger.info("Emitter completed.");
            } catch (IOException e) {
                // IO 异常,通常是客户端断开了连接
                logger.error("IOException, client might have disconnected. {}", e.getMessage());
                emitter.completeWithError(e);
            } catch (InterruptedException e) {
                logger.error("Task interrupted. {}", e.getMessage());
                Thread.currentThread().interrupt();
                emitter.completeWithError(e);
            }
        });

        // 注册回调,在 Emitter 完成(complete/completeWithError)或超时(timeout)时触发
        emitter.onCompletion(() -> logger.info("Emitter onCompletion callback."));
        emitter.onTimeout(() -> logger.warn("Emitter onTimeout callback."));
        emitter.onError(e -> logger.error("Emitter onError callback.", e));

        logger.info("Controller method finished, emitter returned.");
        return emitter;
    }
}

代码解析:

  • 我们创建了一个 ResponseBodyEmitter 并设置了 60 秒的超时时间。如果 60 秒内没有任何数据发送,连接会自动断开。

  • 我们使用 ExecutorService 来管理后台线程。这是一个好习惯,可以避免资源耗尽。

  • 在后台任务中,我们循环 5 次,每次 sleep(1000) 模拟耗时,然后调用 emitter.send() 发送进度信息。

  • 任务结束后,必须调用 emitter.complete() 来正常关闭连接。如果发生异常,调用 emitter.completeWithError()。

  • onCompletion, onTimeout, onError 是非常有用的回调,可以帮助我们记录日志和清理资源。

3.3:创建客户端 (HTML + JavaScript)

现在,我们需要一个前端页面来接收并展示这些流式数据。这里我们使用现代浏览器都支持的 fetch API。

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ResponseBodyEmitter Demo</title>
    <style>
        body { font-family: sans-serif; padding: 20px; }
        #progress-container { border: 1px solid #ccc; padding: 10px; min-height: 100px; background-color: #f9f9f9; }
    </style>
</head>
<body>
    <h1>Task Progress</h1>
    <button id="startButton">Start Task</button>
    <div id="progress-container">
        <p>Click "Start Task" to see the progress.</p>
    </div>

    <script>
        const startButton = document.getElementById('startButton');
        const progressContainer = document.getElementById('progress-container');

        startButton.addEventListener('click', async () => {
            progressContainer.innerHTML = '<p>Starting task...</p>';
            startButton.disabled = true;

            try {
                // 1. 使用 fetch API 发起请求
                const response = await fetch('/progress');

                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }

                // 2. 获取一个 ReadableStream 的 reader
                const reader = response.body.getReader();
                const decoder = new TextDecoder(); // 用于将 Uint8Array 解码成字符串

                while (true) {
                    // 3. 读取数据块
                    const { done, value } = await reader.read();

                    if (done) {
                        console.log('Stream finished.');
                        progressContainer.innerHTML += '<p><strong>Task complete!</strong></p>';
                        break;
                    }

                    // 4. 解码并追加到 UI
                    const chunk = decoder.decode(value, { stream: true });
                    console.log('Received chunk:', chunk);
                    progressContainer.innerHTML += `<p>${chunk}</p>`;
                }

            } catch (error) {
                console.error('Error fetching stream:', error);
                progressContainer.innerHTML += `<p style="color: red;">Error: ${error.message}</p>`;
            } finally {
                startButton.disabled = false;
            }
        });
    </script>
</body>
</html>

代码解析:

  • fetch('/progress') 发起请求。

  • response.body 是一个 ReadableStream 对象。我们通过 getReader() 获取读取器。

  • 在一个 while(true) 循环中,我们反复调用 reader.read()。这个方法会返回一个 Promise,当服务器发送新数据时,Promise 会 resolve 并带上数据块 value。

  • value 是一个 Uint8Array,我们需要用 TextDecoder 将它转换成可读的字符串。

  • 当服务器调用 emitter.complete() 后,reader.read() 返回的对象的 done 属性会变为 true,我们就可以退出循环了。

现在,运行你的 Spring Boot 应用,访问 http://localhost:8080/index.html,点击按钮,你就能看到进度条实时更新了!

四. 技术选型对比:Emitter vs. The WebSocket

了解了 ResponseBodyEmitter 的用法后,一个关键问题来了:我应该在什么时候用它?它和 SseEmitter、WebSocket 以及传统的异步方法有什么不同?

图解时刻:

特性

ResponseBodyEmitter

SseEmitter (Server-Sent Events)

WebSocket

传统 @Async

通信方向

单向 (服务器 -> 客户端)

单向 (服务器 -> 客户端)

双向 (全双工)

单向 (一次性响应)

底层协议

HTTP/1.1

HTTP/1.1

WebSocket (ws/wss)

HTTP/1.1

客户端 API

fetch API (需要手动解析)

EventSource (浏览器原生支持)

WebSocket API

标准 fetch / XHR

自动重连

否,需手动实现

是 (浏览器标准)

否,需手动实现

不适用

事件类型

否,数据格式自定义

是 (可定义 event name)

否,消息格式自定义

不适用

复杂度

低

极低

高

中

最佳场景

自定义格式的流式数据,进度更新

标准化的事件推送,新闻 feed,通知

实时聊天,在线游戏,协同编辑

后台耗时任务,返回单个完整结果

ResponseBodyEmitter vs. SseEmitter

SseEmitter 是 ResponseBodyEmitter 的一个子类。它专门用于实现 HTML5 Server-Sent Events (SSE) 规范。

SSE 的好处是浏览器有原生支持的 EventSource API,它会自动处理重连、消息解析等问题,前端代码更简洁。

结论:

如果你需要的是标准的事件流,并且不介意遵循 SSE 的数据格式(data: ...\n\n),优先选择 SseEmitter。

如果你的数据格式非常特殊,或者想完全控制传输的每一个字节,那么 ResponseBodyEmitter 提供了更大的灵活性。

ResponseBodyEmitter vs. WebSocket
  • 方向性:Emitter 是单向的,WebSocket 是双向的。如果你需要客户端也频繁地向服务器发送消息,请直接选择 WebSocket。

  • 协议:Emitter 跑在标准的 HTTP 之上,对网络防火墙和代理更友好。WebSocket 是一个独立的协议,有时可能会被公司网络策略限制。

  • 开销:建立 WebSocket 连接的握手过程比普通 HTTP 请求更复杂。对于只需要服务器推送的简单场景,Emitter 更轻量。

结论:杀鸡焉用牛刀。对于只需要服务器向客户端单向推送信息的场景(如进度条、数据流),ResponseBodyEmitter 或 SseEmitter 是更简单、更合适的选择。

ResponseBodyEmitter vs. 传统 @Async

一个用 @Async 注解的控制器方法,通常返回一个 CompletableFuture<V> 或 DeferredResult<V>。它的目的是不阻塞请求处理线程,但它最终仍然只会返回一个完整的 HTTP 响应。而 ResponseBodyEmitter 的目的是在一个连接中返回多个数据块。

结论:@Async 用于优化单个、耗时响应的吞吐量。ResponseBodyEmitter 用于实现流式、分块的响应。两者解决的问题维度不同。

5. thinking

ResponseBodyEmitter 是 Spring 工具箱中一件被低估的利器。它为我们提供了一种优雅、高效的方式来处理服务器到客户端的流式数据推送,而无需引入 WebSocket 的复杂性。:

  • ResponseBodyEmitter 适用于进度更新、大数据流式传输等单向推送场景。

  • 其核心是利用 Servlet 3.0 的异步特性,在后台线程中向一个保持开放的 HTTP 连接发送数据。

  • 结合 Spring Boot 3 和 fetch API,我们可以轻松地实现一个完整的推流应用。

  • 在技术选型时,应根据通信方向、客户端支持和业务复杂度来决定使用 Emitter、SSE 还是 WebSocket。

so,下次当你的产品经理提出“我要一个实时更新的进度条”时,别急着说“好的,我需要搭个 WebSocket 服务”。先想一想,ResponseBodyEmitter 这把轻巧而锋利的“水果刀”,是不是更适合这个任务呢?

Having so funny, Happy coding!

SpringBoot SSE流式响应实战:告别等待,实现实时进度推送 在Web开发中,服务器与客户端的实时数据交互是提升用户体验的关键。传统HTTP请求-响应模式在处理耗时任务时,用户只能被动等待,缺乏即时反馈。Server-Sent Events(SSE)作为一种基于HTTP的轻量级协议,通过建立单向长连接,实现了服务器向客户端的持续数据推送。其技术原理在于保持HTTP连接不关闭,并以特定文本格式(如data、event字段)流式传输事件数据,天然支持自动重连。这种机制在工程实践中价值显著,尤其适用于需要实时进度通知、日志流式输出或状态更新的场景,例如后台任务执行、数据导出 阅读详情

相关推荐

小白学习图像处理6——Canny边缘检测算法原理

目录结构一、高斯滤波二、梯度计算1、原理2、代码如下三、非极大值抑制三、双阈值检测四、matlab 的 edge函数五、总结 Canny边缘检测算法主要步骤:高斯滤波、梯度计算、非极大值抑制和双阈值检测。 一、高斯滤波   使用高斯滤波的目的是平滑图像,滤除图像中的部分噪声(因为微分算子对噪声很敏感)。高斯滤波具体办法是生成一个高斯模板,使用卷积进行时域滤波: img = im2double(im...

我有两颗糖 7791

Java 中的 ResponseBodyEmitter:详解与实战

是 Spring MVC 提供的一个类,用于实现服务端向客户端分块推送响应数据。它是异步非阻塞的,可以在响应未完成时多次向客户端发送部分数据,而无需等待任务完成。是 Spring 提供的轻量级流式传输解决方案,能有效提升高并发和实时性场景的用户体验。通过本文的讲解和示例,相信大家已经掌握了它的使用技巧和注意事项。在实际项目中,不妨尝试将其应用于实时日志、进度更新等场景,让你的应用更加智能、高效。

2301_79858914的博客 3436

STM32--基于STM32F103C8T6的OV7670摄像头显示

使用 STM32F103C8T6 微控制器驱动 OV7670 摄像头模组,并将图像数据显示在屏幕上,是嵌入式系统中常见的图像采集与显示应用。OV7670 是一款 CMOS VGA 图像传感器,具有高灵敏度、低电压等特点,适用于嵌入式应用。而 STM32F103C8T6 是一款常用的 MCU 控制器,具有丰富的管脚资源和较高的工作频率。

duierrorshuobu的博客 2145

SpringBoot + ResponseBodyEmitter 实时异步流式推送

是 Spring Framework 提供的一个类,用于处理需要长时间运行的异步响应场景。它允许你在服务器端逐步发送数据到客户端,而不是一次性返回所有内容。这对于实时更新的应用场景非常有用,比如股票报价、聊天应用、社交媒体更新等。

qq_38519364的博客 2698

MuJoCo 机械臂关节空间阻抗控制Impedance实现(附代码)

阻抗控制通过调整关节的刚度和阻尼,让机器人在接触时产生顺应性,减少冲击力,其核心目标是在接触外部载荷时能够自适应调整输出力矩,实现力与位置的协同控制。阻抗控制可以很快的到达设定位置,在此基础上,使用mujoco交互中的施加外力操作,可以看到当外力松开后,机械臂也可以很快的回弹回设定位置。其中self.Kp和self.Kd为固定刚度和阻尼系数,通过修改刚度和阻尼参数,可以变得更柔顺来应对各种场景。注释掉该行后,直接simulate仿真可以看到机械臂控制位置震荡等情况,接下来引入关节空间阻抗控制的代码。

darcsdn 1274

CAT24C16 EEPROM的I²C页写与页读底层实现

I²C是一种广泛应用于嵌入式系统的双线同步串行通信协议,其核心在于严格的时序控制、地址寻址机制与主从应答逻辑。理解I²C总线物理层(上拉电阻、SCL/SDA电气特性)、协议层(START/STOP、ACK/NACK、7位地址格式)及外设寄存器操作(如SR1状态标志、CCR时钟分频),是实现可靠通信的基础。在实际工程中,EEPROM类器件(如CAT24C16)因其非易失性、页缓冲区结构和写保护(WP)功能,成为数据持久化关键载体;其16字节页写约束、内部写周期(5–10ms)与地址指针自动递增等特性,直接决定

weixin_42591413的博客 388

SpringMVC之九:Spring MVC的异步模式(ResponseBodyEmitter、SseEmitter、StreamingResponseBody)

DeferredResult高级使用 上篇博文介绍的它的基本使用,那么本文主要结合一些特殊的使用场景,来介绍下它的高级使用,让能更深刻的理解DeferredResult的强大之处。 它的优点也是非常明显的,能够实现两个完全不相干的线程间的通信。处理的时候请注意图中标记的线程安全问题~~~ 实现长轮询服务端推送消息(long polling) 简单科普双向通信的方式 在WebSocket协议之前(它是2011年发布的),有三种实现双向通信的方式:轮询(polling)、长轮询(long-po

weixin_42073629的博客 2094

2023年全国青少年信息素养大赛C++编程初中组决赛真题+答案解析

2023年全国青少年信息素养大赛C++编程初中组决赛真题+答案解析

伶俐角少儿编程 819

Java后端LLM框架选型:Spring AI vs LangChain4j生产实测

LLM集成已成为Java后端核心能力,但框架选型直接影响系统稳定性、可观测性与运维成本。Spring AI以Spring生态无缝集成见长,强调自动装配与开箱即用;LangChain4j则坚持显式声明与接口抽象,提供Token级流式控制、租户级资源隔离及强契约集成能力。二者在依赖注入侵入性、流式响应粒度、外部服务鲁棒性及生产可观测性四大维度存在本质差异。本文基于日均21万调用的金融信贷审批系统真实压测数据,揭示Spring AI在连接池管理、链路追踪初始化、错误堆栈透明度等方面的隐性风险,以及LangChai

weixin_34268169的博客 292

YOLO11改进策略【独家融合改进】| EfficientViMBlock + LGAG + DySample

通过学习采样来学习上采样DySample。

Limiiiing的博客 363

微信小程序|基于小程序+云开发制作一个菜谱小程序

今天吃什么?这是一个让强迫症左右为难的问题,跟随此文基于小程序+云开发制作一个菜谱小程序,根据现有食材一键生成菜谱,省心又省力。

商务合作 / 项目定制 / 学习交流。个人vx:lovely_wml 1万+

SSE生产实践:超时断连、心跳机制与消息补偿的完整指南

在实时推送场景中,Server-Sent Events(SSE)作为一种基于HTTP的轻量级单向通信协议,凭借其简单性和浏览器原生支持,成为许多Java后端的技术选择。SSE通过text/event-stream长连接实现服务端到客户端的数据流推送,但生产环境的复杂性远超本地开发——Nginx代理空闲超时、Tomcat异步超时、连接数耗尽、断线重连丢失消息等问题,常常让开发者陷入“本地能跑,上线就断”的困境。本文从SSE协议基础出发,深入解析连接断开的四大层根因,详述心跳保活、Last-Event-ID消息

weixin_30861459的博客 294

用一台笔记本跑起 SAP HANA:从 express edition 的定位,到本地硬件门槛与安装实操

SAP HANA是面向个人开发者、初创团队与教学科研场景的精简发行版,保留了in-memory列存引擎、SQLScriptAFLGraphSpatial等核心能力,可免费用于开发与一定规模的生产用途(上限为 32 GB RAM)。它最大的特征,是能在资源受限的laptop或上快速落地,让你在本地完成从建模、开发到测试的完整闭环。SAP在交付形态上,提供了多种安装包:预配置的VM OVA、面向Linux的二进制安装包,以及历史上出现过的容器化方案;你可以按现有设备与熟悉的运维方式择优选择。官方文档把VM。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 534

Spring Boot 2.x实战53 - Spring Web MVC 25 - 异步请求(HTTP Streaming - ResponseBodyEmitter)

5.2 HTTP Streaming DeferredResult和Callable只能异步返回单个值;如果我们想有多个异步返回的话,我们可以用HTTP Streaming来实现。 HTTP Streaming是一种推送形式的数据传输技术,它通过无限期开放的HTTP连接让web服务器(tomcat)能持续向客户端(浏览器)传送数据。 在HTTP Streaming下,web服务器会握住客户端的请求并让返回保持开放,这样服务器可以通过返回一直发送数据。这个请求-返回的通道会一直开启,除非我们显示的要求关闭。

汪云飞记录本 2060

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Spring MVC 阅读官方文档知识点总结

一、功能及配置 1.DispatcherServlet、拦截器、错误页面 2.Annotated Controllers 带注释的控制器,Spring MVC提供了一个基于注释的编程模型,其中@Controller和 @RestController组件使用注释来表达请求映射,请求输入,异常处理等。带注释的控制器具有灵活的方法签名,无需扩展基类或实现特定的接口。 3.使用@RequestMapping批注将请求映射到控制器方法。它具有各种属性,可以通过URL,HTTP方法,请求参数,标头和媒体类型进行匹配。您

兴华的博客 910

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Spring MVC的异步模式(ResponseBodyEmitter、SseEmitter、StreamingResponseBody)

通过spring mvc异步接口,实现后端分段向前端发送信息。

fyk的博客 3794

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

java.lang.IllegalStateException: ResponseBodyEmitter has already completed

遇到的问题:接收方设置了24h的超时时间,而发送方没设置超时时间,导致。请求处理完成:当请求处理方法(例如控制器方法)执行完毕并返回时,异常处理:如果在请求处理期间出现异常,并且没有捕获或处理该异常,例如,当您发送完所有的数据后,可以显式地调用。的任何方法来发送数据,超时事件将触发,并导致。请求超时:如果在设置的超时时间内没有调用。时,您可以手动在适当的时机调用。方法来标记流的结束。自动complete。

weixin_46078600的博客 4198

ResponseBodyEmitter介绍

以下是关于。

code_fly 786

【小家Spring】高性能关键技术之---体验Spring MVC的异步模式(ResponseBodyEmitter、SseEmitter、StreamingResponseBody) 高级使用篇

前言 上篇博文:【小家Spring】高性能关键技术之—体验Spring MVC的异步模式(Callable、WebAsyncTask、DeferredResult) 基础使用篇 介绍了Spring MVC异步模式的基本使用,相信小伙伴们基本的使用都能运用自如了。 那么本篇文章主要介绍一下异步模式的高级使用(主要是DeferredResult)以及原理过程分析,废话不多说,进入正题 Deferred...

架构师:通透,才能写出好代码! 7781

Spring 实现 3 种异步流式接口,干掉接口超时烦恼

这篇介绍三种实现异步流式接口的工具,算是 Spring 知识点的扫盲。使用起来比较简单,没有什么难点,但它们在实际业务中的应用场景还是很多的,通过这些工具,可以有效提高系统的性能和响应能力。

tangjieqing的博客 1480

SpringBoot + ResponseBodyEmitter 实时异步流式推送:告别轮询,让数据推送更高效

高效传输:减少无效请求实时性好:数据变更立即推送实现简单:基于HTTP协议内存友好:流式处理大数据记住,ResponseBodyEmitter适合单向服务器推送的场景,对于需要双向通信的场景,还是需要WebSocket。但对大多数实时数据推送需求,ResponseBodyEmitter绝对是更优的选择!

我爱娃哈哈的专栏 1175

想在controller里面把实时调第三方模型的实时返回的流式数据,按每句话用String类型return给前端

方法是异步的,并使用一个新线程或者异步任务来获取第三方模型的实时数据,并将数据逐行发送给客户端。对象,并在一个新的线程中启动了获取第三方模型实时数据的任务。每获取到一行数据,就使用。中将实时调用第三方模型的流式数据逐行返回给前端,可以使用 Spring MVC 的。在这种情况下,需要确保。将数据逐行发送给客户端。

suukii的博客 2817

SpringBoot + ResponseBodyEmitter 实时异步流式推送,优雅!

ChatGPT 的火爆,让流式输出技术迅速走进大众视野。在那段时间里,许多热爱钻研技术的小伙伴纷纷开始学习和实践 SSE 异步处理。我当时也写过相关文章,今天,咱们换一种更为简便的方式来实现流式输出,那就是ResponseBodyEmitter。其实,ResponseBodyEmitter并非新技术,早在 Spring Framework 4.2 版本就已被引入。直到最近,我们在开发一个滚动日...

路人甲Java 564

JAVA笔记 | ResponseBodyEmitter等异步流式接口快速学习

先简单记录下简单使用跟测试,后续再补充具体,最近有用到,简单来说就是后端(服务端)编写个发射器,实现一次请求,一直向前端客户端发射数据,直到发射器执行完毕,模拟ai一句一句回复的效果。

qq_37630282的博客 1060

SpringBoot+ResponseBodyEmitter异步流式推送神技,非常强大!

ResponseBodyEmitter 就像 Spring Boot 送给 Java 开发者的一件神器,它让我们能够轻松实现高效的异步流式推送。通过本文的学习,你应该已经掌握了以下核心技能:理解异步流式推送的必要性和应用场景掌握 ResponseBodyEmitter 的核心方法和使用技巧学会与其他技术的对比分析和选型策略了解常见问题的解决方案和性能优化技巧最后,我想说的是,技术的价值不在于它有多复杂,而在于它能解决什么问题。

竹林幽深 410
上一篇: Missing API key * run /login, Claude code 报错
下一篇: Spring 7.0 M7 重磅发布:虚拟线程、AOT 增强
nextera-void
博客等级 码龄14年 332粉丝 51原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nextera-void

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值