文章目录

  • 一、Netty服务器端启动细节分析
    • 1.1 实现一个简单的http服务器
    • 1.2 服务器端启动细节分析
    • 1.3 创建与初始化 NioServerSocketChannel
      • 1.3.1 **通过反射工厂创建 Channel**:
      • 1.3.2 **初始化 Channel**
    • 1.4 注册到 Boss EventLoopGroup
      • 1.4.1 **异步提交注册任务**
      • 1.4.2 **EventLoop 执行注册**
      • 1.4.3 触发 Handler 添加事件
      • 1.4.4 触发 Channel 注册成功事件
    • 1.5 绑定端口与激活
      • 1.5.1 执行绑定操作
      • 1.5.2 标记 Channel 为激活状态
      • 1.5.3 触发 Channel 激活事件
      • 1.5.4 自动注册ACCEPT事件
    • 1.6 接收连接 - ServerBootstrapAcceptor 的工作
    • 1.7 总结与精要分析


推荐阅读:

【01】Netty从0到1系列之I/O模型
【02】Netty从0到1系列之NIO
【03】Netty从0到1系列之Selector
【04】Netty从0到1系列之Channel
【05】Netty从0到1系列之Buffer(上)
【06】Netty从0到1系列之Buffer(下)
【07】Netty从0到1系列之零拷贝技术
【08】Netty从0到1系列之整体架构、入门程序
【09】Netty从0到1系列之EventLoop
【10】Netty从0到1系列之EventLoopGroup
【11】Netty从0到1系列之Future
【12】Netty从0到1系列之Promise
【13】Netty从0到1系列之Netty Channel
【14】Netty从0到1系列之ChannelFuture
【15】Netty从0到1系列之CloseFuture
【16】Netty从0到1系列之Netty Handler
【17】Netty从0到1系列之Netty Pipeline【上】
【18】Netty从0到1系列之Netty Pipeline【下】
【19】Netty从0到1系列之Netty ByteBuf【上】
【20】Netty从0到1系列之Netty ByteBuf【中】
【21】Netty从0到1系列之Netty ByteBuf【下】
【22】Netty从0到1系列之Netty 逻辑架构【上】
【23】Netty从0到1系列之Netty 逻辑架构【下】


一、Netty服务器端启动细节分析

1.1 实现一个简单的http服务器

目标

  • 完成http服务器,请求-响应的过程.

作为开发者来说,只要照葫芦画瓢即可轻松上手。大多数场景下,你只需要实现与业务逻辑相关的一系列 ChannelHandler,再加上 Netty 已经预置了 HTTP 相关的编解码器就可以快速完成服务端框架的搭建。所以,我们只需要两个类就可以完成一个最简单的 HTTP 服务器,它们分别为服务器启动类和业务逻辑处理类,结合完整的代码实现我将对它们分别进行讲解。

  1. 服务器端代码
package cn.tcmeta.httpserver;import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;/*** @author: laoren* @description: 实现一个简单的http服务器* @version: 1.0.0*/
public class MyHttpSimpleServer {public void doStart(int port) throws InterruptedException {NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);NioEventLoopGroup workerGroup = new NioEventLoopGroup(2);try {ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();// 编码、解码处理器pipeline.addLast("codec", new HttpServerCodec());// 压缩处理器pipeline.addLast("compressor", new HttpContentCompressor());// 合并处理器pipeline.addLast("aggregator", new HttpObjectAggregator(1024 * 1024));// 自定义处理器pipeline.addLast("handler", new MyHttpServerHandler());}}).childOption(ChannelOption.SO_KEEPALIVE, true);ChannelFuture future = bootstrap.bind(port).sync();System.out.println("✅ MyHttpSimpleServer 启动,端口: " + port);future.channel().closeFuture().sync();}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}public static void main(String[] args) {try {new MyHttpSimpleServer().doStart(8080);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
  1. 自定义处理器代码
package cn.tcmeta.httpserver;import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;/*** @author: laoren* @description: 自定义处理类* @version: 1.0.0*/
@Slf4j
public class MyHttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {String content =String.format("Receive http request, uri: %s, method: %s, content: %s%n", request.uri(), request.method(), request.content().toString(CharsetUtil.UTF_8));log.info("uri: {}, request method: {}, content: {}", request.uri(), request.method(), request.content());// 处理响应事件FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.OK,Unpooled.wrappedBuffer(content.getBytes()));ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);}
}
  1. 测试示例
  • 客户端测试
    在这里插入图片描述

