尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
JFinal中WebSocket握手被拦截的根因与三套放行方案
前阵子接了个JFinal项目老板要加一个实时对话面板打算把DeepSeek大模型的回复通过WebSocket实时推给前端。我用DeepSeek辅助生成了一套集成代码结果前端WebSocket一连上后端控制台就是一堆握手失败。第一反应是代码写错了查来查去才发现根本不是DeepSeek生成的代码不行而是JFinal框架自己把WebSocket请求给拦了。这条路我踩了一整天把JFinal的拦截机制翻了个底朝天今天把排查过程和解决方案完整写出来。如果你也在JFinal里被WebSocket握手坑过或者正准备把DeepSeek这类大模型接进来这篇文章能省你大半天时间。1. 先认清JFinal的请求处理链才能定位“谁拦的”很多人在JFinal里跑WebSocket项目遇到的报错五花八门404、403、握手超时、連上就断。这些现象的根源几乎都在同一个地方——请求压根没走到你的WebSocket处理器而是被JFinal的请求链路提前“消化”或“拒绝”了。要搞清楚被谁拦就先得知道一个请求进到JFinal之后到底经过哪些关卡。1.1 一条请求进JFinal后的完整路径JFinal的请求链路本质上是一条Handler链。框架启动时会把所有Handler串起来请求进来后按顺序往下传最后才交给路由分发到具体的Controller或拦截器。这条链上的核心成员有全局Handler在路由之前的钩子可以处理静态资源、跨域、编码等通用逻辑。路由分发根据请求路径匹配到对应的Controller方法。全局拦截器真正进入Controller之前执行的拦截逻辑比如登录校验、权限控制。Controller自身拦截器绑定在类或方法上的拦截器。Controller方法你的业务代码。这个顺序很关键。WebSocket握手请求一旦到了路由分发这个环节就会被当成普通HTTP请求去匹配Controller。而WebSocket的握手本质上是HTTP协议里的Upgrade请求它期望的是直接升级成WebSocket连接而不是去执行某个Controller方法。如果你没有在路由之前把这个请求放行给WebSocket容器它就必然被拦截或者被错误分发。1.2 WebSocket握手请求和普通HTTP请求有什么区别WebSocket的连接建立分两步HTTP握手浏览器发一个GET请求请求头里带Upgrade: websocket和Connection: Upgrade以及Sec-WebSocket-Key等字段。协议升级服务端如果同意会返回101 Switching Protocols然后TCP连接直接变成WebSocket通道之后双方在这个通道上自由收发帧。也就是说WebSocket的第一次请求确实是一个HTTP GET请求但它不是普通的业务请求。它需要由容器的WebSocket处理器来响应而不是由你的Controller去处理。JFinal本身作为一个MVC框架默认情况下并不知道某个路径的WebSocket请求要怎么处理除非你显式地把它交给WebSocket容器。JFinal的拦截器拦截的是Controller调用链如果一个WebSocket请求被路由匹配到了某个没有实际意义的Controller方法那这个方法里的返回值、异常处理、拦截器逻辑都会干扰握手。更常见的情况是WebSocket请求根本匹配不到任何Controller直接返回404前端看到的就是握手失败。1.3 为什么说“DeepSeek生成的代码让JFinal拦了WebSocket”用DeepSeek辅助写代码的时候它给出的WebSocket集成方案大概率是标准写法前端连ws://localhost:8080/ws/deepseek后端开启一个WebSocket端点。但问题在于标准WebSocket端点在原生Java Web项目里很顺放进JFinal却不一定。因为JFinal默认把一切进入的请求都交给自己的Handler链处理如果WebSocket端点是通过框架之外的容器层注册的那要看这个端点的注册位置和JFinal的Handler链谁先执行。如果你的项目是用JFinal内置的Jetty启动的JFinal的Handler链在前面WebSocket请求先被JFinal的路由匹配逻辑过一遍这就会产生冲突。我当时的项目就是典型的这种情况DeepSeek生成的代码里把/ws/deepseek这个地址放在了Controller层等于告诉JFinal“这是一个普通Controller路径”。结果前端一握手JFinal就把它当成一个普通的GET请求在路由匹配阶段找不到对应方法然后一堆拦截器开始工作不是404就是被全局拦截器拦截返回了未登录提示。这本质上就是请求被框架“拦截”了而不是DeepSeek的对话功能有问题。2. WebSocket被拦到底卡在哪一层定位问题的时候别急着改代码先判断请求到底死在哪一环。根据我这次排查的经验WebSocket请求被拦截的位置无非四种拦截器层、Handler层、路由层、容器层。每一层的表现症状还不太一样。2.1 拦截器层最常见的误伤源头如果你项目里配置了全局拦截器比如登录拦截器、权限拦截器、Token校验拦截器那么WebSocket握手请求会像普通HTTP请求一样先经过这些拦截器。全局拦截器的执行时机是在Controller方法调用之前但前提是请求已经匹配到了某个Controller方法。如果手写代码时把WebSocket端点对应的路径映射到了一个Controller方法上那么握手请求就会先被打进拦截器链。很多项目的全局拦截器会做登录校验读不到用户Session就直接重定向到登录页面或者返回JSON错误。前端WebSocket收到一个302或者非101的响应握手就失败了。我当时遇到的有一个现象就是前端控制台显示WebSocket connection to ws://... failed: Error during WebSocket handshake: Unexpected response code: 302。这就是典型的被登录拦截器重定向了。如果握手路径压根没匹配到Controller那么JFinal的最终结果是走Handler链末尾的404处理根本不会进拦截器表现就是404或者HTTP/1.1 400 Bad Request。所以先别急得确认请求到底有没有走到Controller层。2.2 Handler层优先级比拦截器更高但经常被忽略Handler是JFinal里比拦截器更早执行的东西。你可以在JFinalConfig.configHandler()里往Handler链上追加自定义Handler。Handler的执行顺序是链式的每个Handler可以选择放行给下一个也可以直接响应请求。有些开发者图省事在全局Handler里做了跨域处理、静态资源处理、路径重写。比如写了一个RewriteHandler把某些路径重写成了别的Controller路径或者写了一个StaticHandler把某些前缀当成静态资源目录。如果你的WebSocket路径恰好在你自己的Handler里被正则匹配了那它可能在到达WebSocket容器之前就被当成静态文件或者被重写掉了。我排查的时候发现项目里有个老Handler专门处理前端静态资源请求它匹配了/ws/*这个模式以为是静态资源直接返回了文件内容。前端拿到的响应自然也不是101握手直接失败。2.3 路由层把WebSocket地址注册成了ControllerJFinal的路由表是典型的MVC路由路径匹配Controller方法。如果你的项目里用了类似me.add(/ws/deepseek, DeepSeekController.class)这种方式那就等于告诉JFinal这个路径是一个普通Controller入口。WebSocket握手请求到达这个Controller时会走一次完整的MVC生命周期拦截器、方法执行、返回渲染。就算Controller方法里啥都不写你的Controller类也可能被框架实例化、依赖注入、方法执行这一堆逻辑在WebSocket握手里毫无意义反而会破坏升级请求。典型的坑是Controller方法返回了一个字符串框架试图去渲染模板结果握手请求收到的是渲染后的HTML而不是101状态码。更隐蔽的是有些Controller类上面加了Before(AuthInterceptor.class)注解里面做用户鉴权WebSocket握手请求没有正常的HTTP请求头或Cookie逻辑直接被鉴权拦截器拒绝了。2.4 容器层Jetty/Tomcat对Upgrade的默认处理JFinal默认内嵌Jetty启动而Jetty对WebSocket的支持是JSR-356规范的要生效必须把WebSocket端点的class注册到Jetty的ServerContainer里。如果你只是写了一个ServerEndpoint注解的类但没有在启动时注册Jetty就不会认这个路径请求会被当成普通HTTP请求处理最后交还给JFinal的路由表。如果你用的是外部Tomcat则要检查Tomcat版本对应的JSR-356实现是否被WebSocket相关的Jar包支持。有的项目里出现了类冲突Tomcat自带的WebSocket实现和项目里引用的WebSocket API版本对不上导致ServerEndpoint注解类根本没被扫描到。检查方法很简单启动项目后不要急着连WebSocket先看启动日志里有没有“WebSocket”相关的初始化信息。如果没有说明端点压根没注册到容器。3. 三套能直接用的放行方案排查完层次接下来就是动手解决。我整理了三套方案按优先级从低到高排列。根据你的项目现状选一个最贴合的就行不需要全部用。3.1 方案A拦截器白名单把握手路径放过去这个方案最轻量适用于你的WebSocket路径确实走了某个Controller而且你不想大动干戈改结构的情况。思路是在全局拦截器里加一个判断如果访问路径是WebSocket路径直接放行。public class AuthInterceptor implements Interceptor { private static final SetString WS_PATHS new HashSet(Arrays.asList( /ws/deepseek, /ws/heartbeat )); Override public void intercept(Invocation inv) { String path inv.getController().getRequest().getRequestURI(); String contextPath inv.getController().getRequest().getContextPath(); if (contextPath ! null !contextPath.isEmpty()) { path path.substring(contextPath.length()); } // WebSocket握手请求直接放行不参与登录校验 if (WS_PATHS.contains(path)) { inv.invoke(); return; } // 正常业务请求执行登录校验 // ... 原有的鉴权逻辑 inv.invoke(); } }这里有几个细节要注意。握手请求的URL不会带Query参数所以通过getRequestURI()拿到的路径是干干净净的路径。但如果你的WebSocket连接串里带了token比如ws://abc.com/ws/deepseek?tokenxxx那么getRequestURI()拿到的还是/ws/deepseekQuery在getQueryString()里不影响这里的路径判断。还有一点握手成功后后续的WebSocket帧收发根本不经过JFinal的拦截器了所以在拦截器里做的事情只影响握手这一次请求。如果业务需求是“WebSocket连接必须鉴权”仅仅放行是不够的你还需要在OnOpen回调里自己校验token。具体怎么校验要看你的Session携带的参数。这个方案的局限性是如果请求压根没匹配到Controller再有白名单拦截器也没用。所以遇到404的情况直接上方案B或C。3.2 方案B把WebSocket端点注册到容器原生层如果你的项目是JFinal内嵌Jetty启动的最干净的做法是绕开JFinal的Controller体系直接用JSR-356标准注解写WebSocket端点然后在JFinal的启动配置里把端点注册进Jetty的ServerContainer。import javax.websocket.*; import javax.websocket.server.ServerContainer; import javax.websocket.server.ServerEndpoint; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; public class WsEndpointRegister { public static void register(ServletContextHandler contextHandler) { ServerContainer container (ServerContainer) contextHandler.getServletContext() .getAttribute(javax.websocket.server.ServerContainer); if (container null) { throw new IllegalStateException(WebSocket容器未初始化请检查Jetty版本); } // 把DeepSeek对话端点注册到容器层 container.addEndpoint(DeepSeekWsEndpoint.class); } }对应的WebSocket端点类ServerEndpoint(/ws/deepseek) public class DeepSeekWsEndpoint { OnOpen public void onOpen(Session session) { System.out.println(WebSocket连接打开 session.getId()); } OnMessage public void onMessage(String message, Session session) { // 收到前端消息解析后调用DeepSeek API处理 // 结果通过session.getBasicRemote().sendText()返回给前端 } OnClose public void onClose(Session session) { System.out.println(WebSocket连接关闭 session.getId()); } OnError public void onError(Session session, Throwable error) { error.printStackTrace(); } }缩写可能让初看的人以为我在写伪代码但核心逻辑是真的。注册这一步是关键这一步没做ServerEndpoint注解就白写了。这个方案的优点是完全绕开JFinal的Handler链和拦截器链路径直接由Jetty的WebSocket处理器接管。握手请求先到达容器层JFinal根本看不到。缺点是你需要在你项目的启动流程里找到合适的切入点。JFinal的启动方式不管是JFinal.start()还是自定义StartupListener你需要拿到当前运行上下文里的ServletContext才能获取到ServerContainer。我建议在JFinalConfig子类里覆盖onStart()方法。这个方法在应用启动后会被调用这时候可以拿到上下文信息来做WebSocket端点注册。具体拿ServletContext的方式要看你的启动方式如果用的是JFinal.start()启动内嵌Jetty可以在JFinal启动监听器里注册如果用外部Tomcat就在配置里加一个ServletContextListener或者在web.xml里注册。3.3 方案C用Handler在路由匹配前直接接管第三种方案其实是我最后采用的方案因为在老项目上改启动流程风险比加一个Handler要大。思路是写一个自定义Handler放在JFinal的Handler链最前面当检测到请求路径是WebSocket路径时直接放行给容器不让它进入JFinal的路由匹配流程。public class WebSocketPassthroughHandler extends Handler { Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { // 这里target是请求路径不包含contextPath if (target.startsWith(/ws/)) { // 如果是WebSocket请求直接放行交给下一个Handler // 但关键是下一个Handler得是容器的WebSocket处理器而不是JFinal路由 // 实现方式见下方说明 return; } next.handle(target, request, response, isHandled); } }光这么写还不够因为JFinal的Handler链末端就是路由分发。如果你返回了但没做任何事请求还是会走到末端然后404。这里真正的操作是在Handler里直接把isHandled[0]设为true并自己获取容器的WebSocket处理器来响应。不过在实际的JFinal内嵌Jetty场景里更靠谱的做法是给Jetty的ServletContextHandler添加一个WebSocket升级过滤器这个操作发生在JFinal的请求链处理之前。但如果你不方便改启动代码还有一个取巧的办法在Handler里把请求转发给Jetty的WebSocket路径处理。// 伪代码示意把WebSocket请求原样交给容器层的处理链 // 如果你用的Jetty容器实际实现是通过ServletContextHandler里的WebSocketUpgradeFilter完成的坦白说方案C在纯代码层面比较绕。如果你用同一套代码更简单的是直接在JFinalConfig里重写configInterceptor()来排除WebSocket路径或者在方案B里把端点注册到容器层。3.4 三个方案的取舍给你一个直观的选择依据场景推荐方案原因WebSocket请求能匹配到Controller只是被全局拦截器拦了方案A改动最小加个白名单判断就行WebSocket请求404网络层没问题方案B让请求直接走容器层WebSocket处理器老项目上有多个自定义Handler不敢随便动启动流程方案A 方案B结合先确认你的Handler有没有吃掉这个路径你用的是Jetty内嵌启动方案B最干净不会和JFinal的Handler链冲突你用的是外部Tomcat/WAR包部署方案B注册到Tomcat的ServerContainer说实话方案A只是权宜之计治标不治本。如果以后添加新的WebSocket路径你还得记得往白名单里加很容易漏。方案B才是框架意义上的正确姿势让WebSocket请求走容器层业务请求走JFinal层各归各。4. 借这个项目把DeepSeek流式对话完整打通绕过拦截只是第一步接下来真正要在WebSocket里接DeepSeek对话还有不少细节。这一节我直接把打通链路的过程写出来包括为什么选择浏览器WebSocket连后端、再由后端转发DeepSeek这种架构。4.1 整体链路浏览器WebSocket 后端转发DeepSeek在最终使用的架构里前端浏览器不是直连DeepSeek的API而是前端页面建立ws://localhost:8080/ws/deepseek连接。后端WebSocket端点收到前端发来的用户消息。后端拼装好请求参数调用DeepSeek的对话接口。DeepSeek返回流式结果SSE格式。后端把SSE流逐步解析再通过WebSocket的sendText()方法把每一片内容推给前端。前端收到后即时渲染到大模型对话界面。有人会问为什么不用SSE直连DeepSeek两个原因一是API密钥不能暴露在前端必须由后端持有二是很多业务系统里的会话状态、上下文管理、权限校验都在后端WebSocket天然适合双向实时通信方便前端随时发新指令打断或者追问。4.2 后端转发代码从SSE到WebSocket的核心逻辑DeepSeek的对话接口走的是流式输出也就是text/event-stream格式。后端负责把SSE流里的内容一口一口喂给WebSocket前端。简单示意如下OnMessage public void onMessage(String message, Session wsSession) { // 1. 从WebSocket收到的消息解析出用户输入 // 假设message是JSON格式: {content:你好} JsonObject requestBody Json.parse(message).asObject(); String userContent requestBody.get(content).asString(); // 2. 调用DeepSeek接口这里用Java 11的HttpClient演示 HttpClient client HttpClient.newHttpClient(); HttpRequest request HttpRequest.newBuilder() .uri(URI.create(https://api.deepseek.com/chat/completions)) .header(Authorization, Bearer getApiKey()) .header(Content-Type, application/json) .POST(BodyPublishers.ofString(buildDeepSeekRequestBody(userContent))) .build(); // 3. 异步发送HTTP请求SSE流式响应顺序处理 client.sendAsync(request, BodyHandlers.ofLines()) .thenAccept(response - { response.body().forEach(line - { // SSE格式data: {...} if (line.startsWith(data:)) { String data line.substring(5).trim(); try { // 解析content字段转发给WebSocket客户端 JsonObject dataObj Json.parse(data).asObject(); if (dataObj.has(choices)) { JsonArray choices dataObj.get(choices).asArray(); JsonObject delta choices.get(0).asObject().get(delta).asObject(); if (delta.has(content)) { String contentDelta delta.get(content).asString(); wsSession.getBasicRemote().sendText(contentDelta); } } } catch (Exception e) { // 流式数据结束或非JSON行忽略 } } }); }) .exceptionally(ex - { try { wsSession.getBasicRemote().sendText(调用失败 ex.getMessage()); } catch (Exception e) { // 连接已关闭忽略 } return null; }); }这个代码的逻辑不复杂但有几个细节需要特别留意。SSE的流式输出是按行来的每个事件块由多行数据组成行首是data:。有的服务端还会发注释行以冒号开头要跳过。有些行包含的是完整的回复内容有些是空行分隔符。JSON字段名要跟DeepSeek官方接口对齐别用错不然解析出来全是空字符串。我调试的过程中最常遇到的就是delta里没有content因为可能是角色切换消息也可能是结束标记。所以解析时一定要做字段是否存在判断别一上来就取。还有一个并发问题前端可能在DeepSeek还没返回完的时候又发来一条新消息。如果你的WebSocket端点同时处理多个客户端的消息你需要按会话隔离保存上下文不能把A用户的消息回给B用户。建议给每个WebSocket的Session对应的对话上下文建一个独立对象不要用全局变量。4.3 心跳和断线重连WebSocket连上后如果长时间没消息很多网络设备会把空闲连接断开。前端需要定时发心跳包后端要么原样回pong要么处理完业务后回个空包。我这里写一个简单的心跳实现OnMessage public void onMessage(String message, Session session) { // 约定客户端发ping代表心跳探测 if (ping.equals(message)) { try { session.getBasicRemote().sendText(pong); } catch (IOException e) { // 发送失败说明连接已断触发重连机制 } return; } // 其他消息按业务处理 handleChatMessage(message, session); }前端的心跳间隔一般设置在30秒到60秒之间。别太频繁不然白白消耗流量和资源。我用的是50秒后端空闲会话超时时间设成60秒这样心跳能保证连接在超时之前被刷新。注意服务端也要配置空闲超时比如session.setMaxIdleTimeout(60000)否则某些容器默认的空闲时间很长连接断了你都不知道。如果前端发现WebSocket连接异常关闭要做好重连策略。最简单的重连是固定时间内重试好一点的处理是指数退避比如第一次等1秒第二次等2秒依次翻倍最大间隔不超过1分钟。重连成功后前端如果有未发送完的消息要放进一个队列里重放。5. 常见问题排查实录写到最后把我在这个项目里遇到过的几个高频问题整理一下全是实操里试出来的经验希望能帮你少踩几个坑。5.1 问题一握手404协议升级请求被当成普通HTTP症状前端报Error during WebSocket handshake: Unexpected response code: 404。原因请求路径没有注册到WebSocket容器或者被JFinal路由表吃掉后找不到Controller方法。排查路径先在启动日志里搜WebSocket字样确认端点有没有注册。再到浏览器开发者工具的Network面板看这个请求的响应状态和响应头如果响应头里没有Upgrade: websocket说明不是容器返回的WebSocket升级响应。解决办法按方案B把ServerEndpoint类注册到ServerContainer或者检查web.xml、启动监听器看WebSocket相关初始化有没有执行。5.2 问题二握手成功但连接几十秒后自动断开症状WebSocket连接建立之后过了一段时间通常60秒左右自动断开控制台没有明显异常日志。原因心跳机制没配或者服务端空闲超时设置太短。某些云环境里的负载均衡器还有更短的连接空闲超时比如默认60秒你只能通过心跳去刷新。排查路径抓包看断开前后有没有收到TCP RST包如果有RST且没有明显的超时配置那很可能是中间网络设备把连接清了。解决办法前端定时心跳后端配置合适的maxIdleTimeout。同时确认你的WebSocket容器与反向代理的超时配置匹配。5.3 问题三全局拦截器返回302握手被重定向症状前端控制台报错Unexpected response code: 302或者页面收到一个登录跳转链接。原因WebSocket握手请求被你的全局拦截器当成了普通HTTP请求因为Session里没有用户信息触发了未登录重定向逻辑。排查路径在全局拦截器里打日志打印每个请求的getRequestURI()和请求头Upgrade字段你就知道哪些请求是被拦截器处理的。解决办法按方案A加白名单放行或者在拦截器里增加判断如果请求头里存在Upgrade: websocket直接放行不做鉴权因为鉴权应该放在OnOpen里做。5.4 问题四前端收到的消息乱序或半截症状DeepSeek的流式内容前端一会儿显示空白一会儿显示完整一段或者文字顺序错乱。原因SSE流解析时丢行或者WebSocket发送消息的线程没有同步控制。Java的sendText()如果被多个线程同时调某些容器实现会抛异常。HttpClient.sendAsync()的多线程回调可能并发往同一个Session写消息。排查路径打印每条发送消息的时间戳和内容看是不是并发交错了。解决办法给每个WebSocket会话维护一个发送锁或者把发送逻辑统一放进一个单线程的Executor里确保同一会话的消息按顺序发送。另外SSE解析时遇到[DONE]结束标记一定要正确处理别漏了最后一个空行。5.5 常用排查速查表症状可能原因首选排查工具握手404端点未注册/路由未匹配启动日志、Network面板握手302全局拦截器拦截拦截器内打日志握手403CORS或鉴权失败查看响应头握手超时端口/防火墙/代理问题抓包、telnet连接秒断空闲超时/心跳缺失服务端日志、抓包中文乱码编码不一致检查sendText和前端onmessage编码消息乱序多线程并发发送加锁或单线程发送器消息丢一半SSE解析错误打印原始SSE数据流最后分享一个小经验踩过几次WebSocket的坑之后我现在做JFinal项目里的大模型对话功能会把WebSocket端点注册和业务逻辑分开成两个模块一个只负责连接生命周期管理一个只负责把DeepSeek的流式返回转成前端能消费的数据格式。拦截器那边我习惯在全局拦截器里直接判断Upgrade: websocket请求头而不是维护一个路径白名单这样以后加多少个WebSocket端点都不用改拦截器代码。还有一点特别想提醒用DeepSeek这类工具生成的代码框架集成的部分一定要回读一遍JFinal官方文档再落地。AI给你的往往是很通用的标准Java写法但JFinal有自己的Handler链和拦截器生命周期这种框架特有的执行顺序问题必须结合项目自身结构来处理。你的WebSocket被拦截往往并不是代码写错而是它走的路径根本不在框架预期之内。如果你也遇到了类似的问题可以按照上面的排查顺序来先确认端点有没有注册到容器再看全局拦截器有没有做请求头判断最后检查路由表有没有把WebSocket路径绑定到Controller上。这三步做完绝大多数拦截问题都能解决。
RELATED

