Netty源码分析篇:Channel 的 register 操作

Netty 源码解析(六): Channel 的 register 操作 今天是猿灯塔“365篇原创计划”第六篇。 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一 ):开始 Netty 源码解析(二): Netty 的 Channel Netty 源码解析(三): Netty 的 Future 和 Promise Netty 源码解析(四): Netty 的 ChannelPipeline Netty 源码解析(五): Netty 的线程池... 阅读详情

Netty Channel 注册流程深度解析

前言

在 Netty 的启动过程中,有一个非常关键的步骤:将 Channel 注册到 EventLoop 上。这个过程看似简单,实际上涉及了多个核心组件的协作。本文将带你一步步剖析这个注册流程,让你彻底理解 Netty 是如何将 Channel 和 EventLoop 绑定在一起的。

读完本文,你将了解:

  • Channel 注册的完整调用链路
  • EventLoop 线程是如何启动的
  • Channel 如何与 JDK 底层的 Selector 关联

一、注册流程的起点

无论是客户端的 connect() 还是服务端的 bind(port),最终都会走到 initAndRegister() 方法。我们以服务端启动为例来看:

// 服务端启动入口
public ChannelFuture bind(int inetPort) {
    return bind(new InetSocketAddress(inetPort));
}

public ChannelFuture bind(SocketAddress localAddress) {
    // ... 省略部分代码
    return doBind(localAddress);
}

private ChannelFuture doBind(final SocketAddress localAddress) {
    // 关键:初始化并注册 Channel
    final ChannelFuture regFuture = initAndRegister();
    // ... 省略后续代码
}

final ChannelFuture initAndRegister() {
    Channel channel = null;
    // ... 省略 channel 初始化代码
    
    // 重点:将 channel 注册到 EventLoopGroup 中
    ChannelFuture regFuture = config().group().register(channel);
    // ... 省略后续代码
}

这里的 config().group() 返回的就是我们在启动代码中创建的 NioEventLoopGroup 实例。

二、从 EventLoopGroup 到 EventLoop

NioEventLoopGroup 本身并没有实现 register() 方法,根据 Java 的继承机制,实际调用的是父类 MultithreadEventLoopGroup 的方法:

// MultithreadEventLoopGroup.java
@Override
public ChannelFuture register(Channel channel) {
    // 关键:选择一个 EventLoop 来处理这个 Channel
    return next().register(channel);
}

@Override
public EventLoop next() {
    return (EventLoop) super.next();
}

// MultithreadEventExecutorGroup.java
@Override
public EventExecutor next() {
    // 使用选择器从线程池中选择一个 EventLoop
    return chooser.next();
}

这里的 next() 方法做了什么呢?

简单来说,它从 NioEventLoopGroup 的线程池中选择一个 NioEventLoop 实例。还记得我们创建 NioEventLoopGroup 时传入的线程数吗?这里就是从这些线程中选一个出来,负责处理当前这个 Channel。

选择策略:Netty 使用 chooserFactory 来决定选择哪个 EventLoop,通常采用轮询(Round-Robin)的方式,保证负载均衡。

三、EventLoop 的注册实现

选中了 NioEventLoop 之后,接下来调用它的 register() 方法。但 NioEventLoop 本身也没有实现这个方法,真正的实现在它的父类 SingleThreadEventLoop 中:

// SingleThreadEventLoop.java
@Override
public ChannelFuture register(Channel channel) {
    // 创建一个 Promise 对象,用于异步结果的通知
    return register(new DefaultChannelPromise(channel, this));
}

什么是 Promise?

ChannelPromise 是 Netty 对 JDK Future 的增强版本。与普通的 Future 只能读取结果不同,Promise 是可写的,可以主动设置成功或失败的结果。这在异步编程中非常有用。

继续往下看:

