Netty源码分析---编码器深度解析-简约版

Redisson全面解析:从使用方法到工作原理的深度探索 Redisson是基于原生redis操作指令上进一步的封装,屏蔽了redis数据结构的实现细节,开发可以像操作普通java对象一样使用redis,而本文将针对Redisson中各种使用的数据结构和工具包使用及其实现进行详尽的分析,希望对你有帮助。因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注。因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注。 阅读详情

Netty编码器深度解析

一、为什么需要编码器?

编码器和解码器是一对相反的操作。解码器是把网络传输过来的字节流转换成我们的Java对象,那编码器就是把Java对象转换成字节流发送到网络。

解码器:字节流 → Java对象(入站)
编码器:Java对象 → 字节流(出站)

在网络通信中,所有数据最终都要以字节的形式传输,编码器就是负责这个转换工作的。


二、MessageToByteEncoder 深度解析

从名字就能看出来,MessageToByteEncoder是将消息对象(Message)编码为字节(Byte)的编码器,也就是说它的输入是我们自定义的消息对象,输出是ByteBuf。这是最基础也是最常用的编码器类型。

2.1 核心工作流程

当我们调用ctx.write(msg)发送消息时,编码器的工作流程是这样的:

public abstract class MessageToByteEncoder<I> extends ChannelOutboundHandlerAdapter {
    
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
        ByteBuf buf = null;
        try {
            // 1. 检查消息类型是否匹配
            if (acceptOutboundMessage(msg)) {
                I cast = (I) msg;
                
                // 2. 分配ByteBuf
                buf = allocateBuffer(ctx, cast, preferDirect);
                
                try {
                    // 3. 调用encode方法编码
                    encode(ctx, cast, buf);
                } finally {
                    // 4. 释放原始消息
                    ReferenceCountUtil.release(cast);
                }
                
                // 5. 传递编码后的ByteBuf
                if (buf.isReadable()) {
                    ctx.write(buf, promise);
                } else {
                    buf.release();
                    ctx.write(Unpooled.EMPTY_BUFFER, promise);
                }
                buf = null;
            } else {
                // 类型不匹配,直接传递
                ctx.write(msg, promise);
            }
        } finally {
            if (buf != null) {
                buf.release();
            }
        }
    }
    
    protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;
}

整个流程很清晰:检查类型 -> 分配缓冲区 -> 编码 -> 释放原消息 -> 传递ByteBuf。

2.2 类型匹配机制

编码器通过泛型来指定它能处理的消息类型:

public class IntegerEncoder extends MessageToByteEncoder<Integer> {
    @Override
    protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) {
        out.writeInt(msg);
    }
}

// 使用时
ctx.write(123);        // ✓ 匹配,会调用encode()
ctx.write("Hello");    // ✗ 不匹配,直接传递给下一个Handler

这个机制和解码器的类型匹配是一样的,都是通过泛型来判断。

2.3 ByteBuf的分配

编码器会自动分配ByteBuf,默认使用直接内存:

protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, I msg, boolean preferDirect) {
    return preferDirect ? 
           ctx.alloc().ioBuffer() :    // 直接内存
           ctx.alloc().heapBuffer();   // 堆内存
}

直接内存适合网络传输,因为可以零拷贝直接发送到网络,而堆内存需要先拷贝到直接内存才能发送。所以默认使用直接内存是合理的。

2.4 简单示例

我们来看一个简单的例子,将Integer编码成4个字节:

public class IntegerEncoder extends MessageToByteEncoder<Integer> {
    @Override
    protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) {
        out.writeInt(msg);
    }
}

数据流演示:

输入: Integer 值 = 258 (0x00000102)

encode() 执行:
  out.writeInt(258)
  
ByteBuf 状态:
  索引:     0     1     2     3
         ┌─────┬─────┬─────┬─────┐
  数据:  │ 00  │ 00  │ 01  │ 02  │
         └─────┴─────┴─────┴─────┘
         
输出: ByteBuf [0x00, 0x00, 0x01, 0x02]

2.5 自定义协议编码器

