【netty这点事儿】ByteBuf 的使用模式
堆缓冲区
最常用的 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 的使用模式的更多相关文章
- Netty(7)源码-ByteBuf
一.ByteBuf工作原理 1. ByteBuf是ByteBuffer的升级版: jdk中常用的是ByteBuffer,从功能角度上,ByteBuffer可以完全满足需要,但是有以下缺点: ByteB ...
- Netty实战五之ByteBuf
网络数据的基本单位总是字节,Java NIO 提供了ByteBuffer作为它的字节容器,但是其过于复杂且繁琐. Netty的ByteBuffer替代品是ByteBuf,一个强大的实现,即解决了JDK ...
- Netty 系列三(ByteBuf).
一.概述和原理 网络数据传输的基本单位总是字节,Netty 提供了 ByteBuf 作为它的字节容器,既解决了 JDK API 的局限性,又为网络应用程序提供了更好的 API,ByteBuf 的优点: ...
- Netty怎么切换三种I/O模式和源码解释
参考文献:极客时间傅健老师的<Netty源码剖析与实战>Talk is cheap.show me the code! 三种I/O模式 BIO:Block I/O,即同步并阻塞的IO:BI ...
- 【Netty技术专题】「原理分析系列」Netty强大特性之ByteBuf零拷贝技术原理分析
零拷贝Zero-Copy 我们先来看下它的定义: "Zero-copy" describes computer operations in which the CPU does n ...
- 关于netty的多个handler链式模式
1. 老规矩, 引入我们喜闻乐见的maven依赖 <dependency> <groupId>io.netty</groupId> <artifactId&g ...
- netty: 以默认的ByteBuf作为传输数据
client部分代码: //线程 EventLoopGroup worker = new NioEventLoopGroup(); //辅助类 Bootstrap b = new Bootstrap( ...
- Netty 核心容器之ByteBuf 结构详解
原文链接 Netty 核心容器之ByteBuf 结构详解 代码仓库地址 Java的NIO模块提供了ByteBuffer作为其字节存储容器,但是这个类的使用过于复杂,因此Netty实现了ByteBuf来 ...
- netty中的ByteBuf
网络数据的基本单位总是字节.Java NIO 提供了 ByteBuffer 作为它 的字节容器,但是这个类使用起来过于复杂,而且也有些繁琐. Netty 的 ByteBuffer 替代品是 ByteB ...
随机推荐
- java中的左右移
package scanner; public class LeftMove { public static void main(String[] args) { int i = 1; System. ...
- TCP是什么? 最简单的三次握手说明
TCP是什么? TCP(Transmission Control Protocol 传输控制协议)是一种面向连接(连接导向)的.可靠的. 基于IP的传输层协议.TCP在IP报文的协议号是6.TCP是一 ...
- git 简单入门
首先了解一下git的是什么: [百度百科解释]Git是一个开源的分布式版本控制系统,可以有效.高速的处理从很小到非常大的项目版本管理.[2] Git 是 Linus Torvalds 为了帮助管理 ...
- 关于mybatis 注解sql sum(参数)传参写法
新手出道 验证了很久sum()里面带参数方式 #{参数}一直不行日志显示参数已经传进 但就是加不上去 返回的始终是0 后面换成$(参数)之后就行了 @Select("select sum($ ...
- P1345 [USACO5.4]奶牛的电信Telecowmunication
P1345 [USACO5.4]奶牛的电信Telecowmunication 题目描述 农夫约翰的奶牛们喜欢通过电邮保持联系,于是她们建立了一个奶牛电脑网络,以便互相交流.这些机器用如下的方式发送电邮 ...
- 互联网公司为啥不使用mysql分区表?
转:http://www.cnblogs.com/zhulin516114/p/7306708.html 缘起:有个朋友问我分区表在58的应用,我回答不出来,在我印象中,百度.58都没有听说有分区表相 ...
- java—— finall 关键词
_ *{ margin: 0; padding: 0; } .on2{ margin: 10px 0; cursor: pointer; user-select: none; color: white ...
- js 数组与对象的区别
学习javascript的时候,我曾经一度搞不清楚”数组”(array)和”对象”(object)的根本区别在哪里,两者都可以用来表示数据的集合. 比如有一个数组a=[1,2,3,4],还有一个对 ...
- DOM中对象的获得
DOM的所有对象会在页面打开时,由浏览器页面创建. 浏览器把dom定点对象Document对像的引用交给了window对象. 1.document对象的获得 var doc = window.d ...
- Python Selenium + phantomJS 模拟登陆教务管理系统 “抢课”
# _*_coding:utf-8_*_ from selenium import webdriver from selenium.webdriver.common.action_chains imp ...