// SingleThreadEventLoop.java
@Override
public ChannelFuture register(final ChannelPromise promise) {
    ObjectUtil.checkNotNull(promise, "promise");
    // 关键:通过 Unsafe 进行底层注册操作
    promise.channel().unsafe().register(this, promise);
    return promise;
}

什么是 Unsafe?

Unsafe 是 Channel 的内部接口,封装了所有底层的、不安全的操作。为什么叫 Unsafe?因为这些操作直接与操作系统打交道,使用不当可能导致问题。Netty 把这些操作封装在 Unsafe 中,对外提供更安全的 API。

四、核心注册逻辑:绑定 Channel 与 EventLoop

现在进入到 AbstractChannel 的内部类 AbstractUnsafe 中:

// AbstractChannel.AbstractUnsafe
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    // ... 省略部分代码
    
    // 【重点1】将 EventLoop 绑定到 Channel
    // 从此以后,这个 Channel 的所有异步操作都由这个 EventLoop 负责
    AbstractChannel.this.eventLoop = eventLoop;
    
    // 【重点2】判断当前线程是否是 EventLoop 的线程
    if (eventLoop.inEventLoop()) {
        // 如果是,直接执行注册
        register0(promise);
    } else {
        // 如果不是,将注册任务提交到 EventLoop 的任务队列
        try {
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            logger.warn("注册任务提交失败", t);
            closeForcibly();
            closeFuture.setClosed();
            safeSetFailure(promise, t);
        }
    }
}

这段代码有两个关键点:

4.1 Channel 与 EventLoop 的绑定

AbstractChannel.this.eventLoop = eventLoop;

这行代码完成了 Channel 和 EventLoop 的绑定。从此以后:

  • 这个 Channel 的所有 I/O 操作都由这个 EventLoop 处理
  • 保证了线程安全:同一个 Channel 的操作都在同一个线程中执行

4.2 线程判断与任务提交

if (eventLoop.inEventLoop()) {
    register0(promise);
} else {
    eventLoop.execute(() -> register0(promise));
}

为什么要判断线程?

inEventLoop() 方法判断当前执行代码的线程是否就是 EventLoop 的线程。这个判断很重要:

  • 如果是 EventLoop 线程:说明已经在正确的线程中了,直接执行注册操作
  • 如果不是:需要将注册任务提交到 EventLoop 的任务队列,等待 EventLoop 线程来执行

通常在启动阶段,我们是在主线程中调用 bind() 的,所以会走 else 分支。

接下来我们重点看两个方法:

  1. eventLoop.execute(task) - 如何提交任务并启动线程
  2. register0() - 真正的注册逻辑

五、EventLoop 线程的启动

当我们调用 eventLoop.execute(task) 时,实际执行的是父类 SingleThreadEventExecutor 的方法:

// SingleThreadEventExecutor.java
@Override
public void execute(Runnable task) {
    if (task == null) {
        throw new NullPointerException("task");
    }
    
    // 判断当前线程是否是 EventLoop 线程
    boolean inEventLoop = inEventLoop();
    
    // 将任务添加到任务队列
    addTask(task);
    
    // 如果不是 EventLoop 线程,需要启动 EventLoop 线程
    if (!inEventLoop) {
        startThread();
    }
    
    // 如果需要,唤醒可能阻塞的 EventLoop 线程
    if (!addTaskWakesUp && wakesUpForTask(task)) {
        wakeup(inEventLoop);
    }
}

这个方法的逻辑很清晰:

  1. 添加任务:先把任务放到队列中
  2. 启动线程:如果 EventLoop 线程还没启动,现在启动它
  3. 唤醒线程:如果线程正在阻塞等待 I/O 事件,唤醒它来处理任务

重点:EventLoop 线程是懒加载的,只有在第一次提交任务时才会真正启动。

5.1 线程启动的状态检查

// SingleThreadEventExecutor.java
private void startThread() {
    // 只有在未启动状态才启动
    if (state == ST_NOT_STARTED) {
        // 使用 CAS 保证只有一个线程能启动成功
        if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
            doStartThread();
        }
    }
}