假设我们要实现这个协议:[长度(4字节)][类型(1字节)][数据(N字节)]

public class CustomProtocolEncoder extends MessageToByteEncoder<CustomMessage> {
    
    @Override
    protected void encode(ChannelHandlerContext ctx, CustomMessage msg, ByteBuf out) {
        byte[] data = msg.getData();
        byte type = msg.getType();
        
        // 写入长度
        out.writeInt(data.length);
        
        // 写入类型
        out.writeByte(type);
        
        // 写入数据
        out.writeBytes(data);
    }
}

数据流演示:

输入: CustomMessage{type=1, data="Hello"}

encode() 执行过程:
  1. data.length = 5
  2. type = 0x01
  3. data = [0x48, 0x65, 0x6C, 0x6C, 0x6F]

编码后的 ByteBuf:
索引:     0     1     2     3     4     5     6     7     8     9
       ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
数据:  │ 00  │ 00  │ 00  │ 05  │ 01  │ 48  │ 65  │ 6C  │ 6C  │ 6F  │
       └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
        └────────长度=5────────┘ └类型┘ └──────"Hello"──────┘

三、MessageToMessageEncoder 深度解析

从名字可以看出,MessageToMessageEncoder是将一种消息对象(Message)转换为另一种消息对象(Message)的编码器。它的输入和输出都是消息对象,而不是字节流。这个编码器通常用在第一层编码,也就是在MessageToByteEncoder之前,对业务对象做预处理。

3.1 与 MessageToByteEncoder 的区别

MessageToByteEncoder:  消息对象 → ByteBuf
MessageToMessageEncoder: 消息对象A → 消息对象B

可以看到,MessageToByteEncoder的输出是字节流,而MessageToMessageEncoder的输出还是消息对象,需要后续的编码器继续处理。

3.2 典型应用场景

最常见的场景是多层编码,比如先把业务对象转成协议对象,再把协议对象转成字节流:

// 第一层:业务对象 → 协议对象
public class UserToProtocolEncoder extends MessageToMessageEncoder<User> {
    @Override
    protected void encode(ChannelHandlerContext ctx, User user, List<Object> out) {
        ProtocolMessage msg = new ProtocolMessage();
        msg.setType(MessageType.USER_INFO);
        msg.setData(user.toBytes());
        out.add(msg);
    }
}

// 第二层:协议对象 → ByteBuf
public class ProtocolToByteEncoder extends MessageToByteEncoder<ProtocolMessage> {
    @Override
    protected void encode(ChannelHandlerContext ctx, ProtocolMessage msg, ByteBuf out) {
        out.writeInt(msg.getType());
        out.writeBytes(msg.getData());
    }
}

// Pipeline 配置
pipeline.addLast(new UserToProtocolEncoder());
pipeline.addLast(new ProtocolToByteEncoder());

数据流:

User对象
  ↓ UserToProtocolEncoder
ProtocolMessage对象
  ↓ ProtocolToByteEncoder
ByteBuf
  ↓
网络传输

3.3 一对多编码

MessageToMessageEncoder还有一个特点,就是可以一对多编码,一个输入可以产生多个输出:

public class MessageSplitEncoder extends MessageToMessageEncoder<LargeMessage> {
    
    private static final int MAX_SIZE = 1024;
    
    @Override
    protected void encode(ChannelHandlerContext ctx, LargeMessage msg, List<Object> out) {
        byte[] data = msg.getData();
        
        // 将大消息拆分成多个小消息
        for (int i = 0; i < data.length; i += MAX_SIZE) {
            int length = Math.min(MAX_SIZE, data.length - i);
            byte[] chunk = new byte[length];
            System.arraycopy(data, i, chunk, 0, length);
            
            SmallMessage small = new SmallMessage();
            small.setData(chunk);
            small.setSequence(i / MAX_SIZE);
            
            out.add(small);  // 添加多个输出
        }
    }
}

数据流演示:

输入: LargeMessage (3000字节)

