基于Netty构建高性能WebSocket服务器:从原理到实战部署
最近在开发一个需要实时消息推送的项目时遇到了一个头疼的问题如何高效、稳定地处理海量WebSocket连接传统的Spring WebSocket在管理连接、广播消息和集群扩展方面显得有些力不从心。经过一番调研和选型最终锁定了Netty这个高性能网络框架并决定用它来构建一个轻量级的WebSocket服务器。本文将手把手带你从零开始用Netty实现一个功能完整的WebSocket服务涵盖从环境搭建、协议处理到心跳检测、广播推送的全流程并提供可直接复用的核心代码和线上部署的避坑指南。无论你是想学习Netty网络编程还是需要为你的应用集成实时通信能力这篇文章都能给你一套完整的解决方案。1. 背景与核心概念为什么选择 Netty 实现 WebSocket在深入代码之前我们有必要搞清楚两个核心问题什么是WebSocket为什么用Netty来实现它WebSocket是一种在单个TCP连接上进行全双工通信的协议。它使得客户端和服务器之间的数据交换变得更加简单允许服务端主动向客户端推送数据。与传统的HTTP轮询相比WebSocket能显著减少不必要的网络开销和延迟非常适合聊天室、实时通知、在线协作等场景。那么实现WebSocket服务为什么是Netty而不是直接用Spring WebSocket或其他库呢极致性能Netty是一个异步事件驱动的网络应用框架其核心设计如Reactor线程模型、零拷贝、内存池使其在处理高并发连接和海量数据时性能远超基于Servlet容器的实现。灵活可控Netty提供了底层网络通信的完整控制权。你可以精细地管理连接的生命周期、自定义编解码器、优化内存使用这对于构建定制化程度高的中间件或网关至关重要。协议支持完善Netty内置了对WebSocket协议包括RFC6455版本的良好支持我们只需要关注业务逻辑无需从TCP字节流开始解析协议。易于扩展基于Netty的服务可以轻松地集成到任何Java应用中不依赖于特定的Web容器如Tomcat部署方式更加灵活。简单来说如果你追求极致的性能和可控性或者你的应用场景连接数巨大十万、百万级别Netty是实现WebSocket服务的不二之选。2. 环境准备与版本说明在开始编码前请确保你的开发环境已就绪。本文示例将使用最通用的配置。操作系统Windows / macOS / Linux 均可。Java 版本JDK 8 或更高版本。Netty 4.x 对 JDK 8 有良好支持。本文示例基于 JDK 11。构建工具Maven 或 Gradle。本文使用Maven进行依赖管理。IDEIntelliJ IDEA 或 Eclipse。推荐使用 IntelliJ IDEA其对Maven和Netty的支持更好。Netty 版本我们将使用 Netty 4.1.x 的最新稳定版。这是目前最广泛使用的版本API稳定社区资源丰富。项目初始化 创建一个标准的Maven项目。你的pom.xml文件需要引入 Netty 的核心依赖。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion groupIdcom.example/groupId artifactIdnetty-websocket-demo/artifactId version1.0-SNAPSHOT/version properties maven.compiler.source11/maven.compiler.source maven.compiler.target11/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding netty.version4.1.94.Final/netty.version !-- 使用当前稳定版本 -- /properties dependencies !-- Netty 核心依赖 -- dependency groupIdio.netty/groupId artifactIdnetty-all/artifactId version${netty.version}/version /dependency !-- 日志框架方便调试 -- dependency groupIdorg.slf4j/groupId artifactIdslf4j-simple/artifactId version1.7.36/version /dependency /dependencies /project3. 核心原理与 Netty 组件拆解Netty 的处理流程基于ChannelPipeline和ChannelHandler链。理解这个模型是编写Netty程序的关键。ServerBootstrap服务端启动引导类用于配置线程模型、通道类型和处理器链。EventLoopGroup可以理解为“线程池”负责处理I/O操作。通常服务端需要两个一个bossGroup接受连接一个workerGroup处理已建立连接的读写。Channel代表一个网络连接如Socket连接。ChannelPipeline一个包含一系列ChannelHandler的处理器链。数据如ByteBuf会像流水一样经过这个管道。ChannelHandler处理器用于处理入站Inbound和出站Outbound事件。我们编写的业务逻辑就在这里实现。编解码器Codec如HttpServerCodec,WebSocketServerProtocolHandler负责协议解析与封装。业务处理器如自定义的TextWebSocketFrameHandler处理具体的WebSocket消息。对于WebSocket服务器典型的Pipeline结构如下HttpServerCodec-HttpObjectAggregator-WebSocketServerProtocolHandler-自定义业务Handler4. 完整实战构建 WebSocket 服务器我们将创建一个简单的WebSocket回声服务器客户端发送一条文本消息服务器原样返回。在此基础上我们会增加连接管理、心跳检测和广播功能。4.1 项目结构规划创建以下包和类保持结构清晰src/main/java/com/example/websocket/ ├── server/ │ ├── WebSocketServer.java // 服务器启动类 │ └── handler/ │ ├── TextWebSocketFrameHandler.java // 核心业务处理器 │ └── HttpRequestHandler.java // HTTP请求处理器用于处理WebSocket握手请求 └── util/ └── ChannelGroupUtil.java // 连接管理工具类可选4.2 编写 HTTP 请求处理器WebSocket连接始于一个HTTP握手请求。我们需要一个处理器来处理这个初始的HTTP请求并正确响应以升级协议到WebSocket。// 文件路径src/main/java/com/example/websocket/server/handler/HttpRequestHandler.java package com.example.websocket.server.handler; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory; /** * 处理HTTP请求主要用于WebSocket握手 */ public class HttpRequestHandler extends SimpleChannelInboundHandlerFullHttpRequest { private final String websocketPath; public HttpRequestHandler(String websocketPath) { this.websocketPath websocketPath; } Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { // 处理WebSocket握手请求 if (isWebSocketHandshakeRequest(request)) { // 创建握手工厂指定WebSocket路径和子协议这里为空 WebSocketServerHandshakerFactory wsFactory new WebSocketServerHandshakerFactory( getWebSocketLocation(request), null, true); WebSocketServerHandshaker handshaker wsFactory.newHandshaker(request); if (handshaker null) { // 不支持的WebSocket版本 WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); } else { // 执行握手完成后会自动将Channel升级为WebSocket handshaker.handshake(ctx.channel(), request); // 握手成功后将此Handler从Pipeline中移除因为后续通信都是WebSocket帧了 ctx.pipeline().remove(this); } } else { // 如果不是WebSocket握手请求返回404本例只支持WebSocket sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND)); } } private boolean isWebSocketHandshakeRequest(FullHttpRequest request) { // 简单判断请求头包含 Upgrade: websocket 且方法是GET return request.headers().contains(Upgrade) websocket.equalsIgnoreCase(request.headers().get(Upgrade)) GET.equalsIgnoreCase(request.method().name()); } private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) { // 发送HTTP响应 ChannelFuture f ctx.channel().writeAndFlush(res); if (!isKeepAlive(req) || res.status().code() ! 200) { f.addListener(ChannelFutureListener.CLOSE); } } private static boolean isKeepAlive(FullHttpRequest req) { return keep-alive.equalsIgnoreCase(req.headers().get(Connection)); } private String getWebSocketLocation(FullHttpRequest req) { // 构建WebSocket的URL用于握手响应头 Sec-WebSocket-Location String location req.headers().get(Host) websocketPath; return ws:// location; } }4.3 编写核心 WebSocket 业务处理器这是处理WebSocket连接、消息和事件的核心。// 文件路径src/main/java/com/example/websocket/server/handler/TextWebSocketFrameHandler.java package com.example.websocket.server.handler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import io.netty.util.concurrent.GlobalEventExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 处理WebSocket文本帧 */ public class TextWebSocketFrameHandler extends SimpleChannelInboundHandlerWebSocketFrame { private static final Logger LOGGER LoggerFactory.getLogger(TextWebSocketFrameHandler.class); // 使用ChannelGroup管理所有活跃的WebSocket连接用于广播 private static final ChannelGroup channels new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { // 监听握手完成事件 if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) { LOGGER.info(WebSocket 握手成功客户端连接: {}, ctx.channel().remoteAddress()); // 握手成功后将当前Channel加入群组 channels.add(ctx.channel()); // 可以向新连接的客户端发送欢迎消息 ctx.channel().writeAndFlush(new TextWebSocketFrame(欢迎连接到WebSocket服务器)); } else { super.userEventTriggered(ctx, evt); } } Override protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { // 判断是否是文本帧我们只处理文本 if (frame instanceof TextWebSocketFrame) { String requestText ((TextWebSocketFrame) frame).text(); LOGGER.info(收到来自 {} 的消息: {}, ctx.channel().remoteAddress(), requestText); // 1. 回声功能原样返回 ctx.channel().writeAndFlush(new TextWebSocketFrame(回声: requestText)); // 2. 广播功能将消息发送给所有连接的客户端除了自己 // channels.writeAndFlush(new TextWebSocketFrame([广播] ctx.channel().remoteAddress() 说: requestText), channel - channel ! ctx.channel()); } else { // 如果不是文本帧抛出异常协议错误 throw new UnsupportedOperationException(不支持的帧类型: frame.getClass().getName()); } } Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { // 连接断开时从群组中移除 channels.remove(ctx.channel()); LOGGER.info(客户端断开连接: {}, ctx.channel().remoteAddress()); super.channelInactive(ctx); } Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { LOGGER.error(WebSocket处理发生异常, cause); ctx.close(); } // 提供一个静态方法用于从外部发送广播消息例如从业务逻辑层触发 public static void broadcastMessage(String message) { channels.writeAndFlush(new TextWebSocketFrame([系统广播] message)); } }4.4 编写服务器启动类现在我们将所有组件组装起来启动服务器。// 文件路径src/main/java/com/example/websocket/server/WebSocketServer.java package com.example.websocket.server; import com.example.websocket.server.handler.HttpRequestHandler; import com.example.websocket.server.handler.TextWebSocketFrameHandler; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.stream.ChunkedWriteHandler; /** * WebSocket 服务器主类 */ public class WebSocketServer { private final int port; private final String websocketPath; public WebSocketServer(int port, String websocketPath) { this.port port; this.websocketPath websocketPath; } public void run() throws Exception { // 1. 创建两个线程组 // bossGroup 用于接受客户端连接 EventLoopGroup bossGroup new NioEventLoopGroup(1); // workerGroup 用于处理已接受连接的I/O操作 EventLoopGroup workerGroup new NioEventLoopGroup(); try { // 2. 创建服务器启动引导类 ServerBootstrap b new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // 使用NIO传输通道 .handler(new LoggingHandler(LogLevel.INFO)) // 给bossGroup添加日志处理器 .childHandler(new ChannelInitializerSocketChannel() { // 给每个新连接设置Pipeline Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( new HttpServerCodec(), // HTTP编解码器 new ChunkedWriteHandler(), // 支持大文件或流式传输 new HttpObjectAggregator(65536), // 将HTTP消息的多个部分聚合为一个完整的FullHttpRequest/Response new HttpRequestHandler(websocketPath), // 自定义HTTP处理器处理握手 new WebSocketServerProtocolHandler(websocketPath, null, true), // WebSocket协议处理器处理握手、ping/pong等 new TextWebSocketFrameHandler() // 自定义业务处理器处理WebSocket帧 ); } }) .option(ChannelOption.SO_BACKLOG, 128) // 服务端接受连接的队列大小 .childOption(ChannelOption.SO_KEEPALIVE, true); // 保持长连接 // 3. 绑定端口开始接收连接 ChannelFuture f b.bind(port).sync(); System.out.println(WebSocket 服务器启动成功监听端口: port , WebSocket路径: websocketPath); System.out.println(你可以使用在线WebSocket测试工具连接: ws://localhost: port websocketPath); // 4. 等待服务器通道关闭阻塞 f.channel().closeFuture().sync(); } finally { // 5. 优雅关闭线程组 workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception { int port 8080; String path /ws; if (args.length 0) { port Integer.parseInt(args[0]); } new WebSocketServer(port, path).run(); } }4.5 运行与测试启动服务器运行WebSocketServer类的main方法。控制台应输出启动成功信息。使用测试工具连接浏览器控制台打开浏览器开发者工具在Console中输入const ws new WebSocket(ws://localhost:8080/ws); ws.onopen () console.log(连接成功); ws.onmessage (event) console.log(收到消息:, event.data); ws.send(Hello Netty!);在线测试工具访问如http://www.websocket.org/echo.html等网站将服务器地址设置为ws://localhost:8080/ws进行连接和测试。观察日志在服务器控制台你应该能看到连接建立、收到消息和发送回声的日志。5. 进阶功能与优化一个生产级的WebSocket服务器还需要更多功能。5.1 实现心跳检测 (Heartbeat)长时间空闲的连接可能因为防火墙、代理等原因被断开。心跳机制Ping/Pong可以保持连接活跃并检测死连接。Netty的WebSocketServerProtocolHandler已经自动处理了标准的Ping/Pong帧。我们只需要确保在业务处理器中不错误地处理它们。TextWebSocketFrameHandler中的channelRead0方法已经过滤了非文本帧。为了更主动地检测我们可以添加IdleStateHandler。修改WebSocketServer.java中的initChannel方法Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( new IdleStateHandler(60, 0, 0), // 读超时60秒写和全部超时为0不检测 new HttpServerCodec(), new ChunkedWriteHandler(), new HttpObjectAggregator(65536), new HttpRequestHandler(websocketPath), new WebSocketServerProtocolHandler(websocketPath, null, true), new TextWebSocketFrameHandler() ); }然后在TextWebSocketFrameHandler中重写userEventTriggered方法处理读空闲事件Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) { // ... 握手成功处理逻辑 } else if (evt instanceof IdleStateEvent) { IdleStateEvent e (IdleStateEvent) evt; if (e.state() IdleState.READER_IDLE) { LOGGER.warn(连接 {} 读空闲超时即将关闭, ctx.channel().remoteAddress()); ctx.close(); // 关闭空闲连接 } } else { super.userEventTriggered(ctx, evt); } }5.2 连接管理与会话绑定在实际业务中我们通常需要将Channel与具体的用户会话如User ID绑定。创建属性映射可以使用Netty的Channel的attr方法。public static final AttributeKeyString USER_ID AttributeKey.valueOf(userId); // 在用户认证后设置 ctx.channel().attr(USER_ID).set(user_123); // 在需要的地方获取 String userId ctx.channel().attr(USER_ID).get();使用 ConcurrentHashMap 管理维护一个MapString, Channel来根据用户ID查找Channel实现私信功能。public class ChannelManager { private static final ConcurrentHashMapString, Channel userChannelMap new ConcurrentHashMap(); public static void bind(String userId, Channel channel) { userChannelMap.put(userId, channel); channel.attr(USER_ID).set(userId); } public static Channel getChannel(String userId) { return userChannelMap.get(userId); } public static void unbind(Channel channel) { String userId channel.attr(USER_ID).get(); if (userId ! null) { userChannelMap.remove(userId); } } }记得在channelInactive和exceptionCaught方法中调用ChannelManager.unbind(ctx.channel())。5.3 消息编解码与协议设计对于复杂业务直接传输文本可能不够。可以定义自己的应用层协议并使用自定义编解码器。定义消息体例如JSON{type: chat, sender: user1, content: 你好, timestamp: 1640995200000}创建编解码器继承MessageToMessageCodec将TextWebSocketFrame与你的业务对象互相转换。public class WebSocketMessageCodec extends MessageToMessageCodecTextWebSocketFrame, ChatMessage { private final ObjectMapper mapper new ObjectMapper(); Override protected void encode(ChannelHandlerContext ctx, ChatMessage msg, ListObject out) throws Exception { String json mapper.writeValueAsString(msg); out.add(new TextWebSocketFrame(json)); } Override protected void decode(ChannelHandlerContext ctx, TextWebSocketFrame frame, ListObject out) throws Exception { String json frame.text(); ChatMessage msg mapper.readValue(json, ChatMessage.class); out.add(msg); } }将WebSocketMessageCodec添加到Pipeline中替换掉直接处理TextWebSocketFrame的Handler。6. 常见问题与排查思路在开发和部署Netty WebSocket服务时你可能会遇到以下问题问题现象可能原因排查步骤与解决方案连接失败握手错误1. 服务器未启动或端口被占用。2. WebSocket路径(/ws)不匹配。3. 防火墙/安全组策略阻止。1. 检查服务器日志确认端口监听成功 (netstat -an | grep 8080)。2. 确保客户端连接的URL路径与服务器配置的websocketPath完全一致。3. 检查服务器和客户端的防火墙设置。连接建立后立即断开1. 心跳超时如果配置了IdleStateHandler。2. 业务处理器抛出未捕获的异常。3. 客户端或服务器主动关闭。1. 调整IdleStateHandler的超时时间或检查网络是否稳定。2. 查看服务器日志中的exceptionCaught异常堆栈。3. 在channelInactive方法中打印日志分析断开原因。收不到服务器消息1. 客户端onmessage事件监听未正确设置。2. 服务器端消息未成功writeAndFlush。3. 消息被Pipeline中的其他Handler拦截或丢弃。1. 使用简单的测试工具如echo网站排除客户端问题。2. 在服务器Handler中在writeAndFlush后添加监听器addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE)检查发送是否成功。3. 使用LoggingHandler添加到Pipeline最前面查看原始数据流。内存泄漏或CPU过高1. 未正确释放ByteBuf等资源。2. ChannelGroup或Map未及时清理失效连接。3. 业务逻辑存在死循环或阻塞操作。1. 确保在Handler中继承SimpleChannelInboundHandler它会自动释放消息。2. 定期检查ChannelGroup或自定义Map移除!channel.isActive()的连接。3. 避免在Netty的I/O线程中执行耗时操作应提交到业务线程池。性能随连接数增长下降1.workerGroup线程数配置不合理。2. 存在同步阻塞调用。3. JVM内存或GC问题。1. 默认NioEventLoopGroup不指定参数线程数为CPU核心数 * 2。对于大量连接可适当增加但并非越多越好。2. 使用channel.eventLoop().execute()或业务线程池执行阻塞任务。3. 监控JVM使用Netty提供的ResourceLeakDetector检测内存泄漏。7. 生产环境最佳实践与工程建议将Demo部署到生产环境还需要考虑更多方面配置化将端口、路径、线程数、超时时间等参数提取到配置文件如application.yml中避免硬编码。优雅启停实现ShutdownHook在JVM关闭时先优雅关闭EventLoopGroup等待处理中的任务完成。Runtime.getRuntime().addShutdownHook(new Thread(() - { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); try { bossGroup.awaitTermination(10, TimeUnit.SECONDS); workerGroup.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }));监控与日志集成Micrometer等监控组件暴露连接数、消息速率等指标。使用SLF4JLogback合理设置日志级别避免Netty DEBUG日志刷屏。安全WSS在生产环境务必使用WebSocketSecure (wss://)。Netty提供了SslHandler。认证在HTTP握手阶段进行Token或Session认证认证失败则拒绝升级连接。限流防止单个客户端恶意发送大量消息可使用令牌桶等算法在Pipeline中限流。集群与扩展单机Netty服务有连接数上限。需要横向扩展时可以借助Redis的Pub/Sub或Kafka等消息中间件在不同服务器实例间广播消息。需要维护一个全局的用户-服务器实例映射关系用于定向推送。资源管理为不同的业务类型设置独立的EventLoopGroup避免相互影响。谨慎使用ChannelGroup的writeAndFlush广播对于万级连接遍历发送可能成为瓶颈考虑分批或异步。从简单的回声服务器到支持心跳、会话管理和私有协议的生产级服务我们一步步实现了基于Netty的WebSocket核心功能。关键在于理解Netty的Pipeline处理模型并在此基础上构建清晰的业务逻辑。在真正上线前务必进行充分的压力测试可使用wrk或JMeter的WebSocket插件并根据监控数据持续调优。Netty的强大性能足以支撑绝大多数实时应用场景剩下的就是根据你的具体业务需求填充更多的细节和功能了。