这里使用了 CAS(Compare-And-Set)操作,保证即使多个线程同时调用 execute(),也只会启动一次 EventLoop 线程。

5.2 真正的线程创建

// SingleThreadEventExecutor.java
private void doStartThread() {
    assert thread == null;
    
    // 使用 ThreadPerTaskExecutor 创建新线程
    executor.execute(new Runnable() {
        @Override
        public void run() {
            // 【关键】将新创建的线程保存到 thread 字段
            thread = Thread.currentThread();
            
            try {
                // 【核心】执行 EventLoop 的主循环
                // 这个 run() 方法在 NioEventLoop 中实现
                SingleThreadEventExecutor.this.run();
                success = true;
            } catch (Throwable t) {
                logger.warn("EventLoop 执行异常", t);
            } finally {
                // ... 清理工作
            }
        }
    });
}

这里有几个关键点:

  1. executor 是什么?

    • 它是 ThreadPerTaskExecutor,在创建 NioEventLoop 时传入的
    • 特点:每次调用 execute() 都会创建一个新线程
    • 所以这里会真正创建一个新的 Java 线程
  2. thread 字段的作用

    • 保存 EventLoop 的工作线程引用
    • 后续 inEventLoop() 方法就是通过比较 Thread.currentThread() == thread 来判断的
  3. run() 方法

    • 这是 EventLoop 的主循环,会一直运行直到 EventLoop 关闭
    • 在 NioEventLoop 中实现,负责处理 I/O 事件和任务队列

5.3 EventLoop 的主循环(简要说明)

NioEventLoop.run() 方法是 EventLoop 的核心,它会一直循环执行以下工作:

// NioEventLoop.java
@Override
protected void run() {
    for (;;) {  // 死循环,直到 EventLoop 关闭
        try {
            // ========== 阶段1:选择策略 ==========
            // 判断是否有任务需要处理
            // - 有任务:使用 selectNow()(非阻塞)
            // - 无任务:使用 select()(阻塞等待 I/O 事件)
            switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                case SelectStrategy.SELECT:
                    select(wakenUp.getAndSet(false));
                    if (wakenUp.get()) {
                        selector.wakeup();
                    }
                default:
            }
            
            // ========== 阶段2:处理 I/O 事件和任务 ==========
            final int ioRatio = this.ioRatio;  // 默认 50,表示 I/O 和任务各占 50%
            
            if (ioRatio == 100) {
                // 不限制任务执行时间
                processSelectedKeys();  // 处理 I/O 事件
                runAllTasks();          // 处理任务队列
            } else {
                // 根据 I/O 时间按比例分配任务执行时间
                long ioStartTime = System.nanoTime();
                processSelectedKeys();  // 处理 I/O 事件
                long ioTime = System.nanoTime() - ioStartTime;
                runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
            }
            
            // ========== 阶段3:关闭检查 ==========
            if (isShuttingDown()) {
                closeAll();
                if (confirmShutdown()) {
                    return;  // 退出循环,线程结束
                }
            }
        } catch (Throwable t) {
            handleLoopException(t);
        }
    }
}

EventLoop 主循环做了三件事:

  1. 等待事件:使用 Selector 等待 I/O 事件或检查任务队列
  2. 处理事件:处理 I/O 事件(如连接、读写)和任务队列中的任务
  3. 检查关闭:判断是否需要关闭 EventLoop

ioRatio 参数:控制 I/O 事件处理和任务处理的时间比例,默认 50 表示各占一半。

关于 EventLoop 主循环的详细分析,可以参考 NioEventLoop-run方法完整学习笔记.md

现在 EventLoop 线程已经启动并运行了,它会从任务队列中取出我们之前提交的注册任务并执行。

六、真正的注册:register0()

当 EventLoop 线程从任务队列中取出注册任务后,会执行 register0() 方法:

// AbstractChannel.AbstractUnsafe
private void register0(ChannelPromise promise) {
    try {
        // ... 省略一些检查代码
        
        // 【核心】执行实际的注册操作
        doRegister();
        
        // 标记为已注册
        registered = true;
        
        // 触发 handlerAdded 事件
        pipeline.invokeHandlerAddedIfNeeded();
        
        // 设置 Promise 为成功,通知等待的线程
        safeSetSuccess(promise);
        
        // 触发 channelRegistered 事件
        pipeline.fireChannelRegistered();
        
        // 如果 Channel 已经激活,触发 channelActive 事件
        if (isActive()) {
            if (firstRegistration) {
                pipeline.fireChannelActive();
            } else if (config().isAutoRead()) {
                beginRead();
            }
        }
    } catch (Throwable t) {
        // 注册失败,关闭 Channel
        closeForcibly();
        closeFuture.setClosed();
        safeSetFailure(promise, t);
    }
}

这个方法做了很多事情,但核心是调用 doRegister() 方法。

6.1 底层注册:doRegister()

// AbstractNioChannel
@Override
protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
            // 【关键】调用 JDK NIO 的 Channel.register() 方法
            selectionKey = javaChannel().register(
                eventLoop().unwrappedSelector(),  // 获取 Selector
                0,                                 // 初始不监听任何事件
                this                               // 将 Netty Channel 作为附件
            );
            return;
        } catch (CancelledKeyException e) {
            if (!selected) {
                // 如果注册失败,清理已取消的 key 后重试
                eventLoop().selectNow();
                selected = true;
            } else {
                // 再次失败,抛出异常
                throw e;
            }
        }
    }
}

这段代码的关键点:

  1. javaChannel():获取 Netty Channel 包装的 JDK NIO Channel(如 SocketChannel)

  2. eventLoop().unwrappedSelector():获取 EventLoop 中的 Selector

  3. 第二个参数为 0:表示暂时不监听任何事件

    • 为什么不直接监听 OP_READ 或 OP_ACCEPT?
    • 因为此时 Channel 可能还没完全初始化好(如 Pipeline 还没配置完)
    • 等到 Channel 激活时,会调用 beginRead() 注册感兴趣的事件
  4. 第三个参数 this:将 Netty 的 Channel 作为 attachment 附加到 SelectionKey 上

    • 当 Selector 检测到事件时,可以通过 SelectionKey.attachment() 获取对应的 Netty Channel
    • 这样就建立了 JDK Channel 和 Netty Channel 的关联

6.2 注册后的事件传播

注册成功后,register0() 会触发一系列事件:

  1. pipeline.invokeHandlerAddedIfNeeded()

    • 触发 Pipeline 中所有 Handler 的 handlerAdded() 回调
    • 这是 Handler 初始化的好时机
  2. pipeline.fireChannelRegistered()

    • 触发 channelRegistered 事件
    • 在 Pipeline 中传播,让所有 Handler 知道 Channel 已注册
  3. pipeline.fireChannelActive()(如果 Channel 已激活)

    • 触发 channelActive 事件
    • 对于服务端,这时会开始监听 OP_ACCEPT 事件
    • 对于客户端,这时会开始监听 OP_READ 事件

关于 register0() 的详细分析,可以参考 register0方法详解.md

七、总结

让我们回顾一下整个注册流程:

bind(port) / connect()
    ↓
initAndRegister()
    ↓
NioEventLoopGroup.register(channel)
    ↓
选择一个 NioEventLoop (通过 chooser.next())
    ↓
SingleThreadEventLoop.register(channel)
    ↓
创建 ChannelPromise
    ↓
AbstractUnsafe.register(eventLoop, promise)
    ↓
绑定 Channel 和 EventLoop
    ↓
判断当前线程 (inEventLoop())
    ↓
eventLoop.execute(注册任务)
    ↓