encode() 执行:
  拆分成3个 SmallMessage
  - SmallMessage[0]: 1024字节
  - SmallMessage[1]: 1024字节
  - SmallMessage[2]: 952字节

输出: List<SmallMessage> (3个对象)

Pipeline 后续处理:
  每个 SmallMessage 会被单独传递给下一个Handler

四、编码器的内存管理

4.1 自动释放机制

编码器会自动释放原始消息,避免内存泄漏:

try {
    encode(ctx, cast, buf);
} finally {
    // 自动释放原始消息
    ReferenceCountUtil.release(cast);
}

这个机制和解码器是一样的,编码完成后,原始消息就没用了,需要释放掉。

4.2 何时需要 retain()?

如果你在encode方法中需要异步处理消息,就需要手动retain:

// ❌ 错误:异步处理会导致问题
@Override
protected void encode(ChannelHandlerContext ctx, CustomMessage msg, ByteBuf out) {
    executor.submit(() -> {
        // 此时 msg 可能已经被释放!
        byte[] data = msg.getData();  // 可能抛出异常
        out.writeBytes(data);
    });
}

// ✅ 正确:同步处理,不需要 retain
@Override
protected void encode(ChannelHandlerContext ctx, CustomMessage msg, ByteBuf out) {
    byte[] data = msg.getData();
    out.writeBytes(data);
}

一般情况下,encode方法都是同步执行的,不需要考虑retain的问题。


五、编码器与解码器的配合使用

5.1 完整的编解码链

编码器和解码器通常成对出现,一个负责编码,一个负责解码:

// 服务端 Pipeline
pipeline.addLast("decoder", new CustomProtocolDecoder());  // 入站
pipeline.addLast("encoder", new CustomProtocolEncoder());  // 出站
pipeline.addLast("handler", new BusinessHandler());

// 客户端 Pipeline
pipeline.addLast("encoder", new CustomProtocolEncoder());  // 出站
pipeline.addLast("decoder", new CustomProtocolDecoder());  // 入站
pipeline.addLast("handler", new ClientHandler());

数据流向:

客户端发送:
  User对象
    ↓ CustomProtocolEncoder (出站)
  ByteBuf
    ↓ 网络传输
  ByteBuf
    ↓ CustomProtocolDecoder (入站)
  User对象
    ↓ BusinessHandler

服务端响应:
  Response对象
    ↓ CustomProtocolEncoder (出站)
  ByteBuf
    ↓ 网络传输
  ByteBuf
    ↓ CustomProtocolDecoder (入站)
  Response对象
    ↓ ClientHandler

5.2 对称的编解码器实现

编码器和解码器的实现应该是对称的,编码器怎么写,解码器就怎么读:

// 编码器
public class MessageEncoder extends MessageToByteEncoder<Message> {
    @Override
    protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) {
        byte[] data = msg.getData();
        out.writeInt(data.length);  // 写入长度
        out.writeByte(msg.getType());  // 写入类型
        out.writeBytes(data);  // 写入数据
    }
}

// 解码器(对称实现)
public class MessageDecoder extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        if (in.readableBytes() < 5) return;
        
        in.markReaderIndex();
        int length = in.readInt();  // 读取长度
        byte type = in.readByte();  // 读取类型
        
        if (in.readableBytes() < length) {
            in.resetReaderIndex();
            return;
        }
        
        byte[] data = new byte[length];
        in.readBytes(data);  // 读取数据
        
        Message msg = new Message();
        msg.setType(type);
        msg.setData(data);
        out.add(msg);
    }
}

可以看到,编码器写什么,解码器就读什么,顺序完全一致。


六、常见问题和最佳实践

6.1 问题1:忘记写入数据

// ❌ 错误示例
@Override
protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) {
    // 什么都不做
}

// 结果:buf.isReadable() = false,会发送空缓冲区

// ✅ 正确做法
@Override
protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) {
    out.writeInt(msg);  // 必须写入数据
}

6.2 问题2:编码器顺序错误

// ❌ 错误的 Pipeline 配置
pipeline.addLast(new StringEncoder());  // 期望 String
pipeline.addLast(new IntegerToStringEncoder());  // 输出 String