  • 服务器端日志

MyHttpSimpleServer 启动,端口: 8080
15:47:34 [INFO ] [nioEventLoopGroup-3-1] c.t.h.MyHttpServerHandler - uri: /bad-request, request method: GET, content: UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 0, cap: 0)Process finished with exit code 130

1.2 服务器端启动细节分析

  • 阶段一: 创建与初始化 NioServerSocketChannel
  • 阶段二: 注册到 Boss EventLoopGroup
  • 阶段三: 绑定端口与激活
  • 阶段四: 接收连接 - ServerBootstrapAcceptor 的工作
Main ThreadServerBootstrapChannelFactoryNioServerSocketChannelBossGroup (EventLoop)ChannelPipelineHeadContextTailContextServerBootstrapAcceptorJava NIObind(port)newChannel()create & initialize1. 新建 JDK ServerSocketChannel2. 配置非阻塞3. 创建 id, unsafe, pipeline4. pipeline.addHead(Tail)5. pipeline.addTail(Head)init()addLast(ServerBootstrapAcceptor)Pipeline: [Head]... ↔ [SBA] ↔ [Tail]register(channel)等待注册完成 (异步)doRegister()javaChannel().register(selector, 0, this)将JDK Channel注册到BossGroup的Selector,关注0(无兴趣事件),附件为NioServerSocketChannelinvokeHandlerAddedIfNeeded()handlerAdded()fireChannelRegistered()fireChannelRegistered() (从头开始传播)doBind()javaChannel().bind()setActive(true)fireChannelActive()fireChannelActive()readIfIsAutoRead()selectionKey().interestOps(OP_ACCEPT)最终在Selector上注册OP_ACCEPT事件,开始接收新连接Main ThreadServerBootstrapChannelFactoryNioServerSocketChannelBossGroup (EventLoop)ChannelPipelineHeadContextTailContextServerBootstrapAcceptorJava NIO

1.3 创建与初始化 NioServerSocketChannel

当调用 ServerBootstrap.bind(port) 时,Netty 并不会立即进行网络调用。第一步是创建服务端的“大门”。

1.3.1 通过反射工厂创建 Channel

  • ServerBootstrap 通过其内部的 ChannelFactory(通常是 ReflectiveChannelFactory)来创建一个新的 NioServerSocketChannel 实例。

在这里插入图片描述

在这里插入图片描述

相关核心代码:

  • io/netty/bootstrap/AbstractBootstrap.java
 final ChannelFuture initAndRegister() {Channel channel = null;try {// 1. 通过反射创建NioServerSocketChannel对象channel = channelFactory.newChannel();init(channel);} catch (Throwable t) {}
  • io/netty/channel/ReflectiveChannelFactory.java
@Override
public T newChannel() {try {return constructor.newInstance();} catch (Throwable t) {throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);}
}
  1. 底层结节实现

NioServerSocketChannel 的构造函数中,Netty 会做几件关键事:

  • SocketChannel.open(): 打开一个新的 Java NIO ServerSocketChannel
  • .configureBlocking(false)立即将其设置为非阻塞模式。这是异步编程的基础。
  • 创建该 Channel 的核心组件:ChannelId(唯一标识)、Unsafe(负责底层读写操作)、以及最重要的 DefaultChannelPipeline

核心代码:

protected AbstractChannel(Channel parent) {this.parent = parent;id = newId();unsafe = newUnsafe();pipeline = newChannelPipeline();
}
rotected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {super(parent);this.ch = ch;this.readInterestOp = readInterestOp;try {ch.configureBlocking(false); // 设置为非阻塞模式} catch (IOException e) {try {ch.close();} catch (IOException e2) {logger.warn("Failed to close a partially initialized socket.", e2);}throw new ChannelException("Failed to enter non-blocking mode.", e);}
}

在这里插入图片描述

1.3.2 初始化 Channel

  • 创建完成后,ServerBootstrap 会调用 init(Channel channel) 方法来初始化这个新 Channel。
  • 设置 ChannelOptions 和 Attributes: 将 ServerBootstrap.option()ServerBootstrap.attr() 中设置的参数应用到该 Channel 的配置中。
  • 设置 Pipeline 的“大门保安”
    • 这是最关键的一步。Netty 会通过 pipeline.addLast() 方法将一个特殊的 ChannelHandler——ServerBootstrapAcceptor——添加到 Channel 的 Pipeline 中。
    • ServerBootstrapAcceptor 的职责: 它本身是一个 ChannelInboundHandler。它的唯一使命就是在“有连接到来”这个入站事件被触发时,将新接入的客户端连接(SocketChannel)正式接手过来。我们会在阶段三详细讲解它。
  • 核心代码
  final ChannelFuture initAndRegister() {Channel channel = null;try {channel = channelFactory.newChannel();init(channel); // 初始化Channel} catch (Throwable t) {}
@Override
void init(Channel channel) {// 设置ChannelOptions setChannelOptions(channel, newOptionsArray(), logger);// 设置AttributessetAttributes(channel, newAttributesArray());// 获取绑定的PipelineChannelPipeline p = channel.pipeline();final EventLoopGroup currentChildGroup = childGroup;final ChannelHandler currentChildHandler = childHandler;final Entry<ChannelOption<?>, Object>[] currentChildOptions = newOptionsArray(childOptions);final Entry<AttributeKey<?>, Object>[] currentChildAttrs = newAttributesArray(childAttrs);final Collection<ChannelInitializerExtension> extensions = getInitializerExtensions();p.addLast(new ChannelInitializer<Channel>() {@Overridepublic void initChannel(final Channel ch) {final ChannelPipeline pipeline = ch.pipeline();ChannelHandler handler = config.handler();if (handler != null) {pipeline.addLast(handler);}ch.eventLoop().execute(new Runnable() {@Overridepublic void run() {// 将ServerBootstrapAcceptor添加到Pipeline中pipeline.addLast(new ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs,extensions));}});}});if (!extensions.isEmpty() && channel instanceof ServerChannel) {ServerChannel serverChannel = (ServerChannel) channel;for (ChannelInitializerExtension extension : extensions) {try {extension.postInitializeServerListenerChannel(serverChannel);} catch (Exception e) {logger.warn("Exception thrown from postInitializeServerListenerChannel", e);}}}
}

在这里插入图片描述

至此,一个功能完备、配置齐全的 NioServerSocketChannel 就准备好了,但它还没有被赋予“生命”(即注册到 EventLoop)。

1.4 注册到 Boss EventLoopGroup

这是 Netty 异步设计的核心体现。注册操作本身是异步的

1.4.1 异步提交注册任务

  • ServerBootstrapAbstractUnsafe.register0() 操作封装成一个任务,提交给 BossGroup 中的一个 EventLoop
  • 主线程(调用 bind() 的线程)在此刻立即返回一个 ChannelFuture,它可以用来监听注册(和后续绑定)是否成功。主线程不会被阻塞

在这里插入图片描述

注册不会阻塞主线程,直接返回一个ChannelFuture

 ChannelFuture regFuture = config().group().register(channel);if (regFuture.cause() != null) {if (channel.isRegistered()) {channel.close();} else {channel.unsafe().closeForcibly();}}

在这里插入图片描述

  • bossGroup中的NioEventLoop
    在这里插入图片描述

  • AbstractUnsafe.register0()注册的EventLoop

在这里插入图片描述

1.4.2 EventLoop 执行注册

  • 被选中的 EventLoop 线程会在其运行周期内的某个时间点执行这个注册任务。
  • 真正的注册: 在 doRegister() 方法中,会调用 Java NIO 的 ServerSocketChannel.register(eventLoop.selector, 0, this)
    • 关键点 1: 将 JDK 的 ServerSocketChannel 注册到 EventLoop 持有的 Selector 上。
    • 关键点 2兴趣操作为 0,表示暂时不监听任何事件。这是因为此时 Channel 还未激活(未绑定端口),监听 OP_ACCEPT 没有意义。
    • 关键点 3附件(Attachment) 设置为 Netty 自己的 NioServerSocketChannel 对象。这样,当 Selector 返回事件时,Netty 可以直接从 SelectionKey 中取出附件,就知道是哪个 Netty Channel 发生了事件。
  1. io/netty/channel/AbstractChannel#register0
private void register0(ChannelPromise promise) {try {// check if the channel is still open as it could be closed in the mean time when the register// call was outside of the eventLoopif (!promise.setUncancellable() || !ensureOpen(promise)) {return;}boolean firstRegistration = neverRegistered;doRegister(); // ✅✅✅✅✅ 核心注册方法neverRegistered = false;registered = true;// Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the// user may already fire events through the pipeline in the ChannelFutureListener.pipeline.invokeHandlerAddedIfNeeded();safeSetSuccess(promise);pipeline.fireChannelRegistered();// Only fire a channelActive if the channel has never been registered. This prevents firing// multiple channel actives if the channel is deregistered and re-registered.if (isActive()) {if (firstRegistration) {pipeline.fireChannelActive();} else if (config().isAutoRead()) {// This channel was registered before and autoRead() is set. This means we need to begin read// again so that we process inbound data.//// See https://github.com/netty/netty/issues/4805beginRead();}}} catch (Throwable t) {// Close the channel directly to avoid FD leak.closeForcibly();closeFuture.setClosed();safeSetFailure(promise, t);}
}
  • io/netty/channel/nio/AbstractNioChannel#doRegister()
@Overrideprotected void doRegister() throws Exception {boolean selected = false;for (;;) {try {// 参数1: Selector// 参数2: 0: 表示暂时不监听任何事件。这是因为此时 Channel 还未激活(未绑定端口),监听 OP_ACCEPT 没有意义。// 参数3: this:  附件(Attachment) 设置为 Netty 自己的 NioServerSocketChannel 对象。这样,当 Selector 返回事件时,Netty 可以直接从 SelectionKey 中取出附件,就知道是哪个 Netty Channel 发生了事件selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);return;} catch (CancelledKeyException e) {if (!selected) {// Force the Selector to select now as the "canceled" SelectionKey may still be// cached and not removed because no Select.select(..) operation was called yet.eventLoop().selectNow();selected = true;} else {// We forced a select operation on the selector before but the SelectionKey is still cached// for whatever reason. JDK bug ?throw e;}}}}

在这里插入图片描述

  • register签名
// 参数1: Selector
// 参数2: 监听的事件
// 参数3: 附加数据
public abstract SelectionKey register(Selector sel, int ops, Object att)throws ClosedChannelException;

在这里插入图片描述

1.4.3 触发 Handler 添加事件

  • 注册成功后,EventLoop 线程会回调 ChannelHandler.handlerAdded(ctx) 方法。对于 ServerBootstrapAcceptor 来说,此时它正式“上岗”。

1.4.4 触发 Channel 注册成功事件

  • 最后,EventLoop 线程会通过 PipelineHead 开始向后传播 channelRegistered 事件。此时,所有用户添加到服务端 Channel 的 ChannelHandler 都会感知到注册成功。

1.5 绑定端口与激活

注册成功后,才开始真正的绑定。

1.5.1 执行绑定操作

  • 同样在 EventLoop 线程中,调用 doBind() 方法。
  • 其内部最终会调用 JDK 的 ServerSocketChannel.bind(socketAddress, backlog) 方法,真正地将套接字绑定到指定的端口
  1. 注册成功之后,进行绑定操作,调用doBind
// Registration future is almost always fulfilled already, but just in case it's not.
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {Throwable cause = future.cause();if (cause != null) {// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an// IllegalStateException once we try to access the EventLoop of the Channel.promise.setFailure(cause);} else {// Registration was successful, so set the correct executor to use.// See https://github.com/netty/netty/issues/2586promise.registered();// 调用它doBind0(regFuture, channel, localAddress, promise);}}
});

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.5.2 标记 Channel 为激活状态

  • 绑定成功后,调用 setActive(true) 将 Channel 标记为激活状态。

1.5.3 触发 Channel 激活事件

  • 通过 PipelineHead 开始向后传播 channelActive 事件

1.5.4 自动注册ACCEPT事件

  • 这是一个精妙的设计!channelActive 事件最终会传播到 PipelineHeadContext
  • HeadContextchannelActive 方法中会调用 readIfIsAutoRead()
  • 这个方法调用会层层向下,最终到达底层的 AbstractNioChannel,其 doBeginRead() 方法会被调用。
  • 最终操作doBeginRead() 会修改之前注册到 Selector 上的 SelectionKey 的兴趣操作:selectionKey.interestOps(interestOps | OP_ACCEPT)
  • 至此,Netty 服务端才开始正式监听 OP_ACCEPT 事件,准备接收客户端的连接。

1.6 接收连接 - ServerBootstrapAcceptor 的工作

当客户端发起连接,BossGroupEventLoop 轮询到 OP_ACCEPT 事件时,真正的“魔法”开始了。

  1. 创建子 ChannelEventLoop 线程会调用 NioServerSocketChannelread() 方法,其内部通过 ServerSocketChannel.accept() 创建一个 JDK 的 SocketChannel,并同样被包装成 Netty 的 NioSocketChannel

  2. “Acceptor” 接手: 这个新创建的 NioSocketChannel 会作为入站数据,在 Pipeline 中传播。它首先到达 Head,然后流经 ServerBootstrapAcceptor

  3. 配置并注册子 ChannelServerBootstrapAcceptorchannelRead 方法被触发,它负责:

    • ServerBootstrap.childOptions()childAttrs() 设置到子 Channel 上。
    • ServerBootstrap.childHandler() 中设置的 ChannelInitializer 添加到子 Channel 的 Pipeline 中。
  4. 移交完成: 自此,这个客户端连接生命周期的所有后续 I/O 事件(读、写、断开)都将由 WorkerGroup 中的那个 EventLoop 全权负责。BossGroup 的职责圆满完成。

1.7 总结与精要分析

阶段核心操作执行线程关键细节
创建初始化创建 NioServerSocketChannel主线程配置非阻塞,创建 Pipeline,添加 ServerBootstrapAcceptor
注册将 Channel 注册到 SelectorBossGroup 线程兴趣操作为 0,附件为 Netty Channel,异步操作
绑定调用 JDK bindBossGroup 线程绑定端口,触发 channelActive 事件
激活监听注册 OP_ACCEPT 事件BossGroup 线程HeadContextchannelActive 事件中自动完成
接收连接ServerBootstrapAcceptor 工作BossGroup 线程接收连接,配置子 Channel,将其转交给 WorkerGroup

设计理念与精妙之处:

  1. 职责分离 (Separation of Concerns)BossGroup 只管接收连接,WorkerGroup 只管处理连接,架构清晰,易于理解和扩展。
  2. 完全异步: 从注册到绑定,所有耗时操作都封装成任务提交给 EventLoop,保证启动过程不阻塞主线程。
  3. 惰性监听: 先注册(interestOps=0),后绑定,再设置 OP_ACCEPT。这是一个严谨的状态管理流程,避免了在未准备好时接收到事件。
  4. 统一的抽象: 无论是服务端 Channel 还是客户端 Channel,都通过 ChannelPipelineEventLoop 进行管理,提供了极其一致和灵活的编程模型。
  5. 高效的连接接收ServerBootstrapAcceptor 内置于框架中,将接收连接和后续处理完美衔接,整个过程都在 EventLoop 线程内完成,无比高效。

Netty 的启动过程是其 Reactor 线程模型的完美体现,每一个步骤都经过精心设计,以确保在高并发场景下能够实现最高的性能和可靠性

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一个海康相机OCR的程序

这是一个极其复杂和庞大的​​机器视觉检测程序​​&#xff0c;其核心特点是​​多重冗余、条件判断和流程分支​​。它并非一个简单的线性流程&#xff0c;而是一个为应对各种复杂工业场景&#xff08;如光照变化、产品位置偏移、识别难度高等&#xff09;而设计的​​决策网…

深入解析:preload与prefetch的区别及最佳实践

在前端性能优化领域&#xff0c;资源加载策略直接影响页面的加载速度和用户体验。<link>标签的preload和prefetch属性是浏览器提供的两种关键资源预加载机制&#xff0c;它们都能提前加载资源&#xff0c;但适用场景和行为逻辑却大不相同。本文将从定义、触发时机、优先级…

[论文阅读] 人工智能 + 软件工程(漏洞检测)| 工业场景漏洞检测新突破:CodeBERT跨领域泛化能力评估与AI-DO工具开发

工业场景漏洞检测新突破&#xff1a;CodeBERT跨领域泛化能力评估与AI-DO工具开发 论文信息 论文原标题&#xff1a;Cross-Domain Evaluation of Transformer-Based Vulnerability Detection: Open-Source vs. Industrial Data引文格式&#xff08;APA&#xff09;&#xff1a;[…

【层面一】C#语言基础和核心语法-01(类型系统/面向对象/异常处理)

文章目录1 类型系统1.1 为什么需要类型&#xff1f;1.2 .NET 类型系统的两大支柱&#xff1a;CTS 和 CLS1.3 最根本的分类&#xff1a;值类型 vs 引用类型1.4 内置类型 vs. 自定义类型1.5 类型转换1.6 通用基类&#xff1a;System.Object2 面向对象编程2.1 类和对象2.2 接口和类…

Deepseek构建本地知识库

一.本地部署Deepseek Ollama 介绍 目前市面上主流的&#xff0c;成本最低的部署本地大模型的方法就是通过 Ollama 了&#xff1a; Ollama 是一个开源的本地大语言模型运行框架&#xff0c;专为在本地机器上便捷部署和运行大型语言模型&#xff08;LLM&#xff09;而设计。 核心…

idea自动编译,idea不重启项目,加载修改的内容

idea自动编译&#xff0c;idea不重启项目&#xff0c;加载修改的内容

幸运盒项目—测试报告

幸运盒测试报告 目录幸运盒测试报告一. 概要二. 测试环境三. 测试用例脑图四. 测试用例1. 功能测试1. 注册功能2. 密码登录功能3. 验证码登录功能4. 注册用户功能5. 创建奖品功能6. 新建抽奖活动功能8. 奖品列表9. 活动列表2. 界面测试1. 注册界面2. 密码登录界面3. 验证码登录…

Estimator and Confidence interval

Coefficient of determination and sample correlation coefficient R2SSRSSTR^2 \frac{SSR}{SST}R2SSTSSR​ SSR∑i1n((yi^−yˉ)2)SSR\sum_{i1}^n((\hat{y_{i}}-\bar{y})^2)SSR∑i1n​((yi​^​−yˉ​)2) SST∑i1n((yi−yˉ)2)SST\sum_{i1}^n((y_{i}-\bar{y})^2)SST∑i1n​…

【网络编程】TCP 服务器并发编程:多进程、线程池与守护进程实践

半桔&#xff1a;个人主页&#x1f525; 个人专栏: 《Linux手册》《手撕面试算法》《网络编程》&#x1f516;很多人在喧嚣声中登场&#xff0c;也有少数人在静默中退出。 -张方宇- 文章目录前言套接字接口TCP服务器TCP 多进程TCP 线程池重写Task任务放函数对象客户端重连进程…

还停留在批处理时代吗?增量计算架构详解

在当今的数字化环境中&#xff0c;企业不再只是一味地囤积数据——他们痴迷于尽快把数据转化为可付诸行动的洞察。真正的优势来自于实时发现变化并立即做出反应&#xff0c;无论是调整推荐策略还是规避危机。 十年前&#xff0c;硬件与平台技术的进步让我们能够从容应对海量数…

DataSet-深度学习中的常见类

深度学习中Dataset类通用的架构思路 Dataset 类设计的必备部分 1. 初始化 __init__ 配置和路径管理&#xff1a;保存 config&#xff0c;区分 train/val/test 路径。加载原始数据&#xff1a;CSV、JSON、Numpy、Parquet 等。预处理器/归一化器&#xff1a;如 StandardScaler&am…

【VC】 error MSB8041: 此项目需要 MFC 库

▒ 目录 ▒&#x1f6eb; 导读问题背景环境1️⃣ 核心原因&#xff1a;MFC 组件缺失或配置不当2️⃣ 解决方案&#xff1a;安装 MFC 组件并验证配置2.1 步骤1&#xff1a;检查并安装 MFC 组件2.2 步骤2&#xff1a;检查并修正项目配置2.3 步骤3&#xff1a;针对特定场景的补充方…

Java零基础学习Day10——面向对象高级

一.认识final1.含义final关键字是最终的意思&#xff0c;可以修饰&#xff1a;类&#xff0c;方法&#xff0c;变量修饰类&#xff1a;该类被称为最终类&#xff0c;特点是不能被继承修饰方法&#xff1a;该方法被称为最终方法&#xff0c;特点是不能被重写了修饰变量&#xff…

Qt中解析JSON文件

Qt中解析JSON文件 在Qt中解析JSON字符串主要有两种方式&#xff1a;使用QJsonDocument类或使用QJsonDocument结合QVariant。以下是详细的解析方法&#xff1a; 使用QJsonDocument&#xff08;推荐&#xff09; 这种方式的主要相关类如下&#xff1a; QJsonDocument: QJsonDocum…

深度解析HTTPS:从加密原理到SSL/TLS的演进之路

在互联网时代,数据安全已成为不可忽视的基石。当我们在浏览器地址栏看到"https://"前缀和那把小小的绿色锁图标时,意味着正在进行一场受保护的通信。但这层保护究竟是如何实现的?HTTPS、SSL和TLS之间又存在着怎样的联系与区别?本文将深入剖析这些技术细节,带你全…

Flutter 官方 LLM 动态 UI 库 flutter_genui 发布,让 App UI 自己生成 UI

今日&#xff0c;Flutter 官方正式发布了它们关于 AI 大模型的 package 项目&#xff1a; genui &#xff0c;它是一个非常有趣和前沿的探索类型的项目&#xff0c;它的目标是帮助开发者构建由生成式 AI 模型驱动的动态、对话式用户界面&#xff1a; 也就是它与传统 App 中“写…

Redis常用数据结构及其底层实现

Redis常用数据结构主要有String List Set Zset Hash BitMap Hyperloglog Stream GeoString:Redis最常用的一种数据结构,Sting类型的数据存储结构有三种int、embstr、raw1.int:用来存储long以下的整形embstr raw 都是用来存字符串&#xff0c;其中小于44字节的字符串用embstr存 …

O3.4 opencv图形拼接+答题卡识别

一图形拼接逻辑导入必要的库pythonimport cv2 import numpy as np import sys导入cv2库用于图像处理&#xff0c;numpy库用于数值计算&#xff0c;sys库用于与 Python 解释器进行交互&#xff0c;例如退出程序。定义图像显示函数def cv_show(name, img):cv2.imshow(name, img)c…

SQL注入常见攻击点与防御详解

SQL注入是一种非常常见且危险的Web安全漏洞。攻击者通过将恶意的SQL代码插入到应用程序的输入参数中&#xff0c;欺骗后端数据库执行这些非预期的命令&#xff0c;从而可能窃取、篡改、删除数据或获得更高的系统权限。以下是详细、准确的SQL注入点分类、说明及举例&#xff1a;…

EKSPod 资源利用率配置修复:从占位符到完整资源分析系统

概述 在 Kubernetes 集群管理过程中,资源利用率的监控和优化是保证应用性能和成本效益的关键环节。近期,我们对 EKSPod 管理界面的资源利用率显示功能进行了全面修复,将原先简单的占位符文本升级为完整的资源分析系统。本文将详细介绍这次修复的背景、方案、实现细节和最终…