启动 EventLoop 线程 (如果未启动)
    ↓
EventLoop 线程执行 register0()
    ↓
doRegister() - 调用 JDK 的 Channel.register()
    ↓
触发事件:handlerAdded → channelRegistered → channelActive

核心要点:

  1. 线程绑定:每个 Channel 绑定到一个 EventLoop,保证线程安全
  2. 懒加载:EventLoop 线程在第一次提交任务时才启动
  3. 异步执行:注册操作通过任务队列异步执行,避免阻塞主线程
  4. 事件驱动:注册完成后通过事件通知 Pipeline 中的 Handler

通过这个注册流程,Netty 完成了以下关键工作:

  • 将 Channel 注册到 Selector 上,可以监听 I/O 事件
  • 将 Channel 绑定到 EventLoop,后续所有操作都在同一个线程中执行
  • 初始化 Pipeline,准备好处理业务逻辑

这就是 Netty Channel 注册的完整流程。理解了这个流程,你就掌握了 Netty 启动过程中最核心的部分!

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

相关推荐

Netty源码分析系列之新连接的接入

当 netty 的服务端启动以后,就可以开始接收客户端的连接了。那么在 netty 中,服务端是如何来进行新连接的创建的呢?

天堂的博客 384

「源码解读」网络操作抽象类_ Channel

我们只要摸清楚了 fireChannelRegistered() 方法,以后碰到其他像 fireChannelActive()、fireXxx() 等就知道怎么回事了,它们都是类似的。经过前面的铺垫,我们已经具备一定的基础了,我们开始来把前面学到的内容揉在一起。实例,它用于保存 channel 的配置信息,这里没有我们现在需要关心的内容,直接跳过。操作,这一步其实是非常关键的,对于我们源码分析非常重要。这个方法,所以我们上面说的那些,对于两者都是通用的。操作是非常重要的,要知道这一步大概做了哪些事情,

CoderChronicle的博客 1066

S32K144之芯片锁死解锁——关闭debug端口

S32K144之芯片锁死及解锁 经常捣鼓单片机就会发现,时常会因为某些操作不当导致芯片锁死情况的发生,通过Jlink我们可以读到芯片的ID,但是无法进行Debug,这很是让人苦恼。 基于S32K144的芯片锁死,一般有如下几种可能: 1、时钟配置异常,过高的配置时钟频率以及不配置时钟,都有可能造成芯片保护锁死,我就曾经因为不注意把时钟配置代码注释掉,导致芯片锁死。 2、我是微软雅黑烧写断电,如果在...

weixin_41935674的博客 1万+

Netty Channel源码分析

原文:https://wangwei.one/posts/netty-channel-source-analyse.html 前面,我们大致了解了Netty中的几个核心组件。今天我们就来先来介绍Netty的网络通信组件,用于执行网络I/O操作 —— Channel。 Netty版本:4....

chuqiong4119的博客 189

配电网故障重构/孤岛划分/故障恢复

程序主要复现《基于GA_BFGS算法的配电网故障恢复性重构研究_郑海广》,采用matlab编程软件实现,依据网络结构和DG供电方式对配电网进行孤岛划分,将含有 DG 的配电网拓扑结构看成一个树,搭建成连通图模型,使得将孤岛的划分变成了求取最小生成树,以网损和开关开断次数作为目标,采用 IEEE33 节点配电网为算例,通过遗传算法寻优计算,从而得到最佳的重构方案。

zhangxd212489的博客 2500

ESD镜像解密与转换全攻略:从esd-decrypter到WinNTSetup的完整流程

本文详细解析了ESD镜像的加密特性与压缩优势,并提供了从解密到转换的完整实战指南。核心步骤包括使用esd-decrypter工具解密ESD文件为WIM格式,以及如何结合WinNTSetup进行灵活的系统安装或进一步封装为可引导的ISO镜像,帮助用户高效处理官方Windows安装镜像。

