堆缓冲区

最常用的 ByteBuf 模式是将数据存储在 JVM 的堆空间中。 这种模式被称为支撑数组
(backing array), 它能在没有使用池化的情况下提供快速的分配和释放。

直接缓冲区

直接缓冲区的内容将驻留在常规的会被垃圾回收的堆之外。直接缓冲区对于网络数据传输是理想的选择。因为如果你的数据包含在一个在堆上分配的缓冲区中,那么事实上,在通过套接字发送它之前,JVM将会在内部把你的缓冲区复制到一个直接缓冲区中。
直接缓冲区的主要缺点是,相对于基于堆的缓冲区,它们的分配和释放都较为昂贵。

经验表明,Bytebuf的最佳实践是在IO通信线程的读写缓冲区使用DirectByteBuf,后端业务使用HeapByteBuf。

复合缓冲区

让我们考虑一下一个由两部分——头部和主体——组成的将通过 HTTP 协议传输的消息。这两部分由应用程序的不同模块产生, 将会在消息被发送的时候组装。该应用程序可以选择为多个消息重用相同的消息主体。当这种情况发生时,对于每个消息都将会创建一个新的头部。
因为我们不想为每个消息都重新分配这两个缓冲区,所以使用 CompositeByteBuf 是一个
完美的选择。
需要注意的是, Netty使用了CompositeByteBuf来优化套接字的I/O操作,尽可能地消除了
由JDK的缓冲区实现所导致的性能以及内存使用率的惩罚。这种优化发生在Netty的核心代码中,因此不会被暴露出来,但是你应该知道它所带来的影响。

ByteBuf 的分配方式

池化的分配 PooledByteBufAllocator是ByteBufAllocator的默认方式

可以通过 Channel(每个都可以有一个不同的 ByteBufAllocator 实例)或者绑定到
ChannelHandler 的 ChannelHandlerContext 获取一个到 ByteBufAllocator 的引用。
池化了ByteBuf的实例以提高性能并最大限度地减少内存碎片。此实现使用了一种称为jemalloc的已被大量现代操作系统所采用的高效方法来分配内存。
该方式在netty中是默认方式。

非池化的分配 UnpooledByteBufAllocator

可能某些情况下,你未能获取一个到 ByteBufAllocator 的引用。对于这种情况,Netty 提供了一个简单的称为 Unpooled 的工具类, 它提供了静态的辅助方法来创建未池化的 ByteBuf实例。

依托http进行性能测试

netty http 代码

HttpServer.java
package http.server;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder; public class HttpServer { private static Log log = LogFactory.getLog(HttpServer.class); public void start(int port) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
// server端发送的是httpResponse,所以要使用HttpResponseEncoder进行编码
ch.pipeline().addLast(new HttpResponseEncoder());
// server端接收到的是httpRequest,所以要使用HttpRequestDecoder进行解码
ch.pipeline().addLast(new HttpRequestDecoder());
ch.pipeline().addLast(new HttpServerInboundHandler());
}
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
} public static void main(String[] args) throws Exception {
HttpServer server = new HttpServer();
log.info("Http Server listening on 5656 ...");
server.start(5656);
}
}

HttpServerInboundHandler.java

package http.server;

import static io.netty.handler.codec.http.HttpResponseStatus.OK;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpVersion; public class HttpServerInboundHandler extends ChannelInboundHandlerAdapter {
private static Log log = LogFactory.getLog(HttpServerInboundHandler.class); // private HttpRequest request; // static ByteBuf buf = Unpooled.wrappedBuffer("hello world".getBytes());
byte[] bs = "hello world".getBytes(); @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// if (msg instanceof HttpRequest) {
// request = (HttpRequest) msg;
//
// String uri = request.uri();
// // System.out.println("Uri:" + uri);
// }
// if (msg instanceof HttpContent) {
// HttpContent content = (HttpContent) msg;
// ByteBuf buf = content.content();
// // System.out.println(buf.toString(io.netty.util.CharsetUtil.UTF_8));
// buf.release(); // String res = "hello world.";
// ByteBuf buf = Unpooled.wrappedBuffer(bs);
// ByteBuf buf = Unpooled.directBuffer();
// ByteBuf buf = Unpooled.buffer();
// ByteBuf buf = ctx.alloc().heapBuffer();// 池化堆内存
// ByteBuf buf = ctx.alloc().directBuffer(); // 池化直接内存
// ByteBuf buf = Unpooled.buffer();// 非池化堆内存
ByteBuf buf = Unpooled.directBuffer();// 非池化堆内存
buf.writeBytes(bs);
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, OK, buf); // response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
/*
* if (HttpHeaders.isKeepAlive(request)) { response.headers().set(CONNECTION, Values.KEEP_ALIVE); }
*/
ctx.write(response);
// ctx.flush();
// }
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.error(cause.getMessage());
ctx.close();
} }

池化堆内存 ctx.alloc().heapBuffer()

Running 20s test @ http://127.0.0.1:5656/
4 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.76ms 9.31ms 180.34ms 92.96%
Req/Sec 138.20k 43.16k 210.22k 66.50%
10957832 requests in 20.09s, 522.51MB read
Requests/sec: 545332.08
Transfer/sec: 26.00MB
real 0m20.104s
user 0m10.441s
sys 0m44.703s

池化直接内存 ctx.alloc().directBuffer()

Running 20s test @ http://127.0.0.1:5656/
4 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 4.47ms 9.99ms 149.70ms 91.76%
Req/Sec 138.51k 41.31k 209.94k 63.38%
10981466 requests in 20.09s, 523.64MB read
Requests/sec: 546684.37
Transfer/sec: 26.07MB
real 0m20.098s
user 0m10.890s
sys 0m45.081s

