初识Netty

Netty是由JBoss提供的一个Java的开源框架,是GitHub上的独立项目。

Netty是一个异步的,基于事件驱动的网络应用框架,用于快速开发高性能、高可靠的网络IO程序。

Netty主要针对于TCP协议下,面向客户端的高并发应用,或者是Peer-to-Peer场景下的大量数据次序传输的应用。

Netty本质上是一个NIO的框架,适用于服务器通讯相关的多种应用场景。

底层是NIO,NIO底层是Java IO和网络IO,再往下是TCP/IP协议。

Netty的应用场景

1、经典的Hadoop的高性能通信和序列化组件AVRO(实现数据文件的共享),他的Netty Service是基于Netty的二次封装。

2、在分布式系统中,各个节点之间需要远程服务调用例如RPC框架dubbo。

3、无论是手游服务端还是大型网络游戏,登录服务器都是用Netty作为高性能基础通信组件。

4、地图服务器之间可以方便的通过Netty进行高性能的通信。

IO模型

IO模型很大程度的决定了程序通信的性能。

Java共支持3种IO模型:BIO,NIO,AIO。

BIO:同步阻塞IO,也就是传统阻塞型的IO,服务器实现模式是一个连接对应一个线程。客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个链接不做任何事情会造成不必要的线程开销。

NIO:同步非阻塞IO,服务器实现模式是一个线程处理多个请求,客户端发送的链接请求都会注册到多路复用器上,多路复用器轮询到链接有IO请求就进行处理。

AIO:异步非阻塞,AIO引入了异步通道的概念,采用了Proactor模式,简化了程序编写,有效的请求才启动线程,他的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且链接时间较长的应用。

BIO的编程流程

1、服务端启动一个ServerSocket

2、客户端启动Socket对服务器进行通信,默认情况下对每个客户端建立一个线程。

3、客户端发送请求后,先咨询服务器是否有线程响应,如果没有则会等待,或者被拒绝。

4、如果有响应,客户端线程会等待请求结束后,才会继续执行。(阻塞,同步)

public class BIOServer {

    public static void main(String[] args) throws IOException {
ExecutorService executorService = Executors.newCachedThreadPool();
//创建服务器端socket
final ServerSocket serverSocket = new ServerSocket(6666);
while (true){
System.out.println("等待连接...");
final Socket socket = serverSocket.accept();
//连接一个客户端 System.out.println("连接一个客户端");
executorService.execute(new Runnable() {
public void run() {
handler(socket);
}
});
} } //编写一个handle方法用来处理客户端通讯
public static void handler(Socket socket) {
byte[] bytes = new byte[1024];
try {
InputStream inputStream = socket.getInputStream();
//获取输入流,读取客户端发来的数据
int i;
System.out.println("线程id: "+Thread.currentThread().getId()+" 线程名称 "+Thread.currentThread().getName());
System.out.println("等待读入信息"); while ((i = inputStream.read(bytes)) != -1) {
System.out.println("客户端数据: "+new String(bytes, 0, i));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("关闭与客户端的连接....");
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
}

启动main方法,打开cmd命令窗口。

输入telnet 127.0.0.1 6666 ;连接服务端,这相当于建立了一个客户端连接,然后Ctrl+],从客户端向服务器发送信息。

send hello

然后再开启一个连接。向服务器发送send success

当关闭命令行窗口后客户端与服务器的链接就断开了。从上面可知BIO编程模型,每次建立一个连接,服务端就会创建一个线程。然后每次进行读取的时候,如果客户端不发送数据,服务端线程就一直阻塞在那,直到数据读取成功。

BIO 问题分析

1、每个请求都需要创建独立的线程,与对应的客户端进行数据读入,业务处理,数据写入。

2、当并发数较大时,需要创建大量的线程来处理连接,系统资源占用较大。

3、连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在Read上,造成不必要的资源浪费。

Java NIO

  • Java NIO全称是java non-blocking IO,是指JDK提供的新API。从JDK1.4开始,Java提供了一系列改进的输入输出的新特性,被通称为NIO,是同步非阻塞的IO模型。
  • NIO相关类放在java.nio包以及子包下。
  • NIO有三大核心部分:Channel(通道),Selector(选择器),Buffer(缓冲区)。
  • NIO是面向缓冲区,或者面向块编程的。数据读取到一个稍后处理的缓冲区,需要的时候可以在缓冲区前后移动,这就增加了处理过程的灵活性,使用它可以提供非阻塞的高伸缩网络。
  • Java NIO的非阻塞模式,是一个线程从某个通道发送请求或者读取数据,但是它仅仅能得到目前可用的数据,如果当前没有任务可做,他也不会阻塞等待,它可以去完成其他的事情。
  • NIO可以做到一个线程来处理多个操作,假设有10000个请求过来,根据实际情况,可以分配50到100个线程来处理,而不是必须要创建10000个线程。
  • HTTP2.0使用了多路复用技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1打了好几个数量级。

BIO和NIO的比较

1、BIO以流的方式处理数据,而NIO以块的方式处理数据,块IO的效率比流IO高很多

2、BIO是阻塞的,NIO是非阻塞的

3、NIO是基于字节流和字符流进行操作,而NIO是基于Channel和Buffer进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。selector用于监听多个通道的事件,比如连接请求,数据到达,因此使用单个线程就可以监听多个客户端通道。

NIO中Selector、Channel、Buffer的关系

  1. 每个channel都会对应一个buffer。
  2. 一个selector对应一个线程,一个线程对应多个channel。
  3. 程序切换到哪个channel是由Event(事件)决定的。
  4. selector会根据不同的事件,在各个通道上进行切换。
  5. buffer是一个内存块,底层有一个数组。
  6. 数据的读取和写入是通过buffer,buffer可以切换读写,通过flip方法,但是BIO是单向输出,要么是输入流,要么是输出流。
  7. channel是双向的,可以返回底层操作系统的情况,比如linux,底层的操作系统通道就是双向的。

Buffer

缓冲区:缓冲区本质上是一个可以读写数据的内存块,可以理解成是一个容器对象(含数组),该对象提供了一组方法,可以更轻松的使用内存块;缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化。Channel提供从文件,网络读取数据的渠道,但是读取或写入的数据都必须经过Buffer。

Buffer是一个抽象类,类关系如下:

属性表示的含义

  • capacity:缓冲区容量大小,缓冲区一旦初始化不能改变
  • limit:表示缓冲区当前终点,不能对缓冲区超过极限位置进行读写,limit可以修改。
  • position:位置,每次读取缓冲区,都会改变。
  • mark:标记 -1

ByteBuffer主要的方法如下:

   //创建初始缓冲区
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
//设置缓冲区的初始容量
public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapByteBuffer(capacity, capacity);
} //构造初始化位置offset和上届length的缓冲区
public static ByteBuffer wrap(byte[] array,
int offset, int length)
{
try {
return new HeapByteBuffer(array, offset, length);
} catch (IllegalArgumentException x) {
throw new IndexOutOfBoundsException();
}
} //把数组放到缓冲区中使用
public static ByteBuffer wrap(byte[] array) {
return wrap(array, 0, array.length);
}

Channel

NIO的通道类似于流,但是区别如下。