weixin_29207439的博客 452

Netty源码分析篇:Netty Pipeline 初始化流程

Netty源码分析篇:Netty Pipeline 初始化流程

java_rutu的博客 657

(超详细)Scala环境搭建(scala-2.12.15、IDEA2021.1.3)

Scala环境搭建(scala-2.12.15、IDEA2021.1.3) JunLeon——go big or go home 目录 Scala环境搭建(scala-2.12.15、IDEA2021.1.3) 一、Scala概述 1、什么是Scala? 2、Scala发展史 3、Scala与Java的关系 二、Scala环境搭建 1、环境准备 2、Scala安装与配置环境变量 3、验证Scala 三、编写第一个Scala程序 1、使用IDEA创建maven工程 2、.

JunLeon的博客 3万+

Netty源码分析--认真系列(一)

Netty源码分析--认真系列(一)

java_rutu的博客 2056

揭秘无人机前沿领域的科技新突破

本文旨在系统性地介绍无人机技术在前沿领域的最新突破,涵盖从基础理论到实际应用的各个方面。我们将重点关注那些正在改变无人机行业面貌的关键技术创新,并分析它们对未来发展的影响。文章首先介绍无人机技术的基础概念和背景,然后深入探讨六个关键领域的技术突破,接着分析实际应用案例,最后展望未来发展趋势。自主导航系统(SLAM):同时定位与地图构建技术,使无人机能在未知环境中自主导航计算机视觉(CV):使无人机能够"看"和理解周围环境的技术5G通信:第五代移动通信技术,提供高速、低延迟的数据传输能源管理。

AI天才研究院 1490

ROS从入门到精通2-1:机器人3D可视化工具——Rviz

本文详细介绍了Rviz是什么?为什么要使用这个工具?清晰地列出了Rviz的安装流程和界面的基本功能元素;讲解了Rviz基本数据类型与话题,并用两个实例深入理解Rviz数据可视化的过程

FRIGIDWINTER的博客 5348

Netty源码分析---register0方法详解

摘要:本文详细解析了Netty中AbstractChannel.register0()方法的实现机制。该方法负责将Channel注册到EventLoop的核心流程,包括Selector注册、状态更新和事件触发。关键点包括:1) 确保在EventLoop线程执行;2) 通过doRegister()完成底层Selector注册;3) 触发handlerAdded和channelRegistered事件;4) 根据首次注册状态决定是否触发channelActive事件。该方法还包含完善的异常处理机制,是Netty

java_rutu的博客 531

YOLO11实战优化:长尾分布解决 | 引入Seesaw Loss配合YOLO11分类头,完美应对类别极其不平衡的现实业务场景

摘要: YOLO11在工业缺陷检测等长尾分布场景中面临尾部类别检测失效的问题,根源在于头部类别样本的负样本梯度压制了尾部类别的学习。传统方法如重采样、Focal Loss等效果有限。Seesaw Loss通过动态调整缓解因子和补偿因子,平衡类别间的梯度分布,结合YOLO11的解耦分类头、Anchor-Free设计和C2PSA/C3K2模块,显著提升尾部类别识别能力。文章详细解析了Seesaw Loss的数学原理,并提供了可直接集成到YOLO11的PyTorch实现代码,为长尾分布下的目标检测提供了实用解决方

2601_95807009的博客 255

Netty(四)_核心源码分析

