从零实现浏览器端 Redis 在线查询:WebSocket 隧道
一、要解决什么问题做一个在线 Redis 查询工具技术挑战分两层第一层浏览器怎么连 Redis浏览器 JS 只能发 HTTP/WebSocket不能直接建立 TCP 连接到 Redis 的 6379 端口。必须有一个中间层做协议转换。第二层用户 Redis 在内网怎么办公司内网的192.168.25.71:6379云端服务器根本访问不到。二、整体架构┌──────────┐ WebSocket ┌────────────────┐ WebSocket ┌──────────────┐ TCP:6379 ┌────────┐ │ 浏览器 │ ◄──────────► │ Spring Boot │ ◄──────────► │ Python Agent │ ◄────────► │ Redis │ │ Vue 2 │ │ (消息转发) │ │ (redis-py) │ │ (内网) │ └──────────┘ └────────────────┘ └──────────────┘ └────────┘浏览器WebSocket 客户端发 JSON 命令Spring Boot纯消息转发不解析 Redis 协议不存密码Agent收到 JSON → 调用 redis-py 执行 → 返回结果NginxWebSocket Upgrade 代理三、Spring Boot 端纯透明转发3.1 会话配对ComponentpublicclassSessionManager{// sessionId → { agentSession, browserSession }privatefinalConcurrentHashMapString,SessionPairpairsnewConcurrentHashMap();// Agent 连接 → 生成 8 位 sessionIdpublicStringregisterAgent(WebSocketSessionagentSession){StringsessionIdUUID.randomUUID().toString().replace(-,).substring(0,8);SessionPairpairnewSessionPair(sessionId);pair.agentSessionagentSession;pairs.put(sessionId,pair);returnsessionId;}// 浏览器连接 → 和 Agent 配对publicbooleanpairBrowser(StringsessionId,WebSocketSessionbrowserSession){SessionPairpairpairs.get(sessionId);if(pairnull)returnfalse;pair.browserSessionbrowserSession;returntrue;}// 消息转发Browser → AgentpublicvoidrelayToAgent(StringsessionId,Stringmessage){SessionPairpairpairs.get(sessionId);if(pair!nullpair.agentSession!nullpair.agentSession.isOpen()){pair.agentSession.sendMessage(newTextMessage(message));}}}3.2 两个 WebSocket 端点ConfigurationEnableWebSocketpublicclassWebSocketConfigimplementsWebSocketConfigurer{OverridepublicvoidregisterWebSocketHandlers(WebSocketHandlerRegistryregistry){registry.addHandler(agentHandler,/ws/agent);// Agent 连这里registry.addHandler(browserHandler,/ws/browser/*);// 浏览器连这里}}AgentWebSocketHandler在 Agent 连接时生成 sessionId 返回之后收到的每条消息都 relay 给浏览器。BrowserWebSocketHandler从 URL 路径/ws/browser/{sessionId}提取 ID配对后收到的每条消息 relay 给 Agent。3.3 关键坑AuthFilter 拦截 WebSocket 握手Spring Boot 的Filter先于 WebSocket 处理器执行。ApiAuthFilter拦截了/ws/**握手阶段的 HTTP Upgrade 请求被 401 拦截。加一行白名单即可if(apiUrl.startsWith(/api/pub/)||apiUrl.startsWith(/ws/)){filterChain.doFilter(servletRequest,servletResponse);return;}四、Python Agent协议转换核心4.1 消息协议所有通信走 JSON清晰可调试// 连接 Redis浏览器 → Agent:{type:connect,host:192.168.25.71,port:6379,password:xxx,db:0}Agent → 浏览器:{type:connected,msg:192.168.25.71:6379 DB0 - PONG}// 执行命令浏览器 → Agent:{type:query,command:HGETALL user:1001}Agent → 浏览器:{type:result,result:{name:Alice,age:25},resultType:map}// 错误Agent → 浏览器:{type:error,message:Connection refused}4.2 Redis 连接与命令执行importredisasredis_libdefconnect_redis(ws,msg):globaldb dbredis_lib.Redis(hostmsg.get(host),portmsg.get(port,6379),passwordmsg.get(password)orNone,dbmsg.get(db,0),socket_connect_timeout5,socket_timeout5,decode_responsesTrue,protocol2,# ← 关键强制 RESP2兼容 Redis 6.0)db.ping()defquery_redis(ws,msg):partsshlex.split(msg[command])# HGETALL user:1001 → [HGETALL, user:1001]cmd,argsparts[0].upper(),parts[1:]ifcmdnotinREDIS_READ_CMDS:# 白名单校验returnsend_error(ws,fCommand {cmd} not allowed)resultdb.execute_command(cmd,*args)# 底层调用比反射更可靠send(ws,{type:result,result:display,resultType:rtype})4.3 兼容旧版 Redisredis-py8.x 默认用 RESP3 协议连接时先发HELLO 3协商。Redis 6.0 不支持这个命令直接报错unknown command HELLO, with args beginning with: 3解决办法连接时强制指定 RESP2。dbredis.Redis(...,protocol2)一行搞定兼容所有 Redis 版本。4.4 连接稳定性浏览器 ──ping/15s──► 服务器 ──relay──► Agent ← 应用层心跳 ▲ │ └── pong ────────────┘ ← 服务端 echo 回执 Agent 断开 → 保留 sessionId → 带旧 ID 重连 ← 浏览器无感恢复 浏览器断开 → 指数退避重连2s/4s/8s最多3次 ← 超过3次提示检查 AgentAgent 主循环用whileWebSocketApp每次重连带?sessionId旧IDwhileshould_run[0]:wsWebSocketApp(build_url(),...)ws.run_forever(ping_interval0)delaymin(delay*2,30)time.sleep(delay)五、前端Vue 2 实现5.1 双模式切换el-radio-groupv-modelmodeel-radio-buttonlabeldirect直连模式/el-radio-buttonel-radio-buttonlabelagentAgent 模式/el-radio-button/el-radio-group直连模式HTTP POST 给后端后端用 Jedis 直连 Redis同网段场景Agent 模式WebSocket 发到 AgentAgent 在本地执行后返回5.2 WebSocket 连接管理connectViaAgent(){constwsUrl${location.protocolhttps:?wss::ws:}//${location.host}/ws/browser/${this.conn.sessionId}this.wsnewWebSocket(wsUrl)this.ws.onopen(){this.startHeartbeat()// 15s 间隔 ping/pongthis.ws.send(JSON.stringify({type:connect,host,port,password,db}))}this.ws.onmessage(e){constmsgJSON.parse(e.data)if(msg.typeconnected){this.connectedtrue// 连接成功显示命令输入区}elseif(msg.typeresult){this.resultmsg// 查询结果停止 loadingthis.loadingfalse}}}5.3 结果智能渲染根据resultType自动选渲染方式!-- string --divv-ifresTypestringdivclassmeta{{ resSize }}/divprev-ifisJson{{ resJson }}/pre!-- JSON 自动格式化 --spanv-else{{ resStr }}/span!-- 长文本折叠 --buttonclickcopyResult复制/button/div!-- list --divv-else-ifresTypelistdivclassmeta{{ resList.length }} items/divdivv-for(v,i) in pagedList:class{stripe: i%2}span{{ i }}/spancode{{ v }}/code/divbuttonclickloadMore显示更多/button!-- 分页加载 --/div!-- map --divv-else-ifresTypemapinputv-modelfilterplaceholder筛选字段.../!-- 搜索过滤 --tabletrv-fork in filteredKeystdcode{{ k }}/code/tdtdcode{{ resMap[k] }}/code/td/tr/table/div!-- nil --divv-else-ifresStr(nil)classnil-tip(键不存在)/div每种类型的渲染方式不同string 展示字节数 折叠list 斑马纹 分批加载map 搜索框 隔行变色nil 独立卡片。六、部署架构6.1 Nginx WebSocket 代理location /ws/ { proxy_pass http://127.0.0.1:8000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_read_timeout 3600s; proxy_send_timeout 3600s; }6.2 Agent 打包pipinstallpyinstaller pyinstaller--onefile--nameagent--iconagent.ico agent.py# dist/agent.exe12MB单文件可分发七、结果展示八、总结核心设计思路就三条协议转换下沉到 Agent服务器不碰 Redis 协议只做消息转发。Agent 用原生 redis-py 连接不受浏览器限制Agent 主动出站WebSocket 从内网往外连天然穿透 NAT/防火墙用户零网络配置在线体验https://onltool.site

相关新闻

深度学习环境配置终极指南:从CUDA、cuDNN到PyTorch的兼容性全解析

深度学习环境配置终极指南:从CUDA、cuDNN到PyTorch的兼容性全解析

1. 项目概述:为什么深度学习环境配置是“玄学”? 每次打开深度学习项目,准备大干一场,结果第一步就被环境配置卡住半天,这种感觉太熟悉了。CUDA版本不对、PyTorch装不上、跑起来报各种“RuntimeError: CUDA error”………

2026/10/17 16:01:34 阅读更多 →
暗黑3一键战斗终极指南:D3KeyHelper图形化宏工具完整教程

暗黑3一键战斗终极指南:D3KeyHelper图形化宏工具完整教程

暗黑3一键战斗终极指南:D3KeyHelper图形化宏工具完整教程 【免费下载链接】D3keyHelper D3KeyHelper是一个有图形界面,可自定义配置的暗黑3鼠标宏工具。 项目地址: https://gitcode.com/gh_mirrors/d3/D3keyHelper 想要在暗黑破坏神3中彻底解放双…

2026/10/16 20:56:36 阅读更多 →
像素vs条数级虚拟列表

像素vs条数级虚拟列表

目录 面试(像素):定/估高 缓冲区 DOM 数学公式(缓冲区) 完整代码 动态高度:估高 高度缓存二分查找 避坑指南 找起始索引 start:改用“二分查找” 关于 endIdx 的查找(用二分…

2026/10/14 0:59:58 阅读更多 →

最新新闻

STM32寄存器编程实战:GPIO、定时器与串口

STM32寄存器编程实战:GPIO、定时器与串口

每次看到论坛里有人说“寄存器难”,我都想拉住他聊五分钟。我入行那几年也被寄存器折磨过,直到有一次调一个功耗敏感的项目,手里的库函数版本和例程对不上,底层配置出来的引脚电平就是不对。从那以后我翻烂了参考手册,…

2026/10/17 16:01:21 阅读更多 →
OpenClaw从入门到应用——工具(Tools):apply-patch 实战配置与验证

OpenClaw从入门到应用——工具(Tools):apply-patch 实战配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 16:01:21 阅读更多 →
Oracle实战:开放式基金交易平台数据库设计与PL/SQL编程实现

Oracle实战:开放式基金交易平台数据库设计与PL/SQL编程实现

简介:这是一份Oracle项目实战文档,以开放式基金交易平台数据库设计为完整案例,适合Oracle初学者与数据库设计人员用于练习从需求分析到表结构落地的全过程。文档围绕招商银行某分行的实际业务场景,梳理基金管理、客户账户管理、交…

2026/10/17 16:01:21 阅读更多 →
Docker部署TailChat开源即时通讯平台:用TaoToken统一Key打通消息机器人与AI能力

Docker部署TailChat开源即时通讯平台:用TaoToken统一Key打通消息机器人与AI能力

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 16:01:21 阅读更多 →
DC-6靶机深度解析:WordPress渗透与Linux提权实战指南

DC-6靶机深度解析:WordPress渗透与Linux提权实战指南

1. 项目概述:为什么DC-6是WordPress渗透学习绕不开的“分水岭”如果你正在系统性地打磨Web渗透能力,尤其是想真正吃透WordPress生态下的攻防逻辑,DC-6绝不是一张可跳过的练习卷——它是一道明确的分水岭。我带过不少刚从基础靶机(…

2026/10/17 16:01:21 阅读更多 →
基于深度学习的疲劳驾驶检测:目标检测、图像分类与实时推理实战

基于深度学习的疲劳驾驶检测:目标检测、图像分类与实时推理实战

我还记得去年做毕设选题那会儿,导师给了我几个方向,我一眼就看中了"基于深度学习的疲劳驾驶检测"这个题目。原因很直接:疲劳驾驶导致的事故率一直居高不下,而市面上很多所谓"智能驾驶预警"要么靠红外打哈欠检…

2026/10/17 16:00:20 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →