基于SSE的轻量级实时通信库Hermes:Web应用实时消息推送实践

news2026/5/16 8:04:11
1. 项目概述一个为Web应用量身打造的“信使”最近在折腾一个前后端分离的项目后端服务部署在云端前端应用则直接跑在用户的浏览器里。一个老生常谈的问题又摆在了面前如何让前端能实时、可靠地获取后端的数据变更通知比如用户A在后台发布了一条新动态如何让正在浏览页面的用户B的浏览器能立刻感知并刷新内容传统的轮询Polling效率低下且浪费资源而WebSocket虽然强大但直接在前端维护一个长连接涉及到连接管理、重连、心跳等一堆繁琐的事情对于很多中小型应用来说有点“杀鸡用牛刀”的感觉。就在这个当口我发现了reallygood83/hermes-for-web这个项目。光看名字就很有意思“Hermes”是希腊神话中的信使之神负责传递信息。这个项目定位为一个轻量级的、专门为Web前端设计的实时消息通信库。它没有选择去重新发明轮子比如自己实现一套通信协议而是巧妙地站在了巨人的肩膀上——利用现代浏览器广泛支持的Server-Sent Events (SSE)技术作为传输层并在此基础上构建了一套更友好、更健壮的客户端API。简单来说它帮你封装了SSE连接建立、事件监听、错误处理和自动重连等所有脏活累活让你可以像使用一个普通的JavaScript事件监听器一样轻松地订阅来自服务器的实时消息流。这对于需要实现通知推送、数据看板实时更新、协同编辑提示等功能的Web应用来说无疑是一个“开箱即用”的利器。2. 核心设计思路与技术选型解析2.1 为什么是Server-Sent Events (SSE)在讨论Hermes的实现之前我们必须先理解它为何选择SSE作为基石。Web实时通信领域除了前面提到的轮询和全双工的WebSocketSSE是一个经常被忽视但极其适合特定场景的技术。SSE的核心原理非常简单它基于普通的HTTP协议允许服务器主动向客户端浏览器推送数据。连接建立后这个HTTP连接会一直保持打开状态服务器可以随时通过这个连接发送一系列遵循特定格式data:、event:、id:等字段的文本消息。浏览器端的EventSourceAPI 则负责接收并解析这些消息将其转换为JavaScript事件。那么相比其他方案SSE的优势在哪里协议简单天然兼容HTTP生态SSE就是纯文本的HTTP流。这意味着它不需要像WebSocket那样进行协议升级能无缝通过绝大多数防火墙和代理服务器也更容易被现有的HTTP监控、日志、负载均衡设施所理解和管理。对于已经基于RESTful API构建的后端服务增加一个SSE端点几乎零成本。自动重连与事件IDEventSource原生支持自动重连。如果连接意外断开客户端会尝试重新连接。更妙的是SSE协议支持发送消息ID重连后客户端可以通过Last-Event-ID请求头告知服务器“我最后收到的消息ID是X”服务器从而可以只发送遗漏的消息避免了数据丢失或重复。单向通信的完美匹配很多实时场景如新闻推送、股价更新、服务器状态广播信息流主要是从服务器到客户端的单向流动。SSE正是为这种“一对多”的广播或“一对一”的服务器推送场景量身定做的。使用全双工的WebSocket来处理这种场景相当于只用了它一半的能力却引入了更多的复杂性。Hermes的洞察正在于此它识别出大量Web应用需要的只是一个稳定、可靠的服务器到客户端的单向消息通道。直接使用原生EventSource虽然可以但其API较为简陋错误处理不够完善也不提供更高级的抽象如连接状态管理、消息格式化。hermes-for-web的价值就是填补了这个空白。2.2 Hermes的架构与核心抽象hermes-for-web并没有重新实现网络层它的核心工作是做一个优秀的“包装工”和“调度员”。其架构可以理解为以下几个层次传输层适配底层依赖于浏览器原生的EventSource或兼容的polyfill来建立SSE连接。这一层处理最原始的字节流按照SSE协议规范解析出离散的消息事件。连接管理层这是Hermes的核心。它封装了连接的整个生命周期初始化、连接建立、活跃状态维护、异常断开、自动重连策略可配置重试次数和延迟、以及最终的连接关闭。它维护着一个内部的状态机让使用者可以清晰地知道当前连接处于何种状态。事件分发层SSE协议支持自定义事件类型通过event:字段。Hermes在此基础上提供了更精细的事件监听机制。你可以订阅特定类型的事件如message,notification,stock-update也可以订阅所有事件。它内部维护了一个事件监听器映射表确保事件能被精准地分发给对应的回调函数。应用层API对外暴露出一套简洁、直观的Promise风格或回调风格的API。例如connect(url, options)用于建立连接on(eventType, callback)用于订阅事件close()用于手动关闭连接。它隐藏了所有底层细节让开发者专注于业务逻辑。这种设计遵循了“单一职责”和“依赖倒置”原则。传输层的变化未来如果支持其他协议不会影响上层的业务逻辑应用层也无需关心网络的重连和心跳。这种清晰的分离使得库本身非常稳定也易于测试和维护。3. 快速上手指南与基础用法理论说得再多不如动手跑一遍。我们来看看如何将一个简单的Hermes集成到你的项目中并实现一个基础的实时消息接收功能。3.1 安装与引入首先你需要将Hermes添加到你的项目中。假设你使用npm或yarn进行包管理npm install reallygood83/hermes-for-web # 或 yarn add reallygood83/hermes-for-web然后在你的JavaScript或TypeScript模块中引入它。Hermes是用TypeScript编写的提供了良好的类型提示。// 使用ES Modules import { HermesClient } from reallygood83/hermes-for-web; // 或者在CommonJS环境中 // const { HermesClient } require(reallygood83/hermes-for-web);3.2 建立第一个连接假设你的后端提供了一个SSE端点地址是https://api.your-app.com/events。前端建立连接非常简单// 创建一个Hermes客户端实例 const client new HermesClient({ url: https://api.your-app.com/events, // 其他可选配置项例如 // retryInterval: 3000, // 重连间隔(毫秒) // maxRetries: 5, // 最大重试次数 // withCredentials: true // 是否发送凭据如Cookies }); // 启动连接 client.connect().then(() { console.log(Hermes连接成功); }).catch((error) { console.error(连接失败:, error); });connect()方法返回一个Promise这使得你可以在连接成功后再执行后续初始化逻辑代码流程更清晰。3.3 订阅与处理消息连接建立后就可以订阅你感兴趣的事件了。服务器发送的SSE消息可以带有event字段。例如服务器发送event: notification前端就可以监听notification事件。// 订阅名为 notification 的服务器事件 client.on(notification, (data) { // data 是服务器发送的消息体通常是JSON字符串解析后的对象 console.log(收到新通知:, data); // 在这里更新UI例如显示一个弹窗或更新消息角标 displayNotification(data.title, data.content); }); // 订阅默认的 message 事件如果服务器发送的消息未指定event类型或event为message client.on(message, (data) { console.log(通用消息:, data); }); // 你甚至可以订阅所有事件用于调试或特殊处理 client.on(*, (eventName, data) { console.log(收到任意事件[${eventName}]:, data); });3.4 连接状态管理与错误处理可靠的实时通信必须考虑网络的不稳定性。Hermes提供了连接状态查询和错误监听。// 监听连接状态变化 client.on(state_change, (newState, oldState) { console.log(连接状态从 ${oldState} 变为 ${newState}); // 可以根据状态更新UI例如连接中断时显示“重连中...”的提示 if (newState connected) { hideReconnectingIndicator(); } else if (newState connecting || newState reconnecting) { showReconnectingIndicator(); } }); // 监听错误 client.on(error, (error) { console.error(Hermes客户端错误:, error); // 错误可能包含网络错误、解析错误等 });3.5 断开连接当组件卸载或用户离开页面时记得主动关闭连接以释放资源。// 在Vue/React组件的卸载生命周期中或页面离开前 function cleanup() { if (client) { client.close(); // 关闭连接并清理所有事件监听器 console.log(连接已关闭); } } // 例如在React的useEffect清理函数中 useEffect(() { // ... 连接和订阅的代码 return cleanup; }, []);实操心得一连接时机不要在应用初始化时就立即连接。最好在用户交互后如页面加载完成、用户登录成功后再建立SSE连接。这能避免无效的连接占用服务器资源也符合按需使用的原则。例如可以在用户成功登录后用获取到的token构造一个带认证参数的SSE URL再连接。4. 高级配置与实战场景剖析掌握了基础用法我们来看看如何通过配置和模式来应对更复杂的生产环境需求。4.1 认证与安全在生产环境中SSE端点必须是受保护的。Hermes支持标准的HTTP认证方式。方式一URL查询参数。最简单的方式适合token认证。const token getAuthToken(); // 从本地存储或状态管理获取 const client new HermesClient({ url: https://api.your-app.com/events?token${encodeURIComponent(token)}, });注意这种方式下token会暴露在浏览器历史记录和服务器日志中安全性较低。建议用于时效性极短的token或内部系统。方式二HTTP Header。更安全的方式是携带认证头。这需要服务器在建立SSE连接时支持CORS跨域资源共享并允许相应的自定义头。const client new HermesClient({ url: https://api.your-app.com/events, headers: { Authorization: Bearer ${getAuthToken()}, // 可以添加其他自定义头 }, });你需要确保你的后端SSE端点配置了正确的CORS策略允许来自你前端域的请求并允许Authorization头。方式三使用Cookies。如果你的前后端在同一域名下最简单的就是依赖会话Cookie。const client new HermesClient({ url: /api/events, // 使用相对路径同源 withCredentials: true, // 关键告诉浏览器发送凭据Cookies });这种方式最省心但要求前后端同域。注意事项心跳与超时SSE连接可能因为代理服务器或负载均衡器的空闲超时设置而被断开。即使网络层连接还在应用层连接可能已“僵死”。常见的解决方案是服务器端定期发送注释行以:开头的行浏览器会忽略它。Hermes客户端虽然不直接生成心跳但你可以通过监听任何消息来重置一个本地的“空闲计时器”。更优雅的做法是让服务器每隔15-30秒发送一个特殊的事件如event: ping客户端收到后回复一个event: pong这需要额外的Ajax请求因为SSE是单向的或者仅用于维持连接活性。4.2 实现一个带重试机制的实时数据看板假设我们要构建一个服务器监控看板需要实时显示CPU、内存使用率。网络可能不稳定我们需要优雅地处理中断和重连并在重连后尝试恢复丢失的数据。import { HermesClient } from reallygood83/hermes-for-web; class MetricsDashboard { constructor(serverUrl) { this.lastEventId null; // 用于记录最后收到的消息ID以便断线重连后恢复 this.client null; this.serverUrl serverUrl; this.initClient(); } initClient() { this.client new HermesClient({ url: this.serverUrl, retryInterval: 2000, // 重连等待2秒 maxRetries: 10, // 最多重试10次 // 初始连接时可以携带Last-Event-ID headers: this.lastEventId ? { Last-Event-ID: this.lastEventId } : {} }); this.client.on(state_change, (state) { this.updateConnectionStatus(state); }); this.client.on(metrics, (data) { // 假设服务器发送的data格式为 {id: 123, cpu: 45, memory: 60} this.lastEventId data.id; // 保存最新的事件ID this.updateChart(data); }); this.client.on(error, (err) { console.error(Metrics stream error:, err); // 可以在这里上报错误到监控系统 }); this.connect(); } async connect() { try { await this.client.connect(); console.log(Metrics stream connected.); } catch (err) { console.error(Initial connection failed, will retry according to strategy., err); // 初始连接失败Hermes会根据配置自动重试这里可以记录日志或提示用户 } } updateConnectionStatus(state) { const statusEl document.getElementById(connection-status); statusEl.textContent 状态: ${state}; statusEl.className status-${state}; } updateChart(metrics) { // 更新UI图表... console.log(更新图表数据:, metrics); } destroy() { if (this.client) { this.client.close(); } } } // 使用 const dashboard new MetricsDashboard(https://monitor.your-app.com/metrics-stream); // 在页面卸载时 window.addEventListener(beforeunload, () dashboard.destroy());在这个例子中我们利用了SSE的id字段和Last-Event-ID机制来实现数据恢复。Hermes客户端本身不自动处理这个头但我们可以通过监听事件来记录ID并在初始化或重连时手动设置请求头。更完善的库可能会在内部自动管理这个过程。4.3 与前端框架Vue/React集成在现代前端框架中我们需要确保SSE连接的生命周期与组件生命周期绑定避免内存泄漏。React Hooks 示例import { useState, useEffect, useRef } from react; import { HermesClient } from reallygood83/hermes-for-web; function useServerSentEvents(url, options {}) { const [messages, setMessages] useState([]); const [connectionState, setConnectionState] useState(disconnected); const clientRef useRef(null); useEffect(() { // 创建客户端实例 const client new HermesClient({ url, ...options }); clientRef.current client; client.on(state_change, (newState) { setConnectionState(newState); }); client.on(message, (data) { setMessages(prev [...prev, data]); // 将新消息添加到列表 }); client.on(error, (err) { console.error(SSE Error:, err); }); // 建立连接 client.connect(); // 清理函数组件卸载时关闭连接 return () { if (clientRef.current) { clientRef.current.close(); clientRef.current null; } }; }, [url]); // 依赖项当url变化时会重新创建连接 return { messages, connectionState }; } // 在组件中使用 function NotificationPanel() { const { messages, connectionState } useServerSentEvents(https://api.your-app.com/notifications); return ( div div连接状态: {connectionState}/div ul {messages.map((msg, idx) ( li key{idx}{msg.content}/li ))} /ul /div ); }这个自定义Hook封装了Hermes客户端的创建、连接、订阅和清理逻辑使其可以在任何函数式组件中复用且连接生命周期与组件严格绑定。Vue 3 Composition API 示例import { ref, onUnmounted } from vue; import { HermesClient } from reallygood83/hermes-for-web; export function useServerSentEvents(url, options) { const messages ref([]); const connectionState ref(disconnected); let client null; const init () { client new HermesClient({ url, ...options }); client.on(state_change, (state) { connectionState.value state; }); client.on(notification, (data) { messages.value.push(data); }); client.connect().catch(err { console.error(连接失败, err); }); }; const disconnect () { if (client) { client.close(); client null; } }; onUnmounted(() { disconnect(); }); init(); // 立即初始化 return { messages, connectionState, disconnect // 可选暴露断开方法 }; }实操心得二连接数限制浏览器对同一域名下的并发HTTP连接数有限制通常为6个。一个SSE连接会长期占用一个连接。如果你的页面同时有多个SSE连接需求或者还有大量的Ajax请求可能会达到瓶颈。解决方案是1合并流让后端提供一个聚合的SSE端点发送不同类型的事件前端根据事件类型进行过滤分发。2使用HTTP/2HTTP/2支持多路复用可以在一个TCP连接上并行交错地传输多个请求和响应极大缓解连接数限制问题。确保你的服务器和CDN支持HTTP/2。5. 常见问题、性能优化与排查技巧即使使用了封装良好的库在实际部署中还是会遇到各种问题。下面是我在项目中趟过的一些坑和总结的优化点。5.1 常见问题速查表问题现象可能原因排查步骤与解决方案连接无法建立控制台报跨域错误CORS后端SSE端点未正确配置CORS响应头。1. 检查后端响应头是否包含Access-Control-Allow-Origin: [你的前端域名]或*不推荐生产环境用*。2. 如果使用了自定义头如Authorization需确认Access-Control-Allow-Headers包含该头。3. 对于带凭据的请求withCredentials: trueAccess-Control-Allow-Origin不能为*必须指定明确域名且需设置Access-Control-Allow-Credentials: true。连接建立后很快断开无错误信息代理服务器、负载均衡器或浏览器空闲超时。1. 实施心跳机制让服务器定期发送注释行或ping事件。2. 调整代理/负载均衡器的空闲超时设置如Nginx的proxy_read_timeout调大。3. 客户端可监听state_change到reconnecting状态进行UI提示。移动端4G网络下连接频繁断开重连移动网络不稳定NAT超时。1.降低重试频率增加retryInterval例如5000ms避免频繁重试消耗电量。2.指数退避Hermes可能支持或你需要自己实现重试延迟递增逻辑如2s, 4s, 8s...。3. 考虑使用App状态监听在应用回到前台时主动尝试重连。消息接收延迟或一段时间后收不到消息客户端事件循环阻塞或浏览器标签页被休眠。1. 检查前端是否有耗时同步操作阻塞了JavaScript主线程。2. 对于后台标签页浏览器可能会限制定时器和网络活动以节省资源。考虑使用Page Visibility API在页面不可见时暂停非关键更新可见时恢复或刷新数据。服务器发送了消息但客户端on回调未触发1. 事件名称不匹配。2. 消息格式不符合SSE规范。3. 客户端事件监听器在消息到达后才注册。1. 使用client.on(*, ...)监听所有事件检查实际收到的事件名和数据格式。2. 确保服务器发送的SSE文本以data:开头且每行以\n\n结束。事件行格式为event: yourEventName\n。3. 确保事件监听在client.connect()之前或至少同时设置。5.2 性能优化建议消息压缩如果推送的消息量很大如实时日志流可以考虑在服务器端对消息内容进行GZIP压缩。虽然SSE流本身不能整体压缩但可以对每条data:内的JSON字符串进行压缩如使用pako等库进行gzip压缩再base64编码客户端收到后再解压。这需要权衡CPU消耗和带宽节省。二进制数据SSE协议本身只支持UTF-8文本。如果需要传输二进制数据如图片、音频片段必须先在服务器端进行编码如Base64、ArrayBuffer转十六进制字符串在客户端解码。这会增加约33%的数据体积和编解码开销。如果二进制数据传输是主要需求WebSocket是更合适的选择。连接共享与复用如前所述避免在同一个页面内创建多个SSE连接。设计后端API时尽量规划一个“主事件流”端点通过不同的事件类型来区分业务。前端可以使用一个单例的Hermes客户端管理器来服务整个应用。智能重连与退避除了库自带的基础重试可以实现更智能的策略。例如在连续重连失败N次后提示用户“网络异常请检查网络”或者根据错误类型如HTTP 403/404不重试网络超时则重试决定是否重试。5.3 调试技巧浏览器开发者工具在Network网络标签页中找到你的SSE请求类型通常是eventsource或fetch取决于实现点击可以看到详细的请求和响应头。在Response响应标签页你可以实时看到服务器推送过来的原始数据流这是排查消息格式问题最直接的方式。使用*事件监听器在开发阶段用client.on(*, (eventName, data) console.log(eventName, data))来捕获所有流过的事件确保你订阅的事件名是正确的。模拟服务器对于前端开发有一个能发送SSE的模拟后端至关重要。你可以用Node.js的http模块快速写一个或者使用像Mock Service Worker (MSW)这样的工具来拦截API请求并模拟SSE响应这样可以在完全脱离后端的情况下进行开发和测试。6. 与WebSocket的对比及选型建议最后我们来谈谈这个终极问题我的项目到底该用SSE以及像Hermes这样的库还是WebSocket这是一个经典的“合适工具做合适事”的问题。下面是一个简单的决策矩阵特性维度Server-Sent Events (SSE)WebSocket通信方向单向服务器 - 客户端全双工服务器 - 客户端协议基于HTTP/HTTPS普通文本流独立的ws://或wss://协议二进制帧复杂度低。协议简单浏览器原生支持EventSource无需额外心跳可借助HTTP keep-alive。高。需要管理连接状态、实现心跳保活、处理二进制帧等。数据格式仅文本UTF-8。传输二进制需编码。原生支持文本和二进制帧。自动重连原生支持。浏览器EventSource自动处理。需手动实现。CORS/认证与普通HTTP请求相同处理简单。握手阶段类似HTTP但后续是独立协议。适用场景实时通知、新闻推送、监控数据流、股票行情、评论流只需服务器推送。在线聊天、协同编辑、实时游戏、远程控制、双向数据同步需要频繁双向通信。选型建议毫不犹豫选择 SSE (Hermes) 的情况你的应用场景本质上是服务器向客户端广播或推送数据而客户端向服务器发送请求的频率很低或可以通过普通的HTTP API如Ajax完成。例如仪表盘、实时价格更新、社交媒体动态流、服务器端日志推送。需要考虑 WebSocket 的情况应用需要频繁的、低延迟的双向交互。例如聊天应用用户不断发送和接收消息、多人在线游戏玩家状态实时同步、实时协作工具如Google Docs每个按键都需要双向同步。一个有趣的混合架构在一些大型应用中你甚至可以同时使用两者。用SSE来处理服务器向客户端的广播式通知比如“你有新邮件”而用WebSocket来处理需要双向高频交互的特定功能模块比如一个在线客服聊天窗口。hermes-for-web这样的库让你能以极低的成本引入SSE能力而无需承担WebSocket的复杂性。reallygood83/hermes-for-web这个项目正是抓住了SSE在特定场景下的巨大优势并通过一个精心设计的客户端库将这种优势转化为了开发者的生产力。它可能不是所有实时通信问题的银弹但对于那类典型的、以服务器推送为主导的Web应用来说它提供了一种近乎完美的、简洁而高效的解决方案。下次当你面临实时数据推送的需求时不妨先问问自己“我真的需要WebSocket吗”也许Hermes就是那个更轻巧、更合适的信使。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2617584.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…