服务端

  1. websocket和event-stream的优缺点

WebSocket和Event-Stream(Server-Sent Events)都是实现实时通信的技术,但是它们各自有不同的优缺点。

️ WebSocket

  • 优点:
  1. 双向通信:WebSocket提供了一个全双工的通信通道,客户端和服务器可以同时发送和接收数据。
  2. 实时性:由于WebSocket是持久连接,所以它具有高实时性。
  3. 更少的数据传输量:WebSocket在建立连接后,数据传输时不需要包含HTTP头,因此数据传输量较小。
  4. websocket可传输较为复杂的数据结构,例如json、二进制字节等。
  5. websocket针对java、Python等语言支持较好
  6. websocket天然支持跨域问题(据说也有跨域问题,目前无法模拟出来)
  • 缺点:
  1. 虽然大部分现代浏览器都支持WebSocket,但是一些老版本的浏览器可能不支持。
  2. 协议复杂:WebSocket的协议相对复杂,需要处理连接、断开连接、心跳等问题。
  • 适用场景
  1. 适合较为复杂的业务场景,需要多次进行通讯,例如:聊天、游戏等

Event-Stream (Server-Sent Events):

  • 优点:
  1. 简单易用:Event-Stream的API相对简单,易于使用和理解。
  2. 自动重连:如果连接断开,Event-Stream会自动尝试重连。
  3. 基于HTTP:Event-Stream基于HTTP协议,因此可以利用现有的HTTP基础设施,如负载均衡和缓存。
  • 缺点:
  1. 单向通信:Event-Stream只支持服务器向客户端发送数据,不支持客户端向服务器发送数据。
  2. 实时性:由于Event-Stream是基于HTTP的,因此它的实时性可能不如WebSocket。
  3. 数据传输量:Event-Stream在每次发送数据时都需要包含HTTP头,因此数据传输量可能较大。
  4. 数据结构:Event-Stream传输仅仅支持字符形式,无法适应较复杂的场景
  5. java流式下发可能出现丢包、站包问题,需要自己实现编解码来对消息进一步处理
  • 适用场景
  1. 适合较为简单的场景,例如:大模型客户端发一次消息后,服务端返回结果

event-stream形式

JAVA

  • java实现SSE的方式主要有2种,即通过Spring mvc提供的SseEmitter和Flux。目前我用的比较多的是SseEmitter(原因:本人比较懒,此方式较为简单)
  • 只需定义一个 SseEmitter对象并输出即可,通过 SseEmitter.send()发送消息(发送消息需要异步执行)
  • SseEmitter构造函数中,超时时间设置为0,否则请求过长,会出现超时情况(默认超时时间和http接口相同,可通过spring配置进行设置)
  • SseEmitter种主要的方法
  1. send:发送消息
  2. complete():表示消息已结束
  3. completeWithError(): 发送错误并结束,多用于异常捕获中
   @GetMapping(value = "sseEmitterTest")
public Object sseEmitterTest(@RequestParam(value = "name") String name) {
SseEmitter sseEmitter = new SseEmitter(0L);
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 50, 60L, TimeUnit.SECONDS, queue);
threadPoolExecutor.execute(() -> {
char[] charArray = name.toCharArray();
for (char charStr : charArray) {
try {
sseEmitter.send(new String(new char[]{charStr}));
Thread.sleep(100);
} catch (IOException | InterruptedException e) {
sseEmitter.completeWithError(e);
}
}
sseEmitter.complete();
}); return sseEmitter;
}

客户端

JAVA

引入依赖

 <dependency>
<groupId>org.asynchttpclient</groupId>
<artifactId>async-http-client</artifactId>
<version>2.12.3</version>
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>3.5.2</version>
</dependency>

调用接口

消息处理类
public class DefaultMessageHandler implements AsyncHandler<Response> {