相关新闻

SpringBoot+Vue小区物业管理系统开发实战

SpringBoot+Vue小区物业管理系统开发实战

1. 项目概述 这个基于SpringBootVue的小区物业管理系统是一个典型的前后端分离架构项目,非常适合作为计算机相关专业的毕业设计、课程设计或自学练手项目。系统采用JavaMySQL技术栈,涵盖了物业管理中的核心业务流程,包括业主管理、收费管理、…

2026/8/4 9:34:36 阅读更多 →
Unity Shader Graph线性混合蒙皮节点:原理、应用与实战指南

Unity Shader Graph线性混合蒙皮节点:原理、应用与实战指南

1. 项目概述:从“皮”到“骨”的动画魔法 在实时渲染和游戏开发的世界里,让一个静态的3D模型“活”起来,流畅地做出奔跑、跳跃、攻击等动作,是每个开发者都要面对的核心挑战。这背后的关键技术之一,就是蒙皮&#xff0…

2026/8/4 9:33:36 阅读更多 →
07-Embedding是什么-通俗理解向量检索

07-Embedding是什么-通俗理解向量检索

Embedding(嵌入模型)是什么?用通俗语言理解向量检索系列:从零构建企业 RAG 知识库(第 7 篇)1. Embedding 是一种可比较的表示 Embedding 把文本映射成固定维度数值向量: "如何申请退款&quo…

