流式接口调用

服务端

  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. 适合较为复杂的业务场景,需要多次进行通讯,例如:聊天、游戏等

整理了一份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记的【点击此处即可】即可免费获取

  • 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(): 发送错误并结束,多用于异常捕获中
 

java

复制代码

@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

引入依赖
 

xml

复制代码

<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>

调用接口
消息处理类
 

java

复制代码

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; } }

主方法
 

java

复制代码

@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

  1. 引入依赖
 

python

复制代码

pip install requests

  1. 编码部分
 

python

复制代码

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和端口采用代理来代替
  1. 引入依赖
 

shell

复制代码

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

  1. 编码部分
 

vue

复制代码

<script> import { fetchEventSource } from '@microsoft/fetch-event-source'; export default { data() { return { }; }, mounted() { this.invokeSse(); }, methods: { invokeSse() { fetchEventSource('API', { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: ['text/event-stream', 'application/json'], }, body: JSON.stringify({ data: '' }), onopen(event) { console.log(event); }, onmessage(msg) { // 消息接收 console.log(msg); }, onclose(e) { console.log(e); }, onerror(err) { console.log(err); }, }); } }, }; </script>

  • 23
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值