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

推荐订阅源

OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Register - Security
The Register - Security
The Cloudflare Blog
Last Week in AI
Last Week in AI
月光博客
月光博客
Security Latest
Security Latest
Project Zero
Project Zero
博客园 - 司徒正美
AWS News Blog
AWS News Blog
雷峰网
雷峰网
博客园 - 聂微东
C
Cybersecurity and Infrastructure Security Agency CISA
P
Palo Alto Networks Blog
人人都是产品经理
人人都是产品经理
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tor Project blog
Martin Fowler
Martin Fowler
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Microsoft Azure Blog
Microsoft Azure Blog
B
Blog RSS Feed
A
About on SuperTechFans
V
V2EX
P
Proofpoint News Feed
腾讯CDC
S
Schneier on Security
K
Kaspersky official blog
Google Online Security Blog
Google Online Security Blog
Recorded Future
Recorded Future
H
Hacker News: Front Page
Application and Cybersecurity Blog
Application and Cybersecurity Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
C
CERT Recently Published Vulnerability Notes
aimingoo的专栏
aimingoo的专栏
博客园 - 叶小钗
美团技术团队
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
V
Vulnerabilities – Threatpost
C
Cisco Blogs
PCI Perspectives
PCI Perspectives
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 【当耐特】
N
News and Events Feed by Topic
Google DeepMind News
Google DeepMind News
Hugging Face - Blog
Hugging Face - Blog
阮一峰的网络日志
阮一峰的网络日志
P
Privacy International News Feed
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
W
WeLiveSecurity
Spread Privacy
Spread Privacy
S
Securelist

Chenxu's Blog

RAG检索优化:从 68% 到 82.2% 学习深度学习 北邮计算机研究生选课指北 学习机器学习 上海篇 提出一个好问题 写代码的环境 选购一台合适的设备 计算机的第零课 序言 内蒙古篇 山东篇 一篇对transformers的疑惑 浅析GPUStack 浅谈后端项目分层 浙江篇 河南篇 一些三端协同的开发工具记录 浅谈编程语言中的 GC 在 Windows 上安装 Rust 美化博客 消息队列 MyBatis 现代交换原理2024年题目 回忆版 北邮计科本科课程指北 ClkLog埋点用户分析系统研究报告 大数据梧桐实验分享 大数据HBase实验分享 国产向量数据库研究实践 —— 基于 Milvus 的电影推荐系统 服务器环境配置 大数据HDFS实验分享 数据库系统原理 操作系统 编译原理 手把手教你gozero从开发到部署 (番外篇): 介绍 gtodolist 前端项目 天津篇 北京篇 手把手教你gozero从开发到部署 (5): gtodolist 之任务的删除 前端相关问题 vue3 + element-plus学习 git相关命令 人工智能原理 使用 MongoTemplate 时开启事务 MySQL Redis 力扣刷题笔记之动态规划 记录一次 VSCode 出现的各种奇怪的问题 手把手教你gozero从开发到部署 (4): 在 model 中自己编写函数实现数据库分页查询 记录平时用到的 windows 右键管理 记录平时使用的 linux 的指令 安装 gstore 并使用 java api 手把手教你gozero从开发到部署 (3): gtodolist 之任务的创建和修改 手把手教你gozero从开发到部署 (番外篇): 记录第一次部署 CI/CD 的流程 记录完成 gtodolist 中遇到的 bug 手把手教你gozero从开发到部署 (2): gtodolist 之 user 模块开发 手把手教你gozero从开发到部署 (1): gtodolist 项目说明和环境准备 快速入门 gozero 框架 vue部署在nginx上的相关问题 记一次安装scrapy的报错 将博客部署至服务器 Java CS143: Compliers 《MySQL必知必会》读书笔记(2) 初识 GORM 初识 gin 框架 Spring 《MySQL必知必会》读书笔记(1) 本地搭建博客 🤝友链 🙋🏻‍♂️关于 Browser and Device Check
SSE多服务之间推送数据
Chenxu · 2024-03-29 · via Chenxu's Blog