// 问题:出站Handler的执行顺序是从后往前!
// 实际执行顺序:IntegerToStringEncoder → StringEncoder
// 但 IntegerToStringEncoder 在后面,它的输出无法被 StringEncoder 处理

// ✅ 正确的配置
pipeline.addLast(new IntegerToStringEncoder());  // Integer → String
pipeline.addLast(new StringEncoder());  // String → ByteBuf

// 执行顺序:StringEncoder → IntegerToStringEncoder
// 但因为类型匹配,实际是:
// 1. IntegerToStringEncoder 处理 Integer,输出 String
// 2. StringEncoder 处理 String,输出 ByteBuf

记住:出站Handler的执行顺序是从后往前,但因为类型匹配机制,实际上每个Handler只处理它能处理的类型。

6.3 问题3:ByteBuf 泄漏

// ❌ 错误示例
@Override
protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) {
    ByteBuf temp = ctx.alloc().buffer();
    temp.writeInt(msg);
    out.writeBytes(temp);
    // 忘记释放 temp!
}

// ✅ 正确做法
@Override
protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) {
    ByteBuf temp = ctx.alloc().buffer();
    try {
        temp.writeInt(msg);
        out.writeBytes(temp);
    } finally {
        temp.release();  // 释放临时缓冲区
    }
}

// ✅ 更好的做法:直接写入,不需要临时缓冲区
@Override
protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) {
    out.writeInt(msg);
}

七、Netty 内置编码器

Netty提供了一些常用的编码器,可以直接使用:

// 1. 字符串编码器
pipeline.addLast(new StringEncoder(StandardCharsets.UTF_8));

// 2. Base64 编码器
pipeline.addLast(new Base64Encoder());

// 3. 长度字段编码器(自动添加长度前缀)
pipeline.addLast(new LengthFieldPrepender(4));

// 4. 对象序列化编码器
pipeline.addLast(new ObjectEncoder());

LengthFieldPrepender特别有用,它会自动在数据前面添加长度字段:

// 配置
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast(new StringEncoder());

// 发送
ctx.write("Hello");

// 编码过程:
// 1. StringEncoder: "Hello" → [0x48, 0x65, 0x6C, 0x6C, 0x6F]
// 2. LengthFieldPrepender: 添加长度 → [0x00, 0x00, 0x00, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F]

八、总结

8.1 编码器选择指南

三种编码器的核心区别在于它们处理的数据类型不同:

  • MessageToByteEncoder:消息对象 → 字节流,这是最基础的编码器,直接将对象编码成网络可传输的字节数据
  • MessageToMessageEncoder:消息对象 → 消息对象,用于对业务对象做预处理,比如将User对象转成ProtocolMessage对象
场景推荐编码器原因
对象 → 字节MessageToByteEncoder直接编码为网络数据
对象 → 对象MessageToMessageEncoder协议转换、数据转换
字符串发送StringEncoderNetty内置,开箱即用
自定义协议MessageToByteEncoder灵活控制字节格式

8.2 核心要点

  1. 编码器处理出站数据,继承 ChannelOutboundHandlerAdapter
  2. 自动释放原始消息,避免内存泄漏
  3. preferDirect 控制内存类型,默认使用直接内存
  4. MessageToMessageEncoder 可以一对多,一个输入产生多个输出
  5. 注意 Pipeline 顺序,出站Handler从后往前执行,但类型匹配机制会确保正确的处理顺序

8.3 与解码器的对比

特性解码器编码器
数据方向入站(Inbound)出站(Outbound)
基类ChannelInboundHandlerAdapterChannelOutboundHandlerAdapter
核心方法channelRead()write()
累积机制有(cumulation)无
典型用途字节 → 对象对象 → 字节
SpringBoot+Vue+MySQL 高校学科竞赛平台 完整可运行版(源码+文档) 本项目基于SpringBoot+Vue+MySQL构建高校学科竞赛管理平台,覆盖学生、教师、领队教师、管理员四大角色,实现竞赛发布、报名、刷题、评分、公示等全流程闭环管理。系统架构清晰,技术主流,配套完整源码、数据库脚本、万字论文、开题报告、答辩PPT及部署文档,支持一键运行,适用于毕业设计、课程设计及院系二次开发,真正实现“开箱即用”。 阅读详情

