Netty源码分析---initChannel方法调用流程详解

Netty源码分析系列之服务端Channel注册 Netty中服务端channel是如何注册到多路复用器上的? 阅读详情

initChannel 方法调用流程详解

前言

当 handler().handlerAdded(this) 被调用时,调用的是哪个 ChannelInitializer 实例的方法?

本文将深入剖析这个问题,带你理解 Netty 中两个 ChannelInitializer 实例的调用关系。

让我们先回顾一下关键代码:

final void callHandlerAdded() throws Exception {
    if (setAddComplete()) {
        // 这里调用的是哪个 Handler 的 handlerAdded 方法?
        handler().handlerAdded(this);
    }
}

这个问题的答案是:调用的是 Bootstrap.init() 方法中创建的匿名 ChannelInitializer 实例的 handlerAdded() 方法。

为什么呢?让我们一步步分析。

理解关键:Context 持有的是哪个 Handler?

回顾 Pipeline 初始化的准备阶段

在 Bootstrap.init() 方法中,Netty 创建了一个匿名 ChannelInitializer:

@Override
void init(Channel channel) throws Exception {
    ChannelPipeline p = channel.pipeline();
    
    // 创建匿名 ChannelInitializer(我们称它为 Initializer-A)
    p.addLast(new ChannelInitializer<Channel>() {
        @Override
        public void initChannel(final Channel ch) throws Exception {
            final ChannelPipeline pipeline = ch.pipeline();
            // 获取用户设置的 handler(MyChannelInitializer)
            ChannelHandler handler = config.handler();
            if (handler != null) {
                // 将 MyChannelInitializer 添加到 Pipeline
                pipeline.addLast(handler);
            }
        }
    });
}

关键点:addLast() 做了什么?

当调用 p.addLast(new ChannelInitializer<Channel>() {...}) 时:

  1. 创建了一个新的 Context(我们称它为 ctx-A)
  2. ctx-A 持有的 handler 引用指向这个匿名 ChannelInitializer(Initializer-A)
  3. ctx-A 被添加到 Pipeline 链表中
  4. 创建了一个 PendingHandlerAddedTask,持有 ctx-A 的引用

此时的状态:

Pipeline: head <-> ctx-A(持有 Initializer-A) <-> tail
待处理任务: PendingHandlerAddedTask(ctx-A)

执行阶段:调用 handlerAdded

当 Channel 注册完成后,会执行待处理任务:

// PendingHandlerAddedTask.execute()
void execute() {
    callHandlerAdded0(ctx);  // 这里的 ctx 就是 ctx-A
}

// callHandlerAdded0
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
    ctx.callHandlerAdded();  // 调用 ctx-A 的 callHandlerAdded
}

// AbstractChannelHandlerContext.callHandlerAdded
final void callHandlerAdded() throws Exception {
    if (setAddComplete()) {
        // 这里的 handler() 返回的是 ctx-A 持有的 handler
        // 也就是 Initializer-A(匿名 ChannelInitializer)
        handler().handlerAdded(this);
    }
}

关键理解:

  • handler() 方法返回的是当前 Context 持有的 Handler 引用
  • ctx-A 持有的是 Initializer-A(匿名 ChannelInitializer)
  • 所以 handler().handlerAdded(this) 调用的是 Initializer-A 的 handlerAdded() 方法

完整的调用流程

现在让我们完整地追踪整个调用流程,看看 Initializer-A 的 handlerAdded() 方法是如何最终调用到我们自定义的 MyChannelInitializer 的 initChannel() 方法的。

第一步:调用 Initializer-A 的 handlerAdded()

// ChannelInitializer.handlerAdded()
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    // 检查 Channel 是否已经注册
    if (ctx.channel().isRegistered()) {
        // 调用 initChannel(ctx)
        if (initChannel(ctx)) {
            // 初始化成功后,移除自己
            removeState(ctx);
        }
    }
}