非池化堆内存 Unpooled.buffer()

Running 20s test @ http://127.0.0.1:5656/
4 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 4.00ms 8.72ms 150.05ms 91.52%
Req/Sec 138.84k 42.05k 209.72k 63.81%
11017442 requests in 20.09s, 525.35MB read
Requests/sec: 548379.99
Transfer/sec: 26.15MB
real 0m20.101s
user 0m10.639s
sys 0m45.191s

非池化直接内存 Unpooled.directBuffer()

Running 20s test @ http://127.0.0.1:5656/
4 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.64ms 9.36ms 156.79ms 92.71%
Req/Sec 124.55k 33.90k 191.90k 71.61%
9890536 requests in 20.07s, 471.62MB read
Requests/sec: 492854.62
Transfer/sec: 23.50MB
real 0m20.076s
user 0m9.774s
sys 0m41.801s

【netty这点事儿】ByteBuf 的使用模式的更多相关文章

  1. Netty(7)源码-ByteBuf

    一.ByteBuf工作原理 1. ByteBuf是ByteBuffer的升级版: jdk中常用的是ByteBuffer,从功能角度上,ByteBuffer可以完全满足需要,但是有以下缺点: ByteB ...

  2. Netty实战五之ByteBuf

    网络数据的基本单位总是字节,Java NIO 提供了ByteBuffer作为它的字节容器,但是其过于复杂且繁琐. Netty的ByteBuffer替代品是ByteBuf,一个强大的实现,即解决了JDK ...

  3. Netty 系列三(ByteBuf).

    一.概述和原理 网络数据传输的基本单位总是字节,Netty 提供了 ByteBuf 作为它的字节容器,既解决了 JDK API 的局限性,又为网络应用程序提供了更好的 API,ByteBuf 的优点: ...

  4. Netty怎么切换三种I/O模式和源码解释

    参考文献:极客时间傅健老师的<Netty源码剖析与实战>Talk is cheap.show me the code! 三种I/O模式 BIO:Block I/O,即同步并阻塞的IO:BI ...

  5. 【Netty技术专题】「原理分析系列」Netty强大特性之ByteBuf零拷贝技术原理分析

    零拷贝Zero-Copy 我们先来看下它的定义: "Zero-copy" describes computer operations in which the CPU does n ...

  6. 关于netty的多个handler链式模式

    1. 老规矩, 引入我们喜闻乐见的maven依赖 <dependency> <groupId>io.netty</groupId> <artifactId&g ...

  7. netty: 以默认的ByteBuf作为传输数据

    client部分代码: //线程 EventLoopGroup worker = new NioEventLoopGroup(); //辅助类 Bootstrap b = new Bootstrap( ...

  8. Netty 核心容器之ByteBuf 结构详解

    原文链接 Netty 核心容器之ByteBuf 结构详解 代码仓库地址 Java的NIO模块提供了ByteBuffer作为其字节存储容器,但是这个类的使用过于复杂,因此Netty实现了ByteBuf来 ...

  9. netty中的ByteBuf

    网络数据的基本单位总是字节.Java NIO 提供了 ByteBuffer 作为它 的字节容器,但是这个类使用起来过于复杂,而且也有些繁琐. Netty 的 ByteBuffer 替代品是 ByteB ...

随机推荐

  1. canvas实现倒计时效果示例(vue组件内编写)

    前言: 此事例是在vue组件中,使用canvas实现倒计时动画的效果.其实,实现效果的逻辑跟vue没有关系,只要读懂canvas如何实现效果的这部分逻辑就可以了 canvas动画的原理:利用定时器,给 ...

  2. 前端css常用class命名id命名

    1.常用id的命名: (1)页面结构 容器: container 页头:header 内容:content/container 页面主体:main 页尾:footer 导航:nav 侧栏:sideba ...

  3. python_协程方式操作数据库

    # !/usr/bin/python3 # -*- coding: utf-8 -*- import requests import gevent import pymysql from gevent ...

  4. Part 1:请求与响应--Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...

  5. convert图像格式批量转换

    问题:利用GMT绘制生成了eps格式的图像,为了将图像插入到word中,且保持较高的分辨率,利用convert进行图像格式转换,将eps转换成tiff格式. code:  $i ${name}.tif ...

  6. [python] 2、python使用pyaudio进行录音,及其在python虚拟环境virtualenv中安装遇到的问题

    1.pyaudio安装大背景 最近在做智能音箱,需要编写声音拾取代码,我先是百度两篇比较常见的用python进行录音的操作的文章: python写一个录音小程序:http://blog.csdn.ne ...

  7. Delphi json解析相关

    身为一个资深的Delphi 开发者, 最近在做一个小工具的时候,开始捡起来pascal语言. 主要是开发一个内部用的小工具, 主要功能: 1.解析json格式 2.格式化json文件 3.校验json ...

  8. 【mac】mac os X更新High Sierra后出现的问题

    今天更新了一下macbook pro的系统到10.13.1版本,出现了几个小问题,总结一下解决方案: git客户端无法使用 解决方案如下: http://blog.csdn.net/kedongjun ...

  9. template.compile()方法

    template.compile(source, options) source:必传,渲染模板的内容. options:可选,通常不传.(其实是我还没研究明白) return:一个渲染函数. 示例如 ...

  10. Go语言入门——dep入门

    本文出现了大量maven的内容,更适合java程序员阅读,如果你的语言做依赖管理的方案与maven差异很大,可能在有些地方会不理解 从很久之前go语言在依赖解决和管理方面方案的匮乏就被不少人诟病.光指 ...