    @Override
public State onStatusReceived(HttpResponseStatus httpResponseStatus) {
return null;
} @Override
public State onHeadersReceived(HttpHeaders httpHeaders) {
return null;
} @Override
public State onBodyPartReceived(HttpResponseBodyPart httpResponseBodyPart) {
// 业务逻辑处理
return State.CONTINUE;
} @Override
public void onThrowable(Throwable throwable) { } @Override
public Response onCompleted() {
return null;
} }
主方法
@NoArgsConstructor
public class HttpStreamClient { private AsyncHandler<Response> asyncHandler; public HttpStreamClient(AsyncHandler<Response> asyncHandler) {
this.asyncHandler = asyncHandler;
} public void sendMessage(String url, String message) {
// 组装okhttp请求
AsyncHttpClient client = Dsl.asyncHttpClient();
BoundRequestBuilder requestBuilder = client.preparePost(url);
requestBuilder.setHeader("Accept", "text/event-stream");
requestBuilder.setHeader("Content-Type", "application/json");
requestBuilder.setBody(message);
requestBuilder.execute(asyncHandler);
} public static void main(String[] args) {
DefaultMessageHandler defaultMessageHandler = new DefaultMessageHandler();
HttpStreamClient httpStreamClient = new HttpStreamClient(defaultMessageHandler);
String message = "Hello WOrld!"
httpStreamClient.sendMessage("API", message);
}
}

Python

引入依赖

pip install requests

编码部分

import requests
import json def invokeStreamLlm():
requestBody = {
name: "Hello World!"
}
response = requests.post(
"API",
json=requestBody,
stream=True,
headers={"Content-Type": "application/json"},
)
if response.status_code != 200:
return
for line in response.iter_lines():
if line and line.strip():
content = json.loads(line[5:])
code = content['code']
if code == 200:
print(content['data'])
elif code == -200:
print('结束标识') if __name__ == "__main__":
content = invokeStreamLlm()

vue

  • 本地开发时,通过vue的正向代理,流式接口无法做到流式下发。数据会在最后一块全部下发
  • 本地想要流式下发解决方法:
  • 本地部署一个nginx,通过nginx反向代理接口后,可实现流式下发
  • 注:实际项目中,url则是vue配置的api,ip和端口采用代理来代替

引入依赖

npm install @microsoft/fetch-event-source -D

编码部分

  • penWhenHidden表示是否监听窗口变化,为true表示不见听窗口变化,集焦点离开窗口时,不自动断开重连
  • ctrl:
    • 由于sse接口存在断开重连机制,可能存在多次 调用的情况
    • 通过ctrl控制器,可以控制关闭客户端,不会再进行重连
 	<script>
import { fetchEventSource } from '@microsoft/fetch-event-source';
export default {
data() {
return {
ctrl: new AbortController(),
};
},
mounted() {
this.invokeSse();
},
methods: {
invokeSse() {
const that = this;
fetchEventSource('API', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: ['text/event-stream', 'application/json'],
},
body: JSON.stringify({
data: ''
}),
penWhenHidden: true,
signal: that.ctrl.signal,
onopen(event) {
console.log(event);
},
onmessage(msg) {
// 消息接收
console.log(msg);
},
onclose(e) {
console.log(e);
that.ctrl.abort();
},
onerror(err) {
console.log(err);
that.ctrl.abort();
},
});
}
},
};
</script>