相关推荐

从NVLINK到EthLink:GPU互联技术演进全解析(含最新行业趋势)

本文深入解析了从NVLINK到EthLink的GPU互联技术演进,重点剖析了Scale-up与Scale-out两种核心范式。文章详细对比了NVLINK、UALINK及革命性的EthLink技术,后者创新性地将以太网改造为支持Load/Store与RDMA双语义的高性能互联网络,旨在打破封闭生态,实现高性能与开放性的统一。最后,文章提供了技术选型指南并展望了未来以以太网为主导、语义融合的行业趋势。

weixin_29204749的博客 179

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

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

java_rutu的博客 2056

QT+SOEM主站协议库实战指南—从编译到嵌入式应用

本文提供了一份QT与SOEM开源EtherCAT主站协议库的集成实战指南。详细介绍了在Windows和Linux平台下编译SOEM库的步骤,并讲解了如何将其无缝集成到QT项目中,构建具备实时监控与控制功能的工业上位机应用。内容涵盖从环境搭建、核心协议开发到嵌入式部署与优化的全流程,为开发高性价比、自主可控的工业控制解决方案提供完整路径。

u6v7w8x的博客 442

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

本文深入分析Netty的事件处理机制,重点讲解入站事件和出站事件的传播流程。主要内容包括:1) 入站事件处理流程,从NioEventLoop检测事件开始,通过Unsafe对象触发channelRead事件,最终在Pipeline中从head向tail传播;2) 详细解析executionMask位掩码机制,该机制高效记录Handler实现的方法;3) 介绍常见入站事件类型及其触发时机,如channelRegistered、channelActive等。文章通过源码分析展示了Netty高效的事件处理架构。

java_rutu的博客 1315

Adams与Matlab联合仿真入门:从零搭建你的第一个动力学模型

本文为Adams与Matlab联合仿真新手提供了一份详尽的入门指南。文章从软件环境配置、版本兼容性检查讲起,逐步演示了如何在Adams中构建单摆动力学模型并定义输入输出接口,以及在Matlab/Simulink中搭建控制回路。通过一个完整的受控单摆实例,手把手教你完成从模型导出、模块生成到执行联合仿真的全流程,帮助你快速搭建并运行第一个联合仿真模型,实现动力学与控制系统的协同验证。

g5h6i7j的博客 700

Stata实战:DID平行趋势检验完整操作指南(附reghdfe+coefplot代码)

本文提供了一份使用Stata进行DID平行趋势检验的完整实战指南。文章详细讲解了从数据准备、生成相对时间变量,到利用reghdfe命令构建动态效应模型,以及使用coefplot命令进行结果可视化的全流程操作。核心在于通过检验政策前交互项的显著性,验证处理组与对照组是否满足平行趋势假设,为后续因果推断奠定可靠基础。

weixin_29231725的博客 1382

Netty 4.2.x 源码深度解析 (二):FastThreadLocal —— Netty 自研线程局部变量

本文针对 JDK ThreadLocal 哈希冲突、内存泄漏、计算开销大等痛点,解析 Netty 4.2.x 自研的 FastThreadLocal 实现。其通过全局递增索引 + 数组存储实现无冲突 O (1) 存取,依托 InternalThreadLocalMap 完成数据读写与扩容。借助 FastThreadLocalThread 实现快速访问路径,搭配 FastThreadLocalRunnable 自动清理变量,规避线程池内存泄漏,同时适配虚拟线程,兼顾高性能与内存安全。

于云原生深处,追寻架构与代码的优雅 517

petalinux2022.2启动文件编译配置