  • 通道可以同时进行读写,而流只能读或者只能写
  • 通道可以实现异步读写数据
  • 通道可以从缓冲区读数据,也可以写数据到缓冲

BIO中的stream是单向的,例如FileInputStream对象只能进行读取数据的操作,NIO的Channel是双向的,可以读操作,也可以写操作。

Channel在NIO中是一个接口

常用的Channel类有:FileChannel,DatagramChannel、ServerSocketChannel和SocketChannel。

FileChannel用户文件的数据读写,DatagramChannel用于UDP的数据读写,ServerSocketChannel和SocketChannel用于TCP的数据读写。

FileChannel

FileChannel主要用来对文件进行IO操作

//从通道中读取数据放入缓冲区
public abstract int read(ByteBuffer dst) throws IOException;
//将缓冲区的数据写入通道
public abstract int write(ByteBuffer src) throws IOException;
//从目标通道中复制数据到当前通道
public abstract long transferTo(long position, long count,
WritableByteChannel target)
throws IOException;
//把数据从当前通道复制到目标通道
public abstract long transferFrom(ReadableByteChannel src,
long position, long count)
throws IOException;

实例:将数据写入到本地文件

文件不存在就创建

/**
* 创建file_1.txt文件,向文件中写入“前研工作室”,通过管道写入
*/
public class NIOFileChannel {
public static void main(String[] args) throws IOException { //创建文件输出流
FileOutputStream fileOutputStream = new FileOutputStream("D:\\file_1.txt");
//写入的数据
String message = "前研工作室";
//获取一个管道,类型其实是FileChannelImpl
FileChannel channel = fileOutputStream.getChannel();
//创建一个缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//将数据放入缓冲区
byteBuffer.put(message.getBytes());
//缓冲区转变成读状态
byteBuffer.flip();
//将byteBuffer数据写入fileChannel
channel.write(byteBuffer);
//关闭输入流
fileOutputStream.close(); }
}

执行结果

使用前面学到的ByteBuffer和FileChannel将之前创建的文件file_1.txt中的数据读取到控制台上

代码实现

public static String readFileByChannel() throws IOException {
//创建文件输入流
File file = new File("D:\\file_1.txt");
FileInputStream fileInputStream = new FileInputStream(file);
//创建缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());
//获取通道
FileChannel channel = fileInputStream.getChannel();
//读取到通道中
channel.read(byteBuffer);
return new String(byteBuffer.array()); }

读取结果

