Netty入门1之----认识Netty
什么是Netty?
Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架。
Netty 是一个广泛使用的 Java 网络编程框架(Netty 在 2011 年获得了Duke's Choice Award,见https://www.java.net/dukeschoice/2011)。它活跃和成长于用户社区,像大型公司 Facebook 和 Instagram 以及流行 开源项目如 Infinispan, HornetQ, Vert.x, Apache Cassandra 和 Elasticsearch 等,都利用其强大的对于网络抽象的核心代码。
以上是摘自《Essential Netty In Action》这本书,本文的内容也是本人读了这本书之后的一些整理心得,如有不当之处欢迎大虾们指正
Netty和Tomcat有什么区别?
Netty和Tomcat最大的区别就在于通信协议,Tomcat是基于Http协议的,他的实质是一个基于http协议的web容器,但是Netty不一样,他能通过编程自定义各种协议,因为netty能够通过codec自己来编码/解码字节流,完成类似redis访问的功能,这就是netty和tomcat最大的不同。
有人说netty的性能就一定比tomcat性能高,其实不然,tomcat从6.x开始就支持了nio模式,并且后续还有APR模式——一种通过jni调用apache网络库的模式,相比于旧的bio模式,并发性能得到了很大提高,特别是APR模式,而netty是否比tomcat性能更高,则要取决于netty程序作者的技术实力了。
为什么Netty受欢迎?
如第一部分所述,netty是一款收到大公司青睐的框架,在我看来,netty能够受到青睐的原因有三:
- 并发高
- 传输快
- 封装好
Netty为什么并发高
Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,对比于BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高,两张图让你了解BIO和NIO的区别:
从这两图可以看出,NIO的单线程能处理连接的数量比BIO要高出很多,而为什么单线程能处理更多的连接呢?原因就是图二中出现的Selector
。
当一个连接建立之后,他有两个步骤要做,第一步是接收完客户端发过来的全部数据,第二步是服务端处理完请求业务之后返回response给客户端。NIO和BIO的区别主要是在第一步。
在BIO中,等待客户端发数据这个过程是阻塞的,这样就造成了一个线程只能处理一个请求的情况,而机器能支持的最大线程数是有限的,这就是为什么BIO不能支持高并发的原因。
而NIO中,当一个Socket建立好之后,Thread并不会阻塞去接受这个Socket,而是将这个请求交给Selector,Selector会不断的去遍历所有的Socket,一旦有一个Socket建立完成,他会通知Thread,然后Thread处理完数据再返回给客户端——这个过程是阻塞的,这样就能让一个Thread处理更多的请求了。
下面两张图是基于BIO的处理流程和netty的处理流程,辅助你理解两种方式的差别:
除了BIO和NIO之外,还有一些其他的IO模型,下面这张图就表示了五种IO模型的处理流程:
- BIO,同步阻塞IO,阻塞整个步骤,如果连接少,他的延迟是最低的,因为一个线程只处理一个连接,适用于少连接且延迟低的场景,比如说数据库连接。
- NIO,同步非阻塞IO,阻塞业务处理但不阻塞数据接收,适用于高并发且处理简单的场景,比如聊天软件。
- 多路复用IO,他的两个步骤处理是分开的,也就是说,一个连接可能他的数据接收是线程a完成的,数据处理是线程b完成的,他比BIO能处理更多请求,但是比不上NIO,但是他的处理性能又比BIO更差,因为一个连接他需要两次system call,而BIO只需要一次,所以这种IO模型应用的不多。
- 信号驱动IO,这种IO模型主要用在嵌入式开发,不参与讨论。
- 异步IO,他的数据请求和数据处理都是异步的,数据请求一次返回一次,适用于长连接的业务场景。
以上摘自Linux IO模式及 select、poll、epoll详解
Netty为什么传输快
Netty的传输快其实也是依赖了NIO的一个特性——零拷贝。我们知道,Java的内存有堆内存、栈内存和字符串常量池等等,其中堆内存是占用内存空间最大的一块,也是Java对象存放的地方,一般我们的数据如果需要从IO读取到堆内存,中间需要经过Socket缓冲区,也就是说一个数据会被拷贝两次才能到达他的的终点,如果数据量大,就会造成不必要的资源浪费。
Netty针对这种情况,使用了NIO中的另一大特性——零拷贝,当他需要接收数据的时候,他会在堆内存之外开辟一块内存,数据就直接从IO读到了那块内存中去,在netty里面通过ByteBuf可以直接对这些数据进行直接操作,从而加快了传输速度。
下两图就介绍了两种拷贝方式的区别,摘自Linux 中的零拷贝技术,第 1 部分
上文介绍的ByteBuf是Netty的一个重要概念,他是netty数据处理的容器,也是Netty封装好的一个重要体现,将在下一部分做详细介绍。
为什么说Netty封装好?
要说Netty为什么封装好,这种用文字是说不清的,直接上代码:
- 阻塞I/O
public class PlainOioServer {
public void serve(int port) throws IOException {
final ServerSocket socket = new ServerSocket(port); //1
try {
for (;;) {
final Socket clientSocket = socket.accept(); //2
System.out.println("Accepted connection from " + clientSocket);
new Thread(new Runnable() { //3
@Override
public void run() {
OutputStream out;
try {
out = clientSocket.getOutputStream();
out.write("Hi!\r\n".getBytes(Charset.forName("UTF-8"))); //4
out.flush();
clientSocket.close(); //5
} catch (IOException e) {
e.printStackTrace();
try {
clientSocket.close();
} catch (IOException ex) {
// ignore on close
}
}
}
}).start(); //6
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 非阻塞IO
public class PlainNioServer {
public void serve(int port) throws IOException {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
ServerSocket ss = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
ss.bind(address); //1
Selector selector = Selector.open(); //2
serverChannel.register(selector, SelectionKey.OP_ACCEPT); //3
final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes());
for (;;) {
try {
selector.select(); //4
} catch (IOException ex) {
ex.printStackTrace();
// handle exception
break;
}
Set<SelectionKey> readyKeys = selector.selectedKeys(); //5
Iterator<SelectionKey> iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
try {
if (key.isAcceptable()) { //6
ServerSocketChannel server =
(ServerSocketChannel)key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_WRITE |
SelectionKey.OP_READ, msg.duplicate()); //7
System.out.println(
"Accepted connection from " + client);
}
if (key.isWritable()) { //8
SocketChannel client =
(SocketChannel)key.channel();
ByteBuffer buffer =
(ByteBuffer)key.attachment();
while (buffer.hasRemaining()) {
if (client.write(buffer) == 0) { //9
break;
}
}
client.close(); //10
}
} catch (IOException ex) {
key.cancel();
try {
key.channel().close();
} catch (IOException cex) {
// 在关闭时忽略
}
}
}
}
}
}
- Netty
public class NettyOioServer {
public void server(int port) throws Exception {
final ByteBuf buf = Unpooled.unreleasableBuffer(
Unpooled.copiedBuffer("Hi!\r\n", Charset.forName("UTF-8")));
EventLoopGroup group = new OioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); //1
b.group(group) //2
.channel(OioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {//3
@Override
public void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { //4
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(buf.duplicate()).addListener(ChannelFutureListener.CLOSE);//5
}
});
}
});
ChannelFuture f = b.bind().sync(); //6
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync(); //7
}
}
}
从代码量上来看,Netty就已经秒杀传统Socket编程了,但是这一部分博大精深,仅仅贴几个代码岂能说明问题,在这里给大家介绍一下Netty的一些重要概念,让大家更理解Netty。
Channel
数据传输流,与channel相关的概念有以下四个,上一张图让你了解netty里面的Channel。Channel一览- Channel,表示一个连接,可以理解为每一个请求,就是一个Channel。
- ChannelHandler,核心处理业务就在这里,用于处理业务请求。
- ChannelHandlerContext,用于传输业务数据。
- ChannelPipeline,用于保存处理过程需要用到的ChannelHandler和ChannelHandlerContext。
- ByteBuf
ByteBuf是一个存储字节的容器,最大特点就是使用方便,它既有自己的读索引和写索引,方便你对整段字节缓存进行读写,也支持get/set,方便你对其中每一个字节进行读写,他的数据结构如下图所示:
他有三种使用模式:
- Heap Buffer 堆缓冲区
堆缓冲区是ByteBuf最常用的模式,他将数据存储在堆空间。 - Direct Buffer 直接缓冲区
直接缓冲区是ByteBuf的另外一种常用模式,他的内存分配都不发生在堆,jdk1.4引入的nio的ByteBuffer类允许jvm通过本地方法调用分配内存,这样做有两个好处- 通过免去中间交换的内存拷贝, 提升IO处理速度; 直接缓冲区的内容可以驻留在垃圾回收扫描的堆区以外。
- DirectBuffer 在 -XX:MaxDirectMemorySize=xxM大小限制下, 使用 Heap 之外的内存, GC对此”无能为力”,也就意味着规避了在高负载下频繁的GC过程对应用线程的中断影响.
- Composite Buffer 复合缓冲区
复合缓冲区相当于多个不同ByteBuf的视图,这是netty提供的,jdk不提供这样的功能。
除此之外,他还提供一大堆api方便你使用,在这里我就不一一列出了,具体参见ByteBuf字节缓存。
- Codec
Netty中的编码/解码器,通过他你能完成字节与pojo、pojo与pojo的相互转换,从而达到自定义协议的目的。
在Netty里面最有名的就是HttpRequestDecoder和HttpResponseEncoder了。
以上就是我对《Netty实战》这本书的一些心得和书外的一些相关知识整理,如果有不同的见解,欢迎讨论!
Netty入门1之----认识Netty的更多相关文章
- [Netty] - Netty入门(最简单的Netty客户端/服务器程序)
Java中的NIO是一种解决阻塞式IO问题的基本技术,但是NIO的编写对java程序员是有比较高的要求的.那么Netty就是一种简化操作的一个成熟的网络IO编程框架.这里简单介绍一个程序,代码是< ...
- [转]Netty入门(最简单的Netty客户端/服务器程序)
Java中的NIO是一种解决阻塞式IO问题的基本技术,但是NIO的编写对java程序员是有比较高的要求的.那么Netty就是一种简化操作的一个成熟的网络IO编程框架.这里简单介绍一个程序,代码是< ...
- Netty入门一:何为Netty
先了解java的网络编程 Netty为何支持高并发 netty是基于java的nio非阻塞通信,而原始的阻塞通信无法满足高并发.下面我们通过两幅图来简要说明 BIO: 这种模式下一个线程处理一个连接, ...
- Netty入门系列(1) --使用Netty搭建服务端和客户端
引言 前面我们介绍了网络一些基本的概念,虽然说这些很难吧,但是至少要做到理解吧.有了之前的基础,我们来正式揭开Netty这神秘的面纱就会简单很多. 服务端 public class PrintServ ...
- Netty入门系列(2) --使用Netty解决粘包和拆包问题
前言 上一篇我们介绍了如果使用Netty来开发一个简单的服务端和客户端,接下来我们来讨论如何使用解码器来解决TCP的粘包和拆包问题 TCP为什么会粘包/拆包 我们知道,TCP是以一种流的方式来进行网络 ...
- Netty入门系列(3) --使用Netty进行编解码的操作
前言 何为编解码,通俗的来说,我们需要将一串文本信息从A发送到B并且将这段文本进行加工处理,如:A将信息文本信息编码为2进制信息进行传输.B接受到的消息是一串2进制信息,需要将其解码为文本信息才能正常 ...
- JAVA通信系列三:Netty入门总结
一.Netty学习资料 书籍<Netty In Action中文版> 对于Netty的十一个疑问http://news.cnblogs.com/n/205413/ 深入浅出Nettyhtt ...
- (入门篇 NettyNIO开发指南)第三章-Netty入门应用
作为Netty的第一个应用程序,我们依然以第2章的时间服务器为例进行开发,通过Netty版本的时间服务报的开发,让初学者尽快学到如何搭建Netty开发环境和!运行Netty应用程序. 如果你已经熟悉N ...
- netty 入门(一)
netty Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序.更确切的讲是一个组件,没有那么复杂. 例子 一 Discard服务器端 我们 ...
随机推荐
- Spark & Python
技术文章 https://www.cnblogs.com/yangzhang-home/p/6056133.html 基于Python Spark的推荐系统 https://blog.csdn.net ...
- java-web 登陆功能
目录结构 web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi= ...
- 安装Telerik JustMock插件后启动不成功
1.打开Telerik JustMock Configuration 勾选所有框 2.到C:\Program Files (x86)\Progress\Telerik JustMock\Librari ...
- 查找Ubuntu下包的归属
今天在制作docker时,发现我的程序有些依赖的包不太好找应该安装什么. 在centos下面,可以用命令: rpm -qf <libraryname> 在Ubuntu下面,发现一个网站基本 ...
- Mac 下安装mysqldb 问题:一条命令解决mysql_config not found
遇到过几次pip安装mysql-python的时候出现如题的问题,在这里记录一下解决方法. 找不到mysql_config一般是由于通过lnmp.org或者其他方式安装mysql以后mysql_con ...
- Robot Framework_Ride(Edit标签)
前言 RIDE 作为 Robot Framework 的“脸面”,虽然我们已经可以拿它来创建和运行测试了,但我们对它的认识并不全面,这一小节我们将了解这个工具的使用 Edit标签 下面我们来看一看测试 ...
- 【ExtJS】一些基本概念的梳理
学习ExtJS有一段时间了,一些相关知识点虽然每天都在用,不过如果猛的一问起来还是会一愣,趁现在好好梳理下吧.长期修改添加,弄清楚什么就加入什么. 1.Ext.onReady(): onReady() ...
- SpringMVC的json交互
一.注解说明 1.@RequestBody 作用:@RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内 ...
- [转]Work With Odata in Web API: Create Your First Odata Service
本文转自:http://www.c-sharpcorner.com/UploadFile/dacca2/work-with-odata-in-web-api-create-your-first-oda ...
- java 线程池(1)
问题 : 线程池中的 coreSize 和 maxSize 的作用分别是什么? 未执行的线程池存在在哪种数据类型,为什么使用这种类型的数据结构 ThreadPoolExecutor概述 ThreadP ...