作用:

这个方法是 ChannelInitializer 的核心方法,它会:

  1. 检查 Channel 是否已经注册(此时已经注册)
  2. 调用 initChannel(ctx) 方法
  3. 初始化完成后,将自己从 Pipeline 中移除

第二步:调用 initChannel(ChannelHandlerContext)

// ChannelInitializer.initChannel(ChannelHandlerContext)
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
    // 使用 Set 确保每个 Context 只初始化一次
    if (initMap.add(ctx)) {
        try {
            // 关键:调用抽象方法 initChannel(Channel)
            // 由于多态,这里会调用子类(Initializer-A)重写的版本
            initChannel((C) ctx.channel());
        } catch (Throwable cause) {
            // 如果初始化过程中出现异常,触发异常处理
            exceptionCaught(ctx, cause);
        } finally {
            // 无论成功还是失败,都要将 ChannelInitializer 从 Pipeline 中移除
            ChannelPipeline pipeline = ctx.pipeline();
            if (pipeline.context(this) != null) {
                pipeline.remove(this);
            }
        }
        return true;
    }
    return false;
}

作用:

这个方法做了三件重要的事:

  1. 使用 initMap 确保每个 Context 只初始化一次(防止重复初始化)
  2. 调用抽象方法 initChannel(Channel),由于 Java 的多态机制,会调用 Initializer-A 重写的版本
  3. 在 finally 块中移除 ChannelInitializer,确保它只执行一次

为什么要移除?

ChannelInitializer 是一个"一次性"的 Handler,它的唯一作用就是在合适的时机初始化 Pipeline。一旦初始化完成,它就没有存在的必要了,所以要从 Pipeline 中移除。

第三步:调用 Initializer-A 重写的 initChannel(Channel)

// 这是在 Bootstrap.init() 中定义的匿名内部类
new ChannelInitializer<Channel>() {
    @Override
    public void initChannel(final Channel ch) throws Exception {
        final ChannelPipeline pipeline = ch.pipeline();
        
        // 获取用户设置的 handler(MyChannelInitializer)
        ChannelHandler handler = config.handler();
        
        if (handler != null) {
            // 将 MyChannelInitializer 添加到 Pipeline
            pipeline.addLast(handler);
        }
        
        // 注意:这里可能还有其他逻辑,比如添加 ServerBootstrapAcceptor
        // 但对于客户端来说,主要就是添加用户的 handler
    }
}

作用:

这是 Initializer-A 重写的 initChannel() 方法,它的作用是:

  1. 获取用户通过 b.handler() 设置的 Handler(也就是 MyChannelInitializer)
  2. 将 MyChannelInitializer 添加到 Pipeline 中

关键理解:

  • config.handler() 返回的是我们在客户端启动代码中设置的 new MyChannelInitializer()
  • 这里调用 pipeline.addLast(handler) 会将 MyChannelInitializer 添加到 Pipeline

此时 Pipeline 的状态变化:

之前:head <-> Initializer-A <-> tail
之后:head <-> MyChannelInitializer <-> tail
(Initializer-A 在 finally 块中被移除)

MyChannelInitializer 的初始化流程

现在,MyChannelInitializer 已经被添加到 Pipeline 中了。接下来,它也会经历同样的初始化流程。

第四步:addLast(MyChannelInitializer)

当调用 pipeline.addLast(handler) 时(这里的 handler 是 MyChannelInitializer):

@Override
public final ChannelPipeline addLast(ChannelHandler... handlers) {
    return addLast(null, handlers);
}

public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
    if (handlers == null) {
        throw new NullPointerException("handlers");
    }
    
    for (ChannelHandler h: handlers) {
        if (h == null) {
            break;
        }
        // 遍历每个 Handler,逐个添加
        addLast(executor, null, h);
    }
    
    return this;
}

最终会调用到:

@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
    final AbstractChannelHandlerContext newCtx;
    
    synchronized (this) {
        // 检查 Handler 是否可以被多次添加
        checkMultiplicity(handler);
        
        // 将 MyChannelInitializer 包装成 Context(我们称它为 ctx-B)
        newCtx = newContext(group, filterName(name, handler), handler);
        
        // 将 ctx-B 添加到 Pipeline 链表中
        addLast0(newCtx);
        
        // 关键判断:此时 Channel 已经注册(registered = true)
        if (!registered) {
            // 如果还没注册,创建待处理任务
            newCtx.setAddPending();
            callHandlerCallbackLater(newCtx, true);
            return this;
        }
        
        // 因为已经注册,所以直接调用 handlerAdded
        EventExecutor executor = newCtx.executor();
        if (!executor.inEventLoop()) {
            // 如果不在 EventLoop 线程中,提交任务
            callHandlerAddedInEventLoop(newCtx, executor);
            return this;
        }
    }
    
    // 在 EventLoop 线程中,直接调用
    callHandlerAdded0(newCtx);
    return this;
}

关键区别:

与 Initializer-A 不同,此时 Channel 已经注册(registered = true),所以不会创建待处理任务,而是直接调用 callHandlerAdded0(newCtx)。

第五步:调用 callHandlerAdded0(ctx-B)

private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
    try {
        // 调用 ctx-B 的 callHandlerAdded
        ctx.callHandlerAdded();
    } catch (Throwable t) {
        // 异常处理:如果出现异常,移除这个 Handler
        boolean removed = false;
        try {
            remove0(ctx);
            ctx.callHandlerRemoved();
            removed = true;
        } catch (Throwable t2) {
            if (logger.isWarnEnabled()) {
                logger.warn("Failed to remove a handler: " + ctx.name(), t2);
            }
        }
        
        // 触发异常事件
        if (removed) {
            fireExceptionCaught(new ChannelPipelineException(
                ctx.handler().getClass().getName() +
                ".handlerAdded() has thrown an exception; removed.", t));
        } else {
            fireExceptionCaught(new ChannelPipelineException(
                ctx.handler().getClass().getName() +
                ".handlerAdded() has thrown an exception; also failed to remove.", t));
        }
    }
}

作用:

调用 ctx-B 的 callHandlerAdded() 方法,并处理可能出现的异常。

第六步:调用 ctx-B.callHandlerAdded()

final void callHandlerAdded() throws Exception {
    // 设置状态为 ADD_COMPLETE,确保 handlerAdded 只被调用一次
    if (setAddComplete()) {
        // 这里的 handler() 返回的是 ctx-B 持有的 handler
        // 也就是 MyChannelInitializer
        handler().handlerAdded(this);
    }
}

关键理解:

  • 这里的 handler() 返回的是 ctx-B 持有的 Handler
  • ctx-B 持有的是 MyChannelInitializer
  • 所以这里调用的是 MyChannelInitializer 的 handlerAdded() 方法

第七步:调用 MyChannelInitializer 的 handlerAdded()

// ChannelInitializer.handlerAdded()
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    if (ctx.channel().isRegistered()) {
        // 调用 initChannel(ctx)
        if (initChannel(ctx)) {
            removeState(ctx);
        }
    }
}

这个方法和 Initializer-A 的 handlerAdded() 是同一个方法(都是 ChannelInitializer 类的方法)。

第八步:调用 initChannel(ChannelHandlerContext)

private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
    if (initMap.add(ctx)) {
        try {
            // 调用抽象方法 initChannel(Channel)
            // 由于多态,这里会调用 MyChannelInitializer 重写的版本
            initChannel((C) ctx.channel());
        } catch (Throwable cause) {
            exceptionCaught(ctx, cause);
        } finally {
            // 将 MyChannelInitializer 从 Pipeline 中移除
            ChannelPipeline pipeline = ctx.pipeline();
            if (pipeline.context(this) != null) {
                pipeline.remove(this);
            }
        }
        return true;
    }
    return false;
}