Netty(四)_核心源码分析 本篇内容门槛还是有的,需要你对我前三篇netty所说的内容不算陌生 为了提高阅读舒适性,我们并不会一行一行代码解析,并删除部分与目的无关的源码。读源码要带着目的性读,最好你已经知道它背后基本原理,然后带着审视的感觉去进行阅读,反客为主。 在整个流程中,以下这张图要常驻脑海中! 我们选择netty提供的example程序,该程序可以在源码包netty.example.echo包 public final class EchoServer { //... s

JunsIr的技术栈 707

【Bug已解决】Claude Code Auto-update failed 自动更新失败的解决方案

方案适用场景推荐指数清理残留重新安装已经损坏,需要快速恢复可用⭐⭐⭐⭐⭐修复目录权限权限不足导致更新反复失败⭐⭐⭐⭐⭐关闭自动更新网络/安全软件环境不稳定⭐⭐⭐切换原生安装方式想从根源规避 npm 更新问题⭐⭐⭐⭐自检修复脚本团队/服务器环境需要自动化恢复⭐⭐⭐报错的本质是自动更新过程中的文件替换操作因权限不足或被中断而处于不完整状态,而不是 Claude Code 本身的功能缺陷。遇到问题先清理残留文件再重新安装,不要在损坏状态上反复尝试修复;

一键难忘的博客 1万+

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Netty源码分析-AbstractUnsafe(register注册流程)

Netty当中的Channel,对外提供的API都封装再了AbstractUnsafe对象里面。 我们分析一个新的客户端连接NioSocketChannel是如何完成注册的。 注册h @Override public ChannelFuture register(final ChannelPromise promise) { ObjectUtil...

大新博客 714

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

【Netty4】Netty服务端启动源码分析(一)

http://ifeve.com/netty-reactor-4/ https://blog.youkuaiyun.com/huanshirenjian/article/details/90228894

m0_45406092的博客 457

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Netty 源码调试

自己学习笔记

weixin_44094842的博客 748

netty源码分析 之七 transport(Unsafe)

来看下unsafe的接口定义 同样看你实现类, AbstractUnsafe 接着看起实现,统一的模板类,由子类来实现方法 接着看register 方法,其中有pipeline.fireChannelRegistered方法 private void register0(ChannelPromise promise) {

akfly的专栏 872

netty源码分析一 (EventLoopGroup源码分析与线程数设定)

public interface EventLoopGroup extends EventExecutorGroup { /** * Return the next {@link EventLoop} to use */ @Override EventLoop next(); /** * Register a {@link Channel} with this {@link EventLoop}...

austin0725的博客 329

【自己读源码】Netty4.X系列(三) Channel Register

Netty源码分析(三) 前提概要 这次停更很久了,原因是中途迷茫了一段时间,不过最近调整过来了。不过有点要说下,前几天和业内某个大佬聊天,收获很多,所以这篇博文和之前也会不太一样,我们会先从如果是我自己去实现这个功能需要怎么做开始,然后去看netty源码,与自己的实现做对比。 Server端NIO复习 Netty有基于很多IO的实现(...

weixin_34221276的博客 235

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

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

Java_Jsp_Ssh的博客 892

netty注册流程

//1. // 2 //3 ChannelFuture regFuture = config().group().register(channel); if (regFuture.cause() != null) { if (channel.isRegistered()) { ...

qq_38393471的博客 574

认真的 Netty 源码解析(二)

Channel 的 register 操作经过前面的铺垫,我们已经具备一定的基础了,我们开始来把前面学到的内容揉在一起。这节,我们会介绍 register 操作,这一步其...

JAVA高级架构 619

Netty源码分析之服务端启动

Netty服务端启动代码: public final class EchoServer { static final int PORT = Integer.parseInt(System.getProperty("port", "8007")); public static void main(String[] args) throws Exception { ...

weixin_30756499的博客 174

netty源码分析(十六)Channel选择器工厂与轮询算法及注册底层实现

上一节说到注册的入口,即 MultithreadEventLoopGroup: public ChannelFuture register(Channel channel) { return next().register(channel); }注册channel第一步调用了next()方法,next()是MultithreadEventLoopGroup里边的:

wzq6578702的专栏 1522
上一篇: Netty源码分析篇:NioEventLoopGroup分析
下一篇: ChannelHandler深度解析-核心机制与工作原理
不早睡不改名@
博客等级 码龄5年 536粉丝 306原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值