2026/8/4 9:33:36 阅读更多 →

最新新闻

抖音内容批量下载实战指南:douyin-downloader工具深度解析

抖音内容批量下载实战指南:douyin-downloader工具深度解析

抖音内容批量下载实战指南:douyin-downloader工具深度解析 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback s…

2026/8/4 10:26:57 阅读更多 →
音频波形分析实战:从“粗粮”到“细糠”的质量评估指南

音频波形分析实战:从“粗粮”到“细糠”的质量评估指南

这次我们来看一个关于音频波形分析的有趣话题。当你在处理音频、调试音效,或者只是好奇一段声音的质量时,常常会听到“细糠”和“粗粮”这样的比喻。简单来说,这通常用来形容音频波形的视觉形态:波形密集、细节丰富、动态范围控制…

2026/8/4 10:26:57 阅读更多 →
《Java 100 天进阶之路》第69篇:JSP与EL表达式(2026版)

《Java 100 天进阶之路》第69篇:JSP与EL表达式(2026版)

第69篇:JSP与EL表达式(2026版) 📌 系列导航:《Java 100 天进阶之路》完整目录 | ⬅️ 上一篇:第68篇:JavaWeb核心技术之Servlet | ➡️ 下一篇:第70篇:Cookie与Session&a…

2026/8/4 10:26:57 阅读更多 →
智能CLI工具Grok Build:用自然语言自动化日常任务的技术实践