petalinux2021之前的版本里,导入目录components/yocto/下的“environment-setup-aarch64-xilinx-linux” 和 “layers/core/oe-init-build-env”的环境,能使用Yocto的devtool工具。(加入下面内容,先petalinux-build下载完成后才能有源码)制作 BOOT.BIN 启动文件:petalinux-package --boot --fsbl --fpga --u-boot --force。

wangjie36的博客 1395

Netty编码器原理与高性能优化实战

网络编程中的编码器(Encoder)是实现应用层协议与二进制数据转换的关键组件。其核心原理是通过事件驱动机制,将业务对象高效序列化为网络字节流。在Java生态中,Netty框架提供的编码器抽象通过内存池化和零拷贝技术,显著提升了网络通信性能。典型应用场景包括金融交易系统、物联网设备通信等对吞吐量要求苛刻的领域。本文以Netty编码器为例,深入解析其线程模型、内存管理机制,并分享通过CompositeByteBuf实现零拷贝、利用FileRegion处理大文件等工程优化技巧。针对生产环境中常见的内存泄漏和性能

weixin_29251567的博客 160

Python 爬虫实战:爬取评论数据做情感分析

摘要:本文介绍基于Python的京东商品评论爬取与情感分析全流程实现。通过Requests库抓取评论数据接口,结合JSON解析获取结构化评论信息。利用SnowNLP进行中文文本情感分析,量化用户评价的情感倾向(正面/负面/中性)。结果显示正面评论占比66.3%,负面评论13.3%,关键词分析发现"卡顿""发热"是主要负面因素。该方法可应用于产品优化、品牌管理等场景,为决策提供数据支持。

2503_91057718的博客 1709

11、基于多阵列电极的表面肌电信号运动估计

本文介绍了一种基于多阵列电极的表面肌电信号运动估计技术,旨在实现对手腕和手指运动的高精度估计,解决传统人机接口在操控多自由度机器人时的局限性。通过多阵列电极系统采集前臂肌肉活动信号,结合独立成分分析(ICA)和非负矩阵分解(NMF)等信号处理方法,提取肌肉协同作用,进而生成运动指令以控制机器人或假肢手。文章对比了多阵列电极与传统电极的优势,展示了运动估计算法的整体流程,并以假肢手控制为例说明其实际应用。最后探讨了该技术在测量精度、应用拓展、智能化和便携化等方面的未来发展方向。该技术为假肢控制、康复训练和人机

ik678901234的博客 87

新范式源码平台source-code-hunter:Netty协议栈设计与实现

在当今高并发、低延迟的网络应用场景中,传统IO模型已无法满足性能需求。Netty作为业界领先的异步事件驱动网络应用框架,其协议栈设计直接决定了网络通信的性能、可靠性和扩展性。本文将深入剖析Netty协议栈的核心设计理念与实现机制,帮助开发者从源码层面理解这一革命性框架。 通过本文,您将获得: - Netty三层架构设计的深度解析 - 协议栈编解码机制的核心原理 - 高性能网络通信的实现细节 - ...

gitblog_00700的博客 480

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

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

【RocketMQ源码深度解析2】源码目录结构介绍&Remoting通信层

源码目录结构介绍&amp;Remoting通信层一:源码目录结构介绍RocketMQ源码分为以下几个package:rocketmq-broker:整个mq的核心,他能够接受producer和consumer的请求,并调用store层服务对消息进行处理。HA服务的基本单元,支持同步双写,异步双写等模式。rocketmq-clien::mq客户端实现,目前官方仅仅开源了java版本的mq客户端,c+...

信马堂 463

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Netty源码解析与高性能网络编程实践

Reactor模式是现代高性能网络框架的核心架构,通过事件驱动和异步非阻塞I/O实现高并发处理。Netty作为Java生态中最成熟的Reactor实现,其线程模型采用多EventLoop协同工作,配合零拷贝技术显著提升吞吐量。内存池化设计通过PooledByteBufAllocator减少GC压力,而CompositeByteBuf实现协议数据的零拷贝组装。这些优化使Netty在游戏服务器、金融交易等延迟敏感场景表现优异。本文通过EventLoop调度机制和ByteBuf内存管理的源码级分析,揭示如何避免常