 public static void main(String[] args) throws IOException {
String messages = readFileByChannel();
System.out.println("file_01: "+messages);
//file_01: 前研工作室
}

再来一个实例,使用Buffer完成文件的复制

要求

  • 使用FileChannel和read,write完成文件的拷贝
  • 拷贝文本文件file_1.txt,放在当前目录下

代码实现:

public static void copyFileByChannelAndBuffer(File file) throws IOException {
//从指定文件中读取数据复制到file_2.txt
FileInputStream fileInputStream = new FileInputStream(file);
FileChannel inputStreamChannel = fileInputStream.getChannel();
//写入到file_2.txt文件中
FileOutputStream fileOutputStream = new FileOutputStream("file_2.txt");
FileChannel outputStreamChannel = fileOutputStream.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length()); while (true) {
//每次读取之前要清空缓冲区,否则会写入
byteBuffer.clear();
int read = inputStreamChannel.read(byteBuffer);
//文件读取完毕退出循环
if (read == -1) {
break;
}
byteBuffer.flip();
outputStreamChannel.write(byteBuffer); }
//关闭相关流的操作
fileInputStream.close();
fileOutputStream.close();
}

最终执行结果

实例:拷贝文件通过transferFrom方法

使用FileChannel和方法transferFrom,完成文件的拷贝

需求:将D盘下的pic_02.jpg复制到当前目录下

第一步创建相关的输入输出流,第二步是获取对应流的通道,第三步是使用transferFrom去完成拷贝,最后关闭相关通道和流。

public class NiOFileChannel2 {
public static void main(String[] args) throws IOException {
//创建相关流
FileInputStream fileInputStream = new FileInputStream("D:\\pic_02.jpg");
FileOutputStream fileOutputStream = new FileOutputStream("D:\\pic_03.jpg");
//获取对应的fileChannel
FileChannel inputStreamChannel = fileInputStream.getChannel();
FileChannel outputStreamChannel = fileOutputStream.getChannel();
//使用transferFrom去完成拷贝
outputStreamChannel.transferFrom(inputStreamChannel,0,inputStreamChannel.size());
//关闭相关通道和流
inputStreamChannel.close();
outputStreamChannel.close();
fileInputStream.close();
fileOutputStream.close(); } }

关于Buffer和Channel的注意事项和细节

ByteBuffer支持类型化的put和get,put放入的是什么数据类型,get就应该使用相应的数据类型来取出.当遇到java.nio.BufferOverflowException异常时,可能是你所创建的缓冲区带下已经不能容纳你所加入的数据。

public class NIOByteBufferGetPut {
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(10);
long val = 12345;
buffer.putInt(100);
//因为缓冲区一共分配了10个字节,int占用4个字节,long占8个字节,两者加起来已经大于10个字节,所以会抛出java.nio.BufferOverflowException
buffer.putLong(val);
buffer.putChar('特');
buffer.putShort((short) 14);
buffer.flip();
System.out.println(buffer.getInt());
System.out.println(buffer.getLong());
System.out.println(buffer.getChar());
System.out.println(buffer.getShort()); }
}

可以将一个普通的Buffer转成只读的Buffer。

public static void main(String[] args) {
IntBuffer intBuffer = IntBuffer.allocate(10);
for (int i = 0; i < intBuffer.capacity(); i++) {
intBuffer.put(i*2);
}
IntBuffer intBuffer1 = intBuffer.asReadOnlyBuffer();
System.out.println(intBuffer1.getClass().toString()); while (intBuffer1.hasRemaining()) {
System.out.println(intBuffer1.get());
}
intBuffer1.put(1);// 会抛出ReadOnlyBufferException异常
}

asReadOnlyBuffer()方法返回的是IntBuffer

duplicate方法中是创建了一个HeapIntBufferR实例,biang通过构造函数将readOnly属性设置成了true。HeapIntBufferR是IntBuffer的子类。

protected HeapIntBufferR(int[] buf,
int mark, int pos, int lim, int cap,
int off)
{
super(buf, mark, pos, lim, cap, off);
this.isReadOnly = true;
}
public IntBuffer duplicate() {
return new HeapIntBufferR(hb,
this.markValue(),
this.position(),
this.limit(),
this.capacity(),
offset);
}

未完待续,以上总结的可能有错误,欢迎指出!!