第九步:调用 MyChannelInitializer 重写的 initChannel(Channel)

// 这是我们自定义的 MyChannelInitializer
public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel channel) throws Exception {
        // 这里是我们的业务逻辑
        System.out.println("链接报告开始");
        System.out.println("链接报告信息:本客户端链接到服务端。channelId:" + channel.id());
        System.out.println("链接报告完毕");
        
        // 我们可以在这里添加其他 Handler
        // channel.pipeline().addLast(new MyHandler1());
        // channel.pipeline().addLast(new MyHandler2());
    }
}

作用:

这就是我们自定义的初始化逻辑!在这里,我们可以:

  1. 打印日志
  2. 添加编解码器
  3. 添加业务 Handler
  4. 进行其他初始化操作

执行完成后,MyChannelInitializer 也会在 finally 块中被移除。

完整流程图

让我们用一张图来总结整个调用流程:

时间线:T1 - 客户端启动
    |
    v
b.handler(new MyChannelInitializer())
    [MyChannelInitializer 实例被保存在 config 中]

-------------------------------------------------------------------

时间线:T2 - 调用 b.connect()
    |
    v
Bootstrap.init(channel)
    |
    v
创建 Initializer-A(匿名 ChannelInitializer)
    |
    v
p.addLast(Initializer-A)
    |
    v
创建 ctx-A,持有 Initializer-A 的引用
    |
    v
将 ctx-A 添加到 Pipeline
    |
    v
创建 PendingHandlerAddedTask(ctx-A)

Pipeline 状态:head <-> ctx-A(Initializer-A) <-> tail

-------------------------------------------------------------------

时间线:T3 - Channel 注册完成
    |
    v
执行 PendingHandlerAddedTask
    |
    v
callHandlerAdded0(ctx-A)
    |
    v
ctx-A.callHandlerAdded()
    |
    v
ctx-A.handler().handlerAdded(ctx-A)
    [这里的 handler() 返回 Initializer-A]
    |
    v
Initializer-A.handlerAdded(ctx-A)
    |
    v
Initializer-A.initChannel(ctx-A)
    |
    v
Initializer-A.initChannel(channel)  ← 调用匿名内部类重写的方法
    |
    v
获取 config.handler()  [返回 MyChannelInitializer]
    |
    v
pipeline.addLast(MyChannelInitializer)
    |
    v
Initializer-A 被移除

Pipeline 状态:head <-> ctx-B(MyChannelInitializer) <-> tail

-------------------------------------------------------------------

时间线:T4 - MyChannelInitializer 被添加
    |
    v
创建 ctx-B,持有 MyChannelInitializer 的引用
    |
    v
因为 registered = true,直接调用 callHandlerAdded0(ctx-B)
    |
    v
ctx-B.callHandlerAdded()
    |
    v
ctx-B.handler().handlerAdded(ctx-B)
    [这里的 handler() 返回 MyChannelInitializer]
    |
    v
MyChannelInitializer.handlerAdded(ctx-B)
    |
    v
MyChannelInitializer.initChannel(ctx-B)
    |
    v
MyChannelInitializer.initChannel(channel)  ← 调用我们重写的方法
    |
    v
执行我们的业务逻辑:
    - 打印日志
    - 添加其他 Handler
    |
    v
MyChannelInitializer 被移除

最终 Pipeline 状态:head <-> [我们添加的 Handler] <-> tail

核心要点总结

1. 两个不同的 ChannelInitializer 实例

  • Initializer-A:Bootstrap.init() 中创建的匿名内部类

    • 作用:将用户设置的 Handler(MyChannelInitializer)添加到 Pipeline
    • 生命周期:在 register 完成后被调用,执行完立即移除
  • MyChannelInitializer:用户自定义的 ChannelInitializer

    • 作用:执行用户的初始化逻辑,添加业务 Handler
    • 生命周期:被 Initializer-A 添加到 Pipeline 后立即被调用,执行完立即移除