weixin_32932149的博客 202

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Dubbo笔记 ㉓ :Dubbo协议与网络传输

通过之前的阅读,我们发现,无论是消费者还是生产者,在建立Netty 连接时都会注册 编码器和解码器Handler,用于编码和解码数据。简单来说,数据在网络上传播是按照某种格式传播,而这里的编码和解码就是在发送数据前将数据转化为规定的格式再发送,在接收数据时将数据从规定格式转换为我们可接收的格式。

猫吻鱼的博客 1924

LVGL Table 控件实战指南:从基础表格到轻量级列表(X-TRACK 模拟器源码深度解析)

本指南以 X-TRACK 仓库内置的 LVGL(v8.3.0)源码包中 `lvgl/examples/widgets/table` 为例,系统讲解 LVGL Table 控件的完整用法:从用表格展示二维数据("Simple table" 示例),到把表格改造成内存占用极低的 200 项滚动列表("Lightweighted list from table" 示例)。读完本文,你将掌握 Table

gitblog_00337的博客 472

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 298

信创环境下PageOffice文档控件适配与实践

信创适配做到文档控件这一层,拼的已经不是"有没有国产版本",而是同一个功能在两类终端上是否真的等价。等价的标准很具体:同一次点击能得到同样的结果,同一份公文能得到同样的版式,同一个项目不需要维护两套代码。这背后是三件并不轻松的工作:服务端用一套接口同时支撑两类终端,客户端把两种系统与四种芯片的安装包分发清楚,文档内核层由产品侧跟着 WPS 的版本走。浏览器这一层的压力,则被收进了产品自己的窗口里——升级节奏由产品掌握,不再交给运气。

zi_wu_xian的专栏 327

第三篇 第 6 课

前置:第三篇第 1 课(stdx 引入),第二篇第 1 课(std.regex)。环境:仓颉 SDK 1.2.0。本文所有代码与输出均逐行实测。

weixin_41618057的博客 516

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 351

【从零理解 Spring AI · 12】MCP 的两种角色:做客户端还是做服务端

上一篇讲了 MCP 的概念:标准协议、三大角色、M+N 替代 M×N。

nice_lcj520的博客 541

Java 集合容器高频面试题全解:HashMap 树化阈值、1.7 死循环与 ConcurrentHashMap 演进(2026 招聘 JD 真题复盘)

HashMap 树化阈值 8 的泊松分布依据、退化阈值 6 的防抖设计、JDK 1.7 头插成环死循环的排查与修复、1.8 尾插后为何仍线程不安全、ConcurrentHashMap 放弃分段锁的原因与 ForwardingNode 协作扩容、computeIfAbsent 死锁陷阱、大集合找重复的场景题选型,附三层追问链自测。题目来源为 38 期公开招聘 JD 样本与字节、拼多多、建行、美团、快手公开面经。

蒙眼过河 420

spdlog 同步日志库使用详解

spdlog是一个轻量级 C++ 日志库,支持同步日志、异步日志、多线程日志、日志文件轮转和自定义日志格式。对于嵌入式 Linux 应用开发,我建议先使用。它可以帮我们完成:日志级别过滤。时间戳和线程 ID 输出。日志格式化。终端和文件输出。多线程安全写入。日志文件按大小或时间轮转。我们不需要自己实现互斥锁、文件写入、日志格式化和轮转等底层功能。

zengjiajun2006的博客 1120

JavaSE通关手册:数据类型与变量

代码无需额外处理,编译时编译器自动完成转换。

xys_678的博客 221

Java 多态 学习笔记

多态是面向对象编程的重要特性,指同一操作作用于不同对象时产生不同行为。其核心在于“父类引用指向子类对象”,结合方法重写实现运行时动态绑定。多态使代码更灵活、可扩展,如用Person p = new Student();统一处理各类人员注册,无需修改调用逻辑。但多态无法直接调用子类特有方法,需通过instanceof判断并强制转换。编译时看左边(类型),运行时看右边(实际对象),实现解耦与维护便利。

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

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值