智能CLI工具Grok Build:用自然语言自动化日常任务的技术实践

这次我们来看一个名为Grok Build的项目。从名称和网络热度来看,它似乎是一个与命令行界面(CLI)紧密相关,并旨在处理电脑日常任务的工具。结合“Grok”一词在技术领域常有的“深入理解”之意,以及“Build”所代表的构建…

2026/8/4 10:26:57 阅读更多 →
【AI自媒体变现黄金法则】:20年实战总结的7个零门槛赚钱路径,第5个90%的人还没试过

【AI自媒体变现黄金法则】:20年实战总结的7个零门槛赚钱路径,第5个90%的人还没试过

更多请点击: https://codechina.net 第一章:AI做自媒体赚钱 人工智能正深刻重塑内容创作与流量变现的底层逻辑。借助大模型、自动化工具和智能分发平台,普通人无需专业剪辑或写作功底,也能规模化生产高传播性内容,并通…

2026/8/4 10:26:57 阅读更多 →
无标题技术项目的系统化开发与管理方法论

无标题技术项目的系统化开发与管理方法论

1. 项目概述作为一名从业多年的技术博主,我经常遇到一个有趣的现象:许多最有价值的项目往往最初连标题都没有。这种"无标题"状态反而可能蕴含着最纯粹的创意和技术探索。今天我想分享的就是关于如何处理这类"无标题"项目的系统方法论…

