Netty 零拷贝(三)Netty 对零拷贝的改进
Netty 零拷贝(三)Netty 对零拷贝的改进
Netty 之美系列目录 (https://www.cnblogs.com/binarylei/p/10117436.html)
相关文章:
Netty 的“零拷贝”主要体现以下几个方面:
Netty 的接收和发送 ByteBuffer 采用 DIRECT BUFFERS,使用堆外直接内存进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行 Socket 读写,JVM 会将堆内存 Buffer 拷贝一份到直接内存中,然后才写入 Socket 中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
Netty 提供了 CompositeByteBuf 类,它可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf,避免了传统通过内存拷贝的方式将几个小 Buffer 合并成一个大的 Buffer。
通过 FileRegion 包装的 FileChannel.tranferTo 方法 实现文件传输,可以直接将文件缓冲区的数据发送到目标 Channel,避免了传统通过循环 write 方式导致的内存拷贝问题。
通过 wrap 操作,我们可以将 byte[] 数组、ByteBuf、ByteBuffer 等包装成一个 Netty ByteBuf 对象,进而避免了拷贝操作
一、直接缓冲区的应用
Netty 中使用直接缓冲区来读写数据,首先看一下 read 方法中缓冲区的创建方法。
// AbstractNioByteChannel.NioByteUnsafe#read
public final void read() {
final ChannelConfig config = config();
final ByteBufAllocator allocator = config.getAllocator();
final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
ByteBuf byteBuf = allocHandle.allocate(allocator);
// 省略...
}
Netty 中接收缓冲区 ByteBuffer 由 ChannelConfig 分配,而 ChannelConfig 创建 ByteBufAllocator 默认使用 Direct Buffer,这就避免了读写数据的二次内存拷贝问题,从而实现了读写 Socket 的零拷贝功能。
// ByteBufAllocator 用于分配缓冲区
private volatile ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
// RecvByteBufAllocator 可伸缩的缓冲区,根据每次接收的数据大小自动分配缓冲区大小
private volatile RecvByteBufAllocator rcvBufAllocator;
public DefaultChannelConfig(Channel channel) {
this(channel, new AdaptiveRecvByteBufAllocator());
}
AdaptiveRecvByteBufAllocator 继承自 DefaultMaxMessagesRecvByteBufAllocator,在 allocate 分配缓冲区。
// DefaultMaxMessagesRecvByteBufAllocator.MaxMessageHandle.allocate
public ByteBuf allocate(ByteBufAllocator alloc) {
return alloc.ioBuffer(guess());
}
// AbstractByteBufAllocator
public ByteBuf ioBuffer(int initialCapacity) {
if (PlatformDependent.hasUnsafe()) {
return directBuffer(initialCapacity);
}
return heapBuffer(initialCapacity);
}
PlatformDependent.hasUnsafe() 通过判断能否加载到 sun.misc.Unsafe 类就使用直接缓冲区,正常情况下返回 true
private static final boolean HAS_UNSAFE = hasUnsafe0();
public static boolean hasUnsafe() {
return HAS_UNSAFE;
}
// 默认如果能通过反射获取 sun.misc.Unsafe 实例则使用直接缓冲区,因为直接缓冲区底层就是使用 Unsafe 类
private static boolean hasUnsafe0() {
// 1. 加载 android.app.Application 类则返回 true
if (isAndroid()) {
return false;
}
// 2. -Dio.netty.noUnsafe=true
if (PlatformDependent0.isExplicitNoUnsafe()) {
return false;
}
// 3. 通过反射获取 sun.misc.Unsafe 类。Unsafe.class.getDeclaredField("theUnsafe")
try {
boolean hasUnsafe = PlatformDependent0.hasUnsafe();
logger.debug("sun.misc.Unsafe: {}", hasUnsafe ? "available" : "unavailable");
return hasUnsafe;
} catch (Throwable t) {
logger.trace("Could not determine if Unsafe is available", t);
return false;
}
}
我们再分析一直 ByteBufAllocator allocator = ByteBufAllocator.DEFAULT 中的默认 ByteBufAllocator。
ByteBufAllocator DEFAULT = ByteBufUtil.DEFAULT_ALLOCATOR;
// ByteBufUtil,主要判断是否将内存池化,因为直接缓冲区的分配和销毁开销比较大
static final ByteBufAllocator DEFAULT_ALLOCATOR;
static {
String allocType = SystemPropertyUtil.get(
"io.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled");
allocType = allocType.toLowerCase(Locale.US).trim();
ByteBufAllocator alloc;
if ("unpooled".equals(allocType)) {
alloc = UnpooledByteBufAllocator.DEFAULT;
logger.debug("-Dio.netty.allocator.type: {}", allocType);
} else if ("pooled".equals(allocType)) {
alloc = PooledByteBufAllocator.DEFAULT;
logger.debug("-Dio.netty.allocator.type: {}", allocType);
} else {
alloc = PooledByteBufAllocator.DEFAULT;
logger.debug("-Dio.netty.allocator.type: pooled (unknown: {})", allocType);
}
DEFAULT_ALLOCATOR = alloc;
}
// 除了 HAS_UNSAFE 外还需要判断 io.netty.noPreferDirect 属性
public static final PooledByteBufAllocator DEFAULT =
new PooledByteBufAllocator(PlatformDependent.directBufferPreferred());
public static final UnpooledByteBufAllocator DEFAULT =
new UnpooledByteBufAllocator(PlatformDependent.directBufferPreferred());
public static boolean directBufferPreferred() {
return DIRECT_BUFFER_PREFERRED;
}
private static final boolean DIRECT_BUFFER_PREFERRED =
HAS_UNSAFE && !SystemPropertyUtil.getBoolean("io.netty.noPreferDirect", false);
二、CompositeByteBuf
//定义两个ByteBuf类型的 body 和 header
CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer();
compositeByteBuf.addComponents(true, header, body);
addComponents 方法将 header 与 body 合并为一个逻辑上的 ByteBuf, 这两个 ByteBuf 在 CompositeByteBuf 内部都是单独存在的, CompositeByteBuf 只是逻辑上是一个整体。
三、通过 FileRegion 实现零拷贝
利用 nio 提供的 transferTo 实现零拷贝。
srcFileChannel.transferTo(position, count, destFileChannel);
四、通过 wrap / slice 实现零拷贝
// wrappedBuffer 和 slice 都是共享同一内存,并没有拷贝
ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);
ByteBuf header = byteBuf.slice(0, 5);
ByteBuf body = byteBuf.slice(5, 10);
每天用心记录一点点。内容也许不重要,但习惯很重要!
Netty 零拷贝(三)Netty 对零拷贝的改进的更多相关文章
- Netty学习笔记(三)——netty源码剖析
1.Netty启动源码剖析 启动类: public class NettyNioServer { public static void main(String[] args) throws Excep ...
- Netty 零拷贝(一)NIO 对零拷贝的支持
Netty 零拷贝(二)NIO 对零拷贝的支持 Netty 系列目录 (https://www.cnblogs.com/binarylei/p/10117436.html) 非直接缓冲区(HeapBy ...
- Netty 零拷贝(一)Linux 零拷贝
Netty 零拷贝(一)Linux 零拷贝 本文探讨 Linux 中主要的几种零拷贝技术以及零拷贝技术适用的场景. 一.几个重要的概念 1.1 用户空间与内核空间 操作系统的核心是内核,独立于普通的应 ...
- Java后端进阶-网络编程(Netty零拷贝机制)
package com.study.hc.net.netty.demo; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled ...
- Netty入门(三)之web服务器
Netty入门(三)之web服务器 阅读前请参考 Netty入门(一)之webSocket聊天室 Netty入门(二)之PC聊天室 有了前两篇的使用基础,学习本文也很简单!只需要在前两文的基础上稍微改 ...
- Netty中的三种Reactor(反应堆)
目录: Reactor(反应堆)和Proactor(前摄器) <I/O模型之三:两种高性能 I/O 设计模式 Reactor 和 Proactor> <[转]第8章 前摄器(Proa ...
- Netty Reator(三)Reactor 模型
Netty Reator(三)Reactor 模型 Netty 系列目录 (https://www.cnblogs.com/binarylei/p/10117436.html) 本文介绍 DC Sch ...
- Netty 系列(三)Netty 入门
Netty 系列(三)Netty 入门 Netty 是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络服务器和客户端程序.更多请参考:Netty Github 和 Netty中文 ...
- Netty构建游戏服务器(三)--netty spring简单整合
一,基本方法 上节实现了netty的基本连接,这节加入spring来管理netty,由spring来开启netty服务. 在netty服务器中,我们建立了三个类:HelloServer(程序主入口) ...
随机推荐
- python list unicode转中文显示
[u'\u773c', u'\u8179\u90e8', u'\u4e94\u5b98', u'\u53e3\u8154', u'\u8179\u90e8', u'\u53e3\u8154'] str ...
- 杂: PYTHON上数据储存:推荐h5py
一篇很短的小短文,主要推荐下做科学计算是大量数据的储存问题 最近在做一个CNN的项目,文件夹里有20w张图片要读入并保存到一个data文件(不然每次都读20w文件太麻烦). 折腾了一个下午,发现了一个 ...
- catpcha
生成随机验证码: # -*- coding: utf-8 -*- # @Author: huangyong # @Date: 2016-10-29 22:18:38 # @Last Modified ...
- selenium自动化测试通过localstorage绕过登陆
引言: 做自动化测试,尤其是通过page object模式做UI自动化测试,登陆是个很麻烦的事情,比如你想对某个页面进行测试,一般直接链接到那个页面是不可能的,总是需要先登陆,然后刷新页面才能到想要的 ...
- UVA-755-排序
奇怪,我怎么还有一个排序题目没过 题意如下: 公司喜欢有难忘的电话号码,一个让电话号码变得难忘的方式是有一个拼读起来难忘的单词,比如,你可以呼叫University of Waterloo通过拨打难忘 ...
- HAproxy使用
参考官网 安装HAproxy/ pull 官方镜像 本地安装:本地安装路径:/usr/local/haproxy/配置: 添加:/usr/local/haproxy/conf/haproxy.cfg添 ...
- 编写一个基于Soap DataModule的三层数据库应用
服务器端:建立一个Web App Debugger executable类型,不需要接口,添加一个SoapData Module,放上AdoCon,AdoDataSet,DataSetProvider ...
- egret 配置设置
修改index.html的时候,要主要template文件夹下的web文件夹也有个index.html,两者控制的不一样 初始安装新建项目后调试这样的情况.重新安装引擎和下载egret安装包安装,默认 ...
- as3 单例的不常见写法
方法一:(显式允许new一次) package { import flash.errors.IllegalOperationError; import flash.events.EventDispat ...
- Simple2D-26 Simple2D 最后的工作,开发结束
开始的时候打算将 Simple2D 做成一个库的,但现在没有那个功夫了. 要渲染顶点数据,就必须将渲染函数放置到 glClear( ) 函数和 SwapBuffers( ) 函数之间,但又不希望开发时 ...