起源

故事的起源是我有一个需求, 我需要做一个类似于 GPT 那样打字机效果的接口, 在查找资料的过程中我看到了这篇文章 , 由此我了解到了 SSE

此时我对 SSE 的理解就是获取到HttpServletResponsewriter然后写数据即可, 也没有做过什么复杂功能, 这个理解的简短代码如下:

res.setContentType("text/event-stream;charset=UTF-8");
res.getWriter().write("data: " + data + "\n\n"); // 这个 data: 不能删除
res.getWriter().flush();

后来面试中被问到了多个服务, sse 怎么传递数据, 一开始没有理解这个需求, 或者说是不理解这个需求有什么难点, 当时只是简单的认为多个服务就直接互相传HttpServletResponse就行. 但是经过一系列经历后发现, 这个需求并不简单, 最起码HttpServletResponse是不可序列化的, 所以他也无法在多个微服务之间传递.

再次遇到

在实习的过程中, 我遇到了一个需求, 原操作是通过流式输出向前端发送一个 xlsx 表格, 我需要使用feign对接这个接口. 经过查询资料后我获得了一个解决办法, 即: 先写入到 Feign 的 feign.Response, 调用方解析后再写入自己的HttpServletResponse. 由此这个需求算是解决了一半, 不过比较可惜, 这个接口后来被砍掉了.

从此我对于 SSE 在多服务之间的理解变成了: 通过写入中间的一个response然后进行转换.

解决了但并没有完全解决

不过就在最近我又遇到这个问题了, 这次我遇到的问题以及我想要实现的功能如下:

我希望前端请求服务 A, 服务 A 通过Dubbo调用服务 B, 服务 B 可以直接通过HttpServletResponse向前端推送数据.

根据资料搜索, 得知Dubbo中有一个RpcContext可以满足我的需求, 但是实际操作上似乎并不起作用, 这里 是我的一些尝试以及回复. 于是在经过大量尝试后我决定改变架构为如下:

这样就将问题转换成了在两个后端服务之间推送数据, 同样在尝试了大量操作后我发现如何保持HttpServletResponse成了一大难题. 经过了一整天的痛苦鏖战后, 我决定使用两个 websocket 解决这一问题 (没错, ✌ 叛变了), 目前我的完整的业务流程如下:

  1. 前端先使用 http 请求订阅服务, 这一步完成鉴权, 此时服务之间使用普通的 Rpc 调用.
  2. 订阅后前端在使用 websocket 请求, 此时各服务之间也都使用 websocket 推送数据, 因为第一步已经完成了鉴权, 第二步推送服务时也都是从已经完成鉴权的连接中选择进行推送数据, 保证了安全性.

其他

在此过程中我也就我的需求请求了群友大佬, 由此我还获取到了以下这些解决方法:

  • 使用 mq (放弃了, 因为不划算, 数据小没必要, 数据大会消息堆积)
  • 使用 redis 的发布订阅模式 (以前没了解过的新东西, 感觉不错, 但还是不太符合我这个需求)
  • 使用两个 sse 接口 (尝试过, 但是多线程的操作导致如何保持HttpServletResponse成为一个难题)

此过程中我也发现 SpringBoot 中可以通过SseEmitter使用 sse, 不需要再手动添加data: 这个符号 (或者说上面我用的 sse 并不标准). 具体可以参考这篇文章 , 文章代码没有复现, 但看起来讲的还是比较全的.

感觉可以改成 sse 避坑贴了

实测 sse 每次传输字符串长度大于 9000 多会被截断,所以保守起见,如果用 sse 一次传输大量数据的情况下建议 8000 字符分个块,前端将每次收到的数据块拼接合成一个完整数据,建议每个 sse 块用如下数据结构包装一下:

public class SSEResp{
    private Integer curIdx; // 当前第几个数据块
    private Integer allBlokNum; // 一共几个数据块
    private Boolean end; // 是否结束
    private SSEData data; // 真实数据json字符串的一部分
}