相关推荐

半透反LCD 2D仿真实战:TechWiz建模关键与排障

半透反LCD 2D仿真实战:TechWiz建模关键与排障

最近在帮朋友追一个户外可穿戴屏项目,客户开口就是“强光下要看得清”。常规方案里,半透反射式(Transflective)LCD是最合适的路线,这也是我常说的“ Display 既要背光透射又要环境光反射”的双模式面板。但问题在于&am…

📅 2026/10/9 7:32:31
用 CodeBuddy MCP 把静态网站发布到公网:TaoToken 统一 Key 接入 EdgeOne Pages 的最简路径

用 CodeBuddy MCP 把静态网站发布到公网:TaoToken 统一 Key 接入 EdgeOne Pages 的最简路径

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

📅 2026/10/9 7:32:31
Ubuntu下Redis安装配置与实战:从零搭建高性能缓存服务

Ubuntu下Redis安装配置与实战:从零搭建高性能缓存服务

1. 为什么新手都该从 Ubuntu Redis 开始先说结论:如果你刚刚开始接触 Linux 服务器,想学一个“装完就能立刻用、用了就能出效果、踩坑也容易搜到答案”的服务,Redis 绝对是最合适的练手对象之一。配合 Ubuntu 这个对新手最友好的发行版&…

📅 2026/10/9 7:32:31
MORE NEWS