Netty与NIO的更多相关文章

  1. android netty5.0 编译时 java.lang.NoClassDefFoundError: io.netty.channel.nio.NioEventLoopGroup

    android netty5.0 编译时 java.lang.NoClassDefFoundError: io.netty.channel.nio.NioEventLoopGroup 复制netty包 ...

  2. 漫谈Java IO之 Netty与NIO服务器

    前面介绍了基本的网络模型以及IO与NIO,那么有了NIO来开发非阻塞服务器,大家就满足了吗?有了技术支持,就回去追求效率,因此就产生了很多NIO的框架对NIO进行封装--这就是大名鼎鼎的Netty. ...

  3. Netty、NIO、多线程

    一:Netty.NIO.多线程? 时隔很久终于又更新了!之前一直迟迟未动也是因为积累不够,后面比较难下手.过年期间@李林锋hw发布了一个Netty5.0架构剖析和源码解读,看完也是收获不少.前面的文章 ...

  4. 2.Netty 与 NIO 之前世今生

      2.Netty 与 NIO 之前世今生 本文围绕一下几点阐述: 1. NIO 的核心组件 Buffer.Selector.Channel. 2.何谓多路复用? 3.Netty 支持的功能与特性. ...

  5. [netty4][netty-transport]netty之nio传输层

    [netty4][netty-transport]netty之nio传输层 nio基本处理逻辑 查看这里 Selector的处理 Selector实例构建 NioEventLoop.openSelec ...

  6. Netty(二)Netty 与 NIO 之前世今生

    2.1 Java NIO 三件套 在 NIO 中有几个核心对象需要掌握:缓冲区(Buffer).选择器(Selector).通道(Channel). 2.1.1 缓冲区 Buffer 1.Buffer ...

  7. netty简单NIO模型

    首先是使用java原生nio类库编写的例子,开发一套nio框架不简单,所以选择了netty,该例完成后,是netty举例. package com.smkj.netty; public class T ...

  8. 【Netty】NIO框架Netty入门

    Netty介绍 Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序. 也就是说,Netty ...

  9. 基于Netty的NIO优化实践

    1. 浅谈React模型 2. Netty TCP 3. Netty UTP

随机推荐

  1. 题解-CF101D Castle

    题面 CF101D Castle 给一棵 \(n\) 个节点的带权树,求一种遍历方案,从 \(1\) 出发,每条边走两次,走过所有点,第一次经过每个节点的平均时间最小.输出这个平均时间. 数据范围:\ ...

  2. 思想无语言边界:以 cglib 介绍 AOP 在 java 的一个实现方式

    0. 前言 上接:基于 Source Generators 做个 AOP 静态编织小实验 作为第三篇,我们基于cglib在java做一个简单的aop例子, 以此简单作为例子说一个思路在什么样的语言里面 ...

  3. Kubernetes【K8S】(一):Kubernetes组件

    什么是Kubernetes ​ Kubernetes 是一个可移植的.可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化.Kubernetes拥有一个庞大且快速增长的生态系统. ...

  4. MySQL数据库 数据的更新

    有表A,B,有name,sal,deptNo1,数据更新update update A set sal=500 where name='zhangsan';(一次可更改多个值,用逗号隔开)2,数据的删 ...

  5. 细品 Spring Boot+Thymeleaf,还有这么多好玩的细节!

    @ 目录 1. Thymeleaf 简介 2. 整合 Spring Boot 2.1 基本用法 2.2 手动渲染 3. Thymeleaf 细节 3.1 标准表达式语法 3.1.1 简单表达式 3.1 ...

  6. [水题日常]UVA1625 Color Length

    来整理一下思路- 一句话题意:给两个大写字母的序列,每次取出其中一个数列的第一个元素放到新序列里面,对每个字母\(c\)记它的跨度\(L(c)\)为这个字母最后出现的位置-第一次出现的位置,求新序列所 ...

  7. BloomFilter中保存的数据量

    结果 /** * @author WeiJiQian * BF_CARDINAL_THRESHOLD BF_FALSE_POSITIVE_RATE 保存的数据量 * 100,0000 0.01 391 ...

  8. Spark-2-性能监控方式

    1 Spark Web UI Spark提供了一些基本的Web监控页面,对于日常监控十分有用. 通过http://master:4040(默认端口是4040,可以通过spark.ui.port修改)我 ...

  9. Spark性能调优篇八之shuffle调优

    1 task的内存缓冲调节参数 2 reduce端聚合内存占比 spark.shuffle.file.buffer                     map task的内存缓冲调节参数,默认是3 ...

  10. Web服务器-HTTP相关-快速整一个服务器响应浏览器(3.2.1)

    @ 目录 1.HTTP分析 2.简单服务器 关于作者 1.HTTP分析 当你去访问一个网址的时候,浏览器会发送而各种头信息给服务器 然后服务器根据信息,返回一定数据的格式 最简单的就是下面的代码 自己 ...