2026/8/4 10:25:57 阅读更多 →

日新闻

AI Agent白手起家26: 使用标准事件驱动大模型实践

AI Agent白手起家26: 使用标准事件驱动大模型实践

纲要 练习目标:掌握大模型标准事件的调用回顾 LangChain 中的核心标准事件 invokestreambatchastream_eventswith_structured_output 环境准备实战代码:多种事件调用对比 同步调用与流式输出批量处理异步事件流监听结构化输出 运行说明与预期结果总结与扩…

2026/8/4 0:00:40 阅读更多 →
dealsea是什么?跨境卖家必知的美国deal站入门指南

dealsea是什么?跨境卖家必知的美国deal站入门指南

说实话,第一次听说美国这个老牌折扣网站的跨境卖家,十个有八个会问同一个问题:这个平台到底是干嘛的?我见过一个做家居出口的朋友,他在亚马逊上月销二十万美金,却从来没用过它。我给他看了首页——一屏一屏…

2026/8/4 0:01:40 阅读更多 →
清华大学重磅EST:植物自导电闪蒸焦耳热600°C/2600°C两步法!稀土超积累植物秒级转化为CeO₂-石墨烯电催化剂!

清华大学重磅EST:植物自导电闪蒸焦耳热600°C/2600°C两步法!稀土超积累植物秒级转化为CeO₂-石墨烯电催化剂!

通讯作者:邓兵、刘建国通讯单位:清华大学DOI:https://doi.org/10.1021/acs.est.6c00603研究背景稀土元素(REEs)是清洁能源技术与电子器件不可或缺的核心原料,然而传统提取方式依赖能耗高、排放大的采矿与强…

2026/8/4 0:01:40 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/3 4:58:13 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/3 1:53:31 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/4 5:26:40 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/3 13:07:03 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/3 5:19:38 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/3 8:27:36 阅读更多 →