2. 为什么 callHandlerAdded() 调用的是 Initializer-A?

因为:

  1. 在准备阶段,p.addLast(Initializer-A) 创建了 ctx-A
  2. ctx-A 持有的 handler 引用指向 Initializer-A
  3. PendingHandlerAddedTask 持有的是 ctx-A 的引用
  4. 执行阶段调用 ctx-A.handler().handlerAdded() 时,返回的是 Initializer-A

关键理解:Context 持有哪个 Handler 的引用,handler() 方法就返回哪个 Handler。

3. registered 标志的作用

registered 标志决定了 Handler 的初始化时机:

  • registered = false(Channel 还没注册)

    • 创建 PendingHandlerAddedTask,延迟到注册完成后执行
    • Initializer-A 就是这种情况
  • registered = true(Channel 已经注册)

    • 直接调用 callHandlerAdded0(),立即执行初始化
    • MyChannelInitializer 就是这种情况

4. ChannelInitializer 的自动移除机制

ChannelInitializer 在 initChannel(ChannelHandlerContext) 方法的 finally 块中会自动移除自己:

finally {
    ChannelPipeline pipeline = ctx.pipeline();
    if (pipeline.context(this) != null) {
        pipeline.remove(this);
    }
}

这确保了 ChannelInitializer 只执行一次,执行完就从 Pipeline 中消失。

5. 多态机制的应用

整个流程中,Java 的多态机制起到了关键作用:

// ChannelInitializer 中的代码
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
    // 调用抽象方法
    initChannel((C) ctx.channel());
}

虽然这里调用的是抽象方法 initChannel(Channel),但由于多态:

  • 如果当前实例是 Initializer-A,就调用 Initializer-A 重写的版本
  • 如果当前实例是 MyChannelInitializer,就调用 MyChannelInitializer 重写的版本
