服务端

  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. 等保测评FAQ

    之前写过一篇关于等保测评的相关介绍<一起聊聊等保测评>,发现大家对于等保测评这个还是很关注的,有些人问等保测评这份工工作的,也有些人问关于等保测评一些指导意见的,这篇文章我想把大家的问题来 ...

  2. 《使用Gin框架构建分布式应用》阅读笔记:p108-p126

    <用Gin框架构建分布式应用>学习第8天,p108-p126总结,总计18页. 一.技术总结 1.Redis eviction policy (1)什么是 eviction policy? ...

  3. CSS动画(波光粼粼登录页面)

    1.整体效果 https://mmbiz.qpic.cn/sz_mmbiz_gif/EGZdlrTDJa4AbemkU3vLRIDzTIgPHSjicia97wfvMVAhqZL4lsGbQQCbsV ...

  4. 狂神说-Docker基础-学习笔记-07 容器数据卷

    狂神说-Docker基础-学习笔记-07 容器数据卷 视频地址:https://www.bilibili.com/video/BV1og4y1q7M4?p=21 什么是容器数据卷 运行时数据都在容器中 ...

  5. 92. 反转链表 II Golang实现

    题目描述: 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right .请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 ...

  6. ESP32+L298N+MQTT+4G无线远程监控+四驱动小破车

    ESP32+L298N+MQTT+4G无线远程监控+四驱动小破车 小车源代码 注意修改WIFI 信息 #include <analogWrite.h> #include <WiFi. ...

  7. string,字符串使用指南

    string 创建 创建一个字符串或者字符串数组如下 用 cin 输入,可以读一整串字符直到空格或换行才结束 #include <iostream> using namespace std ...

  8. git 暂存区问题

    如果需要合别人的代码进来 需要暂时把自己的代码stash一下,用 git stash 放入暂存 如果需要释放出来用 git stash pop 当暂存用的越来越多,问题出现了需要清理暂存区队列,使用代 ...

  9. Java注解annotation用法和自定义注解处理器

    前言: 在J2EE中,注解使得开发更加便利,省去了生成XML文件的过程,在Hibernate实体声明中,可以简简单单的用几个注解就可以免去生成一个XML的文件操作.这里就主要论述一下annotatio ...

  10. MySQL8.0安装与使用

    Windows: 1.下载:https://dev.mysql.com/downloads/mysql/ 推荐使用镜像:http://mirrors.sohu.com/mysql/MySQL-8.0/ ...