更多资讯

📰

Linux Swap在线扩容全攻略:文件、LVM与分区实操

凌晨两点被监控告警叫起来,登录服务器一看,Swap 使用率 98%,内存也快见底了。这种时候最怕的就是手里没有一套稳妥的“在线扩容”方案——不能重启,不能影响正在跑的业务,最好能在几分钟内把 Swap 空间撑大。这活儿我干…

📰

Spring Boot + Android宠物领养管理系统开发实战:从需求到联调避坑

做这个宠物中心信息管理系统的动机其实有点私人——我课余时间在本地动物救助站做过志愿者,最头疼的不是没人领养,而是信息登记靠纸笔,领养人有没有按时反馈谁也说不清。所以就想着用Java Spring Boot做后端,Android做客户端&…

📰

C盘清理实战:从6.7GB到46GB的深度整理全记录

C盘又红了。相信每个用Windows的朋友都经历过那个瞬间——右下角弹窗提示磁盘空间不足,打开此电脑一看,C盘进度条一片猩红,只剩几个GB。系统开始卡顿,软件安装包下载到一半没空间,微信和QQ聊天记录同步不下来&#xff…

📰

网络安全应急演练全流程:从预案设计到复盘改进

简介:网络信息安全应急演练作为一套可直接套用的企业安全演练文档,面向网络管理员、信息安全负责人及行政管理人员,旨在解决安全预案停留在纸面、缺乏实操验证的问题。文档以公司为背景,明确演练目的——建立健全网络与信息安全运…

📰

后端性能优化:从TLS 1.2到TLS 1.3的握手降延迟实践

后端开发做到某个阶段,就会对“握手”这个词特别敏感。我之前帮团队优化一个公网接口,业务代码本身只需要十几毫秒,但从客户端发出请求到收到响应,一条普通HTTPS请求在TLS 1.2握手上就花掉了五次网络交互中的整整两大段RTT&#x…

📰

从暴力循环到数位DP:梦中的统计P1554数字计数优化实战

1. 这题到底在问什么:梦里的奶牛在数数《梦中的统计》(Dream Counting)是USACO 2006年12月赛季的一道银牌题,编号P1554。题目本身很短,核心诉求一句话就能说清:给定两个非负整数N和M(通常N ≤ M…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