Event-Stream技术的更多相关文章

  1. Azure Event Hub 技术研究系列2-发送事件到Event Hub

    上篇博文中,我们介绍了Azure Event Hub的一些基本概念和架构: Azure Event Hub 技术研究系列1-Event Hub入门篇 本篇文章中,我们继续深入研究,了解Azure Ev ...

  2. Azure Event Hub 技术研究系列3-Event Hub接收事件

    上篇博文中,我们通过编程的方式介绍了如何将事件消息发送到Azure Event Hub: Azure Event Hub 技术研究系列2-发送事件到Event Hub 本篇文章中,我们继续:从Even ...

  3. [Reactive Programming] Using an event stream of double clicks -- buffer()

    See a practical example of reactive programming in JavaScript and the DOM. Learn how to detect doubl ...

  4. Azure Event Bus 技术研究系列1-Event Hub入门篇

    前两个系列研究了Azure IoT Hub和Azure Messaging.最近准备继续研究Azure Event Bus,即Azure的事件中心.首先, Azure Event Hub的官方介绍: ...

  5. Azure Event Hub 技术研究系列1-Event Hub入门篇

    前两个系列研究了Azure IoT Hub和Azure Messaging.最近准备继续研究Azure Event Hub,即Azure的事件中心.首先, Azure Event Hub的官方介绍: ...

  6. Event Logging 技术简介

    https://blog.csdn.net/xiliang_pan/article/details/41805023

  7. Azure IoT Hub和Event Hub相关的技术系列-索引篇

    Azure IoT Hub和Event Hub相关的技术系列,最近已经整理了不少了,统一做一个索引链接,置顶. Azure IoT 技术研究系列1-入门篇 Azure IoT 技术研究系列2-设备注册 ...

  8. (ETW) Event Tracing for Windows 入门 (含pdf下载)

    内容提纲 • ETW 介绍 • ETW 使用 • ETW 监控本机Demo • ETW 监控远程机器的思路 • 底层类库:EventSource 介绍 • 底层类库:TraceEvent 介绍 ETW ...

  9. HTML5中的服务器‘推送’技术 -Server-Sent Events

    转帖:http://www.developersky.net/thread-63-1-1.html 一直以来,HTTP协议都是严格遵循Request-Response模型的.客户端发送一个Reques ...

  10. “服务器推”技术【转载+整理】

    原文地址 本文内容 "服务器推(server-push)"技术的应用 基于客户端套接口的"服务器推"技术 基于 HTTP 长连接的"服务器推" ...

随机推荐

  1. 看图认识CSS

    教程: https://www.w3cschool.cn/css/ https://www.runoob.com/css/css-tutorial.html https://www.w3school. ...

  2. CSP-S模拟赛20241004

    A 你考虑 可以把这个数组当中的每个数表示成另一种形式:\(a_i = k_i\times x+b\)(其中\(x\)是模数,\(b\)为余数). 对于求两个数是否对于某个余数同余,显然你要判断他们两 ...

  3. 基于Java+SpringBoot+Mysql实现的快递柜寄取快递系统功能实现八

    一.前言介绍: 1.1 项目摘要 随着电子商务的迅猛发展和城市化进程的加快,快递业务量呈现出爆炸式增长的趋势.传统的快递寄取方式,如人工配送和定点领取,已经无法满足现代社会的快速.便捷需求.这些问题不 ...

  4. RK3568,字符设备框架:管理同主设备号、不同次设备号设备

    字符设备框架:管理同主设备号.不同次设备号设备 以下代码针对迅为开发板RK3568,开发板系统是ubuntu20.04, 正文 以下是我写的字符设备框架,实现了管理同主设备号.不同次设备号的功能. 代 ...

  5. 鸿蒙NEXT开发案例:抛硬币

    [1]引言(完整代码在最后面) 本项目旨在实现一个简单的"抛硬币"功能,用户可以通过点击屏幕上的地鼠图标来模拟抛硬币的过程.应用会记录并显示硬币正面(地鼠面)和反面(数字100面) ...

  6. Codeforces Round 988 (Div. 3)

    Codeforces Round 988 (Div. 3) 总结 A 没啥好说的,用桶存出现次数. #include <iostream> #include <cstdio> ...

  7. 一个包含了 50+ C#/.NET编程技巧实战练习教程

    DotNetExercises介绍 DotNetGuide专栏C#/.NET/.NET Core编程技巧练习集:C#/.NET/.NET Core编程常用语法.算法.技巧.中间件.类库.工作业务实操练 ...

  8. 项目监控之sentry

    github: https://github.com/getsentry/sentry 1.什么是sentry? 当我们完成一个业务系统的上线时,总是要观察线上的运行情况,对于每一个项目,我们都没办法 ...

  9. GitBook之基本使用

    GitBook 简介 GitBook 官网 GitBook 文档 GitBook 准备工作 安装 Node.js GitBook 是一个基于 Node.js 的命令行工具,下载安装 Node.js,安 ...

  10. kettle 使用 CARTE 执行

    在执行KETTLE 任务的时候,可以使用本地执行,或者使用carte server执行. 1.启动carte server .\Carte.bat localhost 8080 2.配置子服务器 这里 ...