您现在的位置是:人工智能 >>正文
Server-Sent Events (SSE) 技术解析与实战
人工智能92人已围观
简介前言在当今互联网应用中,实时数据交互已成为关键需求。从AI聊天机器人到实时通知系统,从股票行情更新到协作编辑工具,都需要服务器能够主动向客户端推送数据。Server-Sent Events作为一种基于 ...
前言
在当今互联网应用中,术解实时数据交互已成为关键需求。析实从AI聊天机器人到实时通知系统 ,术解从股票行情更新到协作编辑工具 ,析实都需要服务器能够主动向客户端推送数据 。术解Server-Sent Events作为一种基于HTTP的析实单向服务器推送技术 ,在这些场景中展现出独特的术解优势。
SSE特别适合以下场景:
AI聊天机器人:服务器逐步返回AI生成的析实回答实时通知系统 :新消息 、提醒等通知推送日志监控:实时展示系统日志输出数据更新 :股票价格、云计算术解天气信息等实时更新效果图
图片
Server-Sent Events是析实一种允许服务器向客户端推送实时事件的技术。与传统的术解HTTP请求 - 响应模式不同,SSE建立的析实是一个持久的HTTP连接,服务器可以在任何时候通过这个连接向客户端推送数据 。术解
SSE具有以下特点:
基于标准HTTP协议,建站模板析实无需额外协议支持单向通信:仅服务器向客户端推送数据轻量级 :相比WebSocket,术解实现更简单支持自动重连:连接断开时客户端可自动尝试重新连接支持事件类型:可以推送不同类型的事件工作原理SSE的工作流程如下:
复制客户端 服务端 | | |-- GET /events -------->| 建立连接 |<-- 200 OK -------------| 返回事件流 |<-- data: message1 -----| 推送消息1 |<-- data: message2 -----| 推送消息2 |<-- ... ---| 持续推送1.2.3.4.5.6.7. 客户端通过EventSource API向服务器发起请求服务器建立连接并保持该连接打开服务器可以随时通过该连接向客户端推送事件数据当连接断开时,客户端会自动尝试重新连接服务器可以通过特定响应关闭连接SSE数据格式采用简单的文本格式,使用data:前缀表示数据内容,event:前缀表示事件类型 ,例如 :
复制event: message data: Hello, this is a server-sent event! data: This is another message without event type1.2.3.4. SSE 与其他实时技术的对比技术
连接类型
双向通信
实现复杂度
浏览器支持
适用场景
SSE
单向 HTTP
否
低
良好
服务器主动推送,单向数据流
WebSocket
双向 TCP
是免费模板
中
良好
双向实时通信,高交互场景
轮询
多次 HTTP
否
低
良好
简单场景 ,对实时性要求不高
长轮询
单次 HTTP
否
中
良好
服务器主动推送,中等实时性要求
实现 SSE
创建 SSE 控制器以下使用预设的中文回复示例,实际应用中这里会调用AI API
复制@RestController @RequestMapping("/api/sse") public class SseController { // 存储所有活跃的SSE连接 private final Map<String, SseEmitter> clients = new HashMap<>(); // 用于处理消息生成的线程池 private final ExecutorService executor = Executors.newFixedThreadPool(10); // 消息计数器,用于生成唯一消息ID private final AtomicInteger messageCounter = new AtomicInteger(0); // 超时时间设置为30分钟,避免频繁断开重连 private static final long TIMEOUT = 30 * 60 * 1000L; @GetMapping("/connect/{ clientId}") public SseEmitter connect(@PathVariable String clientId) { // 检查是否已有相同客户端ID的源码库连接 if (clients.containsKey(clientId)) { clients.get(clientId).complete(); } // 创建新的SseEmitter实例 SseEmitter emitter = new SseEmitter(TIMEOUT); // 添加心跳机制 ,每20秒发送一次心跳消息 executor.submit(() -> { try { while (true) { Thread.sleep(20000); if (emitter != null && clients.containsKey(clientId)) { emitter.send(SseEmitter.event().name("heartbeat").data("ping")); } else { break; } } } catch (Exception e) { emitter.completeWithError(e); } }); emitter.onTimeout(() -> { System.out.println("客户端连接超时: " + clientId); clients.remove(clientId); emitter.complete(); }); emitter.onError(e -> { System.out.println("客户端连接错误: " + clientId + ", 错误: " + e.getMessage()); clients.remove(clientId); emitter.completeWithError(e); }); // 注册完成事件处理器 emitter.onCompletion(() -> { System.out.println("SSE处理完成: " + clientId); clients.remove(clientId); }); clients.put(clientId, emitter); sendSystemMessage(emitter, "连接已建立 ,您可以开始与AI助手聊天了。"); return emitter; } @GetMapping("/chat/{ clientId}/{ message}") public void sendChatMessage(@PathVariable String clientId, @PathVariable String message) { SseEmitter emitter = clients.get(clientId); if (emitter == null) { return; } // 在单独线程中处理消息 ,避免阻塞主线程 executor.submit(() -> { try { String response = generateAiResponse(message); int messageId = messageCounter.incrementAndGet(); // 发送消息开始事件 emitter.send(SseEmitter.event() .name("messageStart") .data(MapUtil.builder() .put("id", messageId) .put("clientId", clientId) .build())); // 模拟AI逐步生成响应 String[] parts = response.split("(?<= 。| !|?|\\.|!|\\?)"); for (String part : parts) { if (part.trim().isEmpty()) continue; // 发送消息片段 emitter.send(SseEmitter.event() .name("messageFragment") .data(MapUtil.builder() .put("id", messageId) .put("content", part) .put("isLast", false) .build())); // 模拟思考时间 Thread.sleep(500 + (long)(Math.random() * 1000)); } // 发送消息结束事件 emitter.send(SseEmitter.event() .name("messageEnd") .data(MapUtil.builder() .put("id", messageId) .put("clientId", clientId) .build())); } catch (Exception e) { try { emitter.send(SseEmitter.event() .name("error") .data("生成AI回复时出错: " + e.getMessage())); } catch (IOException ex) { ex.printStackTrace(); } } }); } @PostMapping("/ack/{ clientId}/{ messageId}") public void acknowledgeMessage( @PathVariable String clientId, @PathVariable int messageId ) { System.out.println("收到消息确认: " + messageId + " 来自客户端: " + clientId); // 这里可以记录消息已确认,进行相应处理 } @GetMapping("/disconnect/{ clientId}") public void disconnect(@PathVariable String clientId) { SseEmitter emitter = clients.get(clientId); if (emitter != null) { clients.remove(clientId); emitter.complete(); } } private void sendSystemMessage(SseEmitter emitter, String content) { try { emitter.send(SseEmitter.event() .name("systemMessage") .data(MapUtil.of("content", content))); } catch (IOException e) { e.printStackTrace(); } } private String generateAiResponse(String prompt) { // 实际应用中这里会调用AI API // 这里使用预设的中文回复示例 String lowerCasePrompt = prompt.toLowerCase(); if (lowerCasePrompt.contains("你好") || lowerCasePrompt.contains("hi") || lowerCasePrompt.contains("hello")) { return"你好!我是高防服务器AI助手 ,很高兴为你服务。"; } elseif (lowerCasePrompt.contains("一安") || lowerCasePrompt.contains("一安未来")) { return"一安未来公众号致力于Java,大数据;心得交流,技术分享;"; } else { return"很有意思的问题!让我思考一下... " + prompt + " 是一个很好的话题。我可以从多个角度为你分析和解答,你是源码下载否想了解更多相关信息 ?"; } } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.95.96.97.98.99.100.101.102.103.104.105.106.107.108.109.110.111.112.113.114.115.116.117.118.119.120.121.122.123.124.125.126.127.128.129.130.131.132.133.134.135.136.137.138.139.140.141.142.143.144.145.146.147.148.149.150.151.152.153.154.155.156.157.158.159.160.161.162.163. 配置 CORS 复制@Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOriginPattern("*"); // 允许所有域名进行跨域调用 config.addAllowedHeader("*"); // 允许任何请求头 config.addAllowedMethod("*"); // 允许任何方法(POST、GET等) config.setAllowCredentials(true); // 允许携带凭证 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); // 对所有接口都有效 return new CorsFilter(source); } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.Tags:
转载:欢迎各位朋友分享到网络,但转载请说明文章出处“商站动力”。http://www.noorid.com/news/159a9999741.html
相关文章
全球多家银行遭遇安卓恶意软件攻击,幕后黑手为墨西哥黑客!
人工智能从2021年6月到2023年4月,墨西哥黑客一直在使用安卓恶意软件攻击全球金融机构的,特别是西班牙和智利的银行。安全研究员Pol Thill表示,攻击活动是由一个代号为Neo_Net的黑客发起的。Th ...
【人工智能】
阅读更多AMD7570显卡(解析AMD7570显卡的性能、特点和适用场景)
人工智能AMD7570显卡是一款性能与性价比兼具的显卡产品,它在游戏、设计和多媒体处理等领域都有出色的表现。本文将从性能、特点和适用场景等多个方面对AMD7570显卡进行分析和解读。一:性能可靠稳定,助力高清 ...
【人工智能】
阅读更多所问数据的质量如何?(以数据质量指标为评估标准进行分析)
人工智能在当今信息化时代,数据成为了我们日常生活和工作中不可或缺的一部分。然而,对于数据的质量如何,很多人并没有深入了解。本文将以数据质量指标为评估标准,对所问数据的质量进行分析,并给出一些改进的建议。1.数 ...
【人工智能】
阅读更多