Netty源码分析----服务启动之Channel初始化 Netty底层也是基于NIO,所以在分析服务启动的流程之前,我们先回顾一下NIO的启动Server的代码,写的一个Server例子如下,只保留和Netty启动相关的代码 public class NioServer implements Runnable { public static void main(String[] args) { new Thread(new... 阅读详情

相关推荐

netty服务端Chanel注册与线程模型详解(附带源码分析)

一、概述 在上一篇博客中,介绍了netty其实是有其背后的理论支撑的——reactor模式,并且详细介绍了reactor模式。而这边博客将会详细介绍netty是怎么运用reactor模式的,代码是怎么设计和封装的。 二、服务端代码 现在看一段使用netty编写的服务端代码: public class MyServer { public static void main(String[] ...

zw19910924的专栏 1702

[Netty入门] 最简单的Netty应用程序实例

客户端和服务器之前的交互相对比较简单。在客户端与服务器建立连接之后,它会向服务器发送一个或多个消息。反过来,服务器也会将每个消息回送给客户端。接下来将从具体的实例代码来简单看下客户端和服务器具体是怎么交互的。

卧龙小蛋的博客 2369

原来 Netty 的核心启动逻辑是这样的

这篇我们就着重的说下 bind 方法,这个方法也是触发 Netty 真正启动的方法。 先打个预防针,源码也不是那么简单的,有时候看着有点绕,如果你想面试的时候胸有成竹,还是得有点耐心的,如果中间没看懂没事,最后我有总结,看完总结之后应该会清晰的。 对了,如果有条件的话,建议在电脑上看这篇文章,会更加舒适。 好了,开局先来一张图,bind 的核心操作就如下图所示,下面长篇的源码分析也是为了说清楚这个流程,所以什么类名,方法名都不重要,重要的是知晓整体流程: 注意,上图的 Channel

weixin_64314555的博客 579

Netty教程(一)-服务端启动(一)——创建和初始化channel

https://juejin.cn/post/6887195923832078343 以netty的一个小demo为例(使用的源码版本为4.1.50.Final) 从b.bind(PORT)跟进 最终调用到AbstractBootstrap#doBind这个方法 private ChannelFuture doBind(final SocketAddress localAddress) { final ChannelFuture regFuture = initAndRegister(

yygr的博客 745

ONNC Open Neural Network Compiler 开源神经网络编译器 一个 开源、模块化、可重用编译器算法和工具链的库,专门针对深度学习加速器(DLA)部署导入ONNX模型

ONNC是一个集合了开源、模块化、可重用编译器算法和工具链的库,专门针对深度学习加速器(DLA)。ONNC从底层开始构建,旨在将ONNX中间表示(IR)翻译成专有DLA代码。其软件架构设计强调可移植性和可重用性,从而简化了开发新后端的工作。上图展示了ONNC软件栈的顶层框图。软件栈从导入ONNX计算图模型到输出相应的硬件二进制文件,展示了各个功能块。

万有文的博客 1997

【虚幻引擎】UE4初学者系列教程基础篇-全中文新手入门教程(UE5系列继续更新)

菜单栏菜单栏当中有文件、编辑、窗口帮助。文件当中可以进行一个关卡的新建、关卡的打开、关卡的保存还有就是打包项目,(项目大于关卡,项目里面可能有几百个关卡),这是比较常用的几个,这里可以看到打包当中有安卓端HTML5端,IOS端linux,还有就是TVOS(苹果电视)、windows的一些端口的一些设置。还可以打开最近打开的项目。关卡编辑器:工具栏3.3 “模式”面板保存所有:ctrl+shift+s(有的软件截屏也是这个按键需要注意)3.4 “内容浏览器”面板缩放:ctr+鼠标滚轮3.5.视口面板。

qq_45206556的博客 2万+

netty1 -> init「channel和channelPipeline的生成」

1.channel的生成时机 channel可以简单理解成网络连接对象 一般channel会在doBind和doConnect的时候生成,具体的可以跟一下initAndRegister方法 具体生成比较简单:使用了反射工厂的 newInstance方法 不过channel的具体类型略有差异 public class ReflectiveChannelFactory<T exten...

u013657993的专栏 870

视觉检测在激光打码字符检测上的检测案例

在机器视觉检测技术中,OCR识别是比较典型的应用,应用的场景也比较丰富,基本上在制造业,每个工厂或者印刷厂都会用到视觉OCR识别检测,OCR字符识别的原理很简单,通过图像分割后,对字符进行学习、命名。在工业相机拍摄图像后,软件进行处理,从而读取结果。 随着技术的发展,激光打标技术被更多的应用在生产过程中,由激光打标代替油墨进行字符印刷,速度更快、效果更好。 但是随之而来的是在视觉检测上带来的影响,目前市面上应用的字符检测设备大多是由集成商单独开发的软件,在检测油墨印刷时,图像上的字符没有断点,非常连贯,所需

视觉检测设备-视觉检测系统-单张检测设备-卷料印品检测 1463

Netty源码分析---netty-init()方法解析

摘要 ChannelInitializer 是 Netty 中一个特殊的 ChannelHandler,主要用于延迟初始化管道(Pipeline)。它的核心特点包括: 延迟初始化 - 只在 Channel 注册到 EventLoop 后才执行真正的初始化逻辑,避免直接添加 Handler 时因未注册导致的错误。 自动移除机制 - 执行完 initChannel() 方法后会自动从 Pipeline 中移除,保证一次性使用。 简化配置 - 提供统一入口配置 Pipeline,ServerBootstrap 在

java_rutu的博客 391

详细解读文字识别工具———Tesseract-OCR

   在尝试破解12306验证码的时候,提前了解了一下应对各种验证码的解决方案,这里主要是学习到了面对文字图片验证码,例如亚马逊、豆瓣等平台的登录时会遇到的情况,效果还不错。 一、工具介绍 ​    Tesseract-OCR 是一款由HP实验室开发由Google维护的开源OCR(Optical Character Recognition...

别看我只是一只羊 2万+

微信小程序后台定位与实时位置更新的实战指南

本文提供了微信小程序实现后台定位与实时位置更新的完整实战指南。针对小程序退到后台后位置更新中断的痛点,详细解析了基于`startLocationUpdateBackground`API的后台定位解决方案。内容涵盖从隐私协议配置、项目权限声明,到授权、启动、监听、停止的核心代码流程,并分享了真机调试、性能优化及权限引导等关键避坑经验,帮助开发者构建稳定可靠的后台位置服务。

weixin_29008055的博客 473

Netty源码分析---Netty-Pipeline初始化流程详解

本文深入解析了Netty Pipeline的初始化流程,主要分为两个阶段:准备阶段和执行阶段。在准备阶段,Netty通过initAndRegister()方法创建Channel实例并将ChannelInitializer添加到Pipeline中,但此时不会立即执行初始化逻辑。执行阶段发生在Channel注册到EventLoop后,触发handlerAdded()方法完成真正的初始化。文章详细跟踪了从客户端启动代码到addLast()方法的核心逻辑,揭示了Netty采用"延迟初始化"策略的

java_rutu的博客 421

AI: 机器学习的模型是如何训练的?(在试错中学习)

一句话理解机器学习一般训练过程 :通过有标签样本来调整(学习)并确定所有权重Weights和偏差Bias的理想值。 训练的目标:最小化损失函数 (损失函数下面马上会介绍) 机器学习算法在训练过程中,做的就是:检查多个样本并尝试找出可最大限度地减少损失的模型;目标就是将损失(Loss)最小化 在这里插入图片描述 上图就是一般模型训练的一般过程(试错过程),其中 模型: 将一个或多个特征作为输入,然后返回一个预测 (y’) 作为输出。为了进行简化,不妨考虑一种采用一个特征并返回一个预测的模型,如下公式

HowieXue 薛永浩的博客 3万+

Netty源码分析-ChannelInitializer

ChannelInitializer是一个PPLine的初始化工具,可以往PPLine里面设置Handler。 protected abstract void initChannel(C ch) throws Exception; 我们可以重写此方法来完成初始化动作,往PPLine当中加入Handler。 .childHandler(new ChannelInitializer<S...

大新博客 1508

税务某局 webpack 登录接口逆向分析

我们再回到接口逻辑去看一下,发现每次请求前,还有两个接口,getpublickey和sendsm4,这里分析了一下调用逻辑,sm4这个接口用到一个参数是由publickey返回的一个参数而来,这个参数又经过内部加密处理,我们找到这个参数加密的地方,大概是在这个位置。我们还是顺着逻辑将加载器扣下来,然后用一个变量去接收,有了加载器,我们直接把加密代码扣下来做下处理,把一些用到的参数整理下如下。打上断点分析后,_utils_getuuid这个很长的变量也是加载器的一个函数,我们将这段代码也扣下来,如下。

码王吴彦祖的博客 403

[Dify 实战] 如何在内网环境部署 Dify 应用:彻底离线的企业级方案解析

企业内网环境如何离线部署Dify?本文提供详细教程:Dify作为开源AI平台,支持完全离线部署,无需外网访问。文中介绍两种部署方式:推荐使用Docker一键部署,也可选择源码安装定制化方案。重点讲解内网模型配置(支持Ollama/GPT-OSS等)、知识库存储(MinIO/PostgreSQL)及内网代理设置,并给出常见问题排查方法。该方案适用于金融、政府等对数据安全要求高的场景,确保敏感数据不泄露,实现完全本地化的AI应用闭环。

技术小甜甜:传播可落地技术,帮助个人和企业降低解决真实场景问题的成本 455

Netty源码分析---ChannelInitializer工作原理详解

Netty的ChannelInitializer工作原理分为两个阶段:首先将初始化器添加到pipeline中,然后在register时触发其handlerAdded()方法。该过程会调用用户重写的initChannel()方法添加实际处理器,最后ChannelInitializer会从pipeline中自动移除。整个过程通过父类ChannelInitializer的handlerAdded()方法触发,形成完整的处理器链初始化机制。

java_rutu的博客 218

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

netty学习4:ChannelPipeline源码分析

netty学习4:ChannelPipeline源码分析

weixin_45453628的博客 266

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Netty学习之旅----源码分析netty服务端初始化流程(Reactor主从模式实现)

终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!化[外链图片转存中…(img-J8eWwaOU-1712052812552)]《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

2401_83703835的博客 394

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Netty 源码分析之 一 客户端代码分析

没有创新,或者拾人牙慧,知识本来就是这样搬运着传播的。 客户端核心代码如下: public static void start(){ ClientChannelHandler channelHandler = new ClientChannelHandler(); EventLoopGroup bossGroup = new NioEventLoopGroup...

Java_Jsp_Ssh的博客 892

【Netty系列_3】Netty源码分析之服务端channel

highlight: androidstudio 前言 学习源码要有十足的耐性!越是封装完美的框架,内部就越复杂,源码很深很长!不过要抓住要点分析,实在不行多看几遍,配合debug,去一窥优秀框架的精髓!从而提高自己!分享他人! 作为一款优秀的网络通信框架,Netty经历过无数的生产验证,今天我们就一窥究竟,研究下Netty的源码,如果你对Netty的工作原理不清楚,或者对NIO不清楚,...

my的博客 475

java channel源码_Netty源码分析——服务端channel的创建

工作时候,有用过Netty写过网络库。最近想研究下RPC框架,就想着写几篇博客,梳理下Netty的源码。(研究的源码版本是4.1.x)最简单的示例首先还是拿Netty 官网的UserGuide做例子/*** Handles a server-side channel.*/public class DiscardServerHandler extends ChannelInboundHandlerA...

weixin_29410963的博客 309

netty理论之源码分析⑥ 细讲ChannelInitializer

1.ChannelInitializer是啥 一种特殊的ChannelInboundHandler,它提供了在通道注册到eventLoop后初始化通道的简单方法,ChannelInitializer的主要目的是为程序员提供了一个简单的工具,用于在某个Channel注册到EventLoop后,对这个Channel执行一些初始化操作。ChannelInitializer虽然会在一开始会被注册到Cha...

weixin_39666581的博客 3609

15-ChannelInitializer

文章目录ChannelInitializer一、继承关系二、源码2.1 注释2.2 源码分析2.3 handlerAdded和channelRegistered三、使用示例 ChannelInitializer ChannelInitializer 是一种特殊的 ChannelHandler,它也是一种 ChannelInboundHandler,它提供了在通道注册到 EventLoop 后初...

my_momo_csdn的博客 1959

Netty学习之旅------源码分析ChannelPipeline实现原理

其他的内容都可以按照路线图里面整理出来的知识点逐一去熟悉,学习,消化,不建议你去看书学习,最好是多看一些视频,把不懂地方反复看,学习了一节视频内容第二天一定要去复习,并总结成思维导图,形成树状知识网络结构,方便日后复习。这里还有一份很不错的《Java基础核心总结笔记》,特意跟大家分享出来目录:部分内容截图:《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取![外链图片转存中…(img-BsdcrZcN-1712656409200)]

2301_82242670的博客 1085

Netty源码分析--创建Channel(三)

先看一下我Netty的启动类 private void start() throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try {...

weixin_30653097的博客 115
上一篇: Netty源码分析---execute-method解析
下一篇: Netty源码分析---invokeHandlerAddedIfNeeded方法详解
不早睡不改名@
博客等级 码龄5年 536粉丝 306原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值