Netty-NIO 详解、说明、优缺

Liunx IO模型

1、阻塞IO模型

2、非阻塞IO模型

3、IO复用

4、信号驱动式IO

5、异步IO

Liunx IO流程

Netty-NIO 详解、说明、优缺_第1张图片

等待数据准备好

从内核向进程复制数据

阻塞IO模式

Netty-NIO 详解、说明、优缺_第2张图片

非阻塞式IO模型

Netty-NIO 详解、说明、优缺_第3张图片

复用IO模型

Netty-NIO 详解、说明、优缺_第4张图片

信号驱动式I/O模型

Netty-NIO 详解、说明、优缺_第5张图片

异步I/O模型

Netty-NIO 详解、说明、优缺_第6张图片

各种I/O模型的比较

Netty-NIO 详解、说明、优缺_第7张图片

同步、异步

同步I/O操作:导致请求进程阻塞,直到I/O操作完成。

异步I/O操作:不导致请求进程阻塞。

Blocking I/O

Netty-NIO 详解、说明、优缺_第8张图片

package demo07;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class NnServer {
    private int port;

    public NnServer(int port) {
        this.port = port;
    }

    public void startServer() {
        ServerSocket echoServer = null;
        int i = 0;
        System.out.println("服务器在端口[" + this.port + "]等待客户请求......");
        try {
            echoServer = new ServerSocket(this.port);
            while (true) {
                Socket clientRequest = echoServer.accept();
                handleRequest(clientRequest, i++);
            }
        } catch (IOException e) {
            System.out.println(e);
        }
    }

    private void handleRequest(Socket clientSocket, int clientNo) {
        PrintStream os = null;
        BufferedReader in = null;
        try {
            in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            os = new PrintStream(clientSocket.getOutputStream());
            String inputLine;
            while ((inputLine = in.readLine()) != null) {

                // 输入'Quit'退出
                if (inputLine.equals("Quit")) {
                    System.out.println("关闭与客户端[" + clientNo + "]......" + clientNo);
                    os.close();
                    in.close();
                    clientSocket.close();
                    break;
                } else {
                    System.out.println("来自客户端[" + clientNo + "]的输入: [" + inputLine + "]!");
                    os.println("来自服务器端的响应:" + inputLine);
                }
            }
        } catch (IOException e) {
            System.out.println("Stream closed");
        }
    }

    public static void main(String[] args) throws IOException {
        new NnServer(8080).startServer();
    }
}

package demo07;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class EchoClient {
    public static void main(String[] args) {

        Socket echoSocket = null;
        PrintWriter out = null;
        BufferedReader in = null;

        try {

            echoSocket = new Socket("127.0.0.1", 8080);
            out = new PrintWriter(echoSocket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(
                    echoSocket.getInputStream()));
            System.out.println("连接到服务器......");
            System.out.println("请输入消息[输入\"Quit\"]退出:");
            BufferedReader stdIn = new BufferedReader(new InputStreamReader(
                    System.in));
            String userInput;

            while ((userInput = stdIn.readLine()) != null) {
                out.println(userInput);
                System.out.println(in.readLine());

                if (userInput.equals("Quit")) {
                    System.out.println("关闭客户端......");
                    out.close();
                    in.close();
                    stdIn.close();
                    echoSocket.close();
                    System.exit(1);
                }
                System.out.println("请输入消息[输入\"Quit\"]退出:");
            }
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host: PallaviÕs MacBook Pro.");
            System.exit(1);
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for "
                    + "the connection to: PallaviÕs MacBook Pro.");
            System.exit(1);
        }

    }

}

NIO

NIO = New I/O

NIO 1: JSR 51

JDK 1.4引入

http://jcp.org/en/jsr/detail?id=051

NIO 2: JSR203

JDK 7

http://jcp.org/en/jsr/detail?id=203

NIO

Buffers
Channels
Selectors

NIO 2.0

Update
New File System API
Asynchronous I/O

Netty-NIO 详解、说明、优缺_第9张图片

NIO Buffer

一个 Buffer 本质上是内存中的一块, 可以将数据写入这块内存, 从这块内存获取数据

java.nio 定义了以下几个 Buffer 的实现

Netty-NIO 详解、说明、优缺_第10张图片

Java NIO Buffer三大核心概念:position、limit、capacity
最好理解的当然是 capacity,它代表这个缓冲区的容量,一旦设定就不可以更改。比如 capacity 为 1024 的 IntBuffer,代表其一次可以存放 1024 个 int 类型的值。
一旦 Buffer 的容量达到 capacity,需要清空 Buffer,才能重新写入值。

Netty-NIO 详解、说明、优缺_第11张图片

从写操作模式到读操作模式切换的时候(flip),position 都会归零,这样就可以从头开始读写了。
写操作模式下,limit 代表的是最大能写入的数据,这个时候 limit 等于 capacity。
写结束后,切换到读模式,此时的 limit 等于 Buffer 中实际的数据大小,因为 Buffer 不一定被写满了。

Netty-NIO 详解、说明、优缺_第12张图片

java.nio.buffer,缓冲区抽象

ByteBuffer

理解capacity、limit、position、mark
0 – mark – position – limit – capacity

Direct ByteBuffer VS. non-direct ByteBuffer

Non-direct ByteBuffer

HeapByteBuffer,标准的java类
维护一份byte[]在JVM堆上
创建开销小

Direct ByteBuffer

底层存储在非JVM堆上,通过native代码操作
-XX:MaxDirectMemorySize=
创建开销大

Netty-NIO 详解、说明、优缺_第13张图片

Netty-NIO 详解、说明、优缺_第14张图片

Netty-NIO 详解、说明、优缺_第15张图片

Netty-NIO 详解、说明、优缺_第16张图片

Buffer创建

1、allocate/allocateDirect方法

2、wrap方法

Netty-NIO 详解、说明、优缺_第17张图片

Buffer读取

1、put/get方法
2、flip方法
3、mark/reset方法
4、compact方法
5、rewind/clear

Buffer复制 – 浅复制


1、duplicate方法

2、asReadOnlyBuffer方法

3、slice方法

Java NIO Channel
所有的 NIO 操作始于通道,通道是数据来源或数据写入的目的地,主要地, java.nio 包中主要实现的以下几个 Channel:

Netty-NIO 详解、说明、优缺_第18张图片

FileChannel:文件通道,用于文件的读和写
DatagramChannel:用于 UDP 连接的接收和发送
SocketChannel:把它理解为 TCP 连接通道,简单理解就是 TCP 客户端
ServerSocketChannel:TCP 对应的服务端,用于监听某个端口进来的请求

Netty-NIO 详解、说明、优缺_第19张图片

Netty-NIO 详解、说明、优缺_第20张图片

Java NIO Selector

Selector是Java NIO中的一个组件,用于检查一个或多个NIO Channel的状态是否处于可读、可写

如此可以实现单线程管理多个channels,也就是可以管理多个网络链接
Netty-NIO 详解、说明、优缺_第21张图片

java.nio.channels.Selector

支持IO多路复用的抽象实体

注册Seletable Channel

SelectionKey —— 表示Selector和被注册的channel之间关系,一份凭证

SelectionKey 保存channel感兴趣的事件

Selector.select 更新所有就绪的SelectionKey的状态,并返回就绪的channel个数

迭代Selected Key集合并处理就绪channel

Netty-NIO 详解、说明、优缺_第22张图片

创建Selector(Creating a Selector)

Selector selector = Selector.open();

注册Channel到Selector上(Registering Channels with the Selector)

channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

register的第二个参数,这个参数是一个“关注集合”,代表关注的channel状态,
有四种基础类型可供监听, 用SelectionKey中的常量表示如下:

SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE

从Selector中选择channel(Selecting Channels via a Selector)

一旦向Selector注册了一个或多个channel后,就可以调用select来获取channel
select方法会返回所有处于就绪状态的channel
select方法具体如下:

int select()
int select(long timeout)
int selectNow()

select()方法的返回值是一个int,代表有多少channel处于就绪了。也就是自上一次select后有多少channel进入就绪。

selectedKeys()

在调用select并返回了有channel就绪之后,可以通过选中的key集合来获取channel,这个操作通过调用selectedKeys()方法:

Set selectedKeys = selector.selectedKeys();    
Set selectedKeys = selector.selectedKeys();

Iterator keyIterator = selectedKeys.iterator();

while(keyIterator.hasNext()) {

    SelectionKey key = keyIterator.next();

    if(key.isAcceptable()) {
        // a connection was accepted by a ServerSocketChannel.

    } else if (key.isConnectable()) {
        // a connection was established with a remote server.

    } else if (key.isReadable()) {
        // a channel is ready for reading

    } else if (key.isWritable()) {
        // a channel is ready for writing
    }

    keyIterator.remove();
}

NIO带来了什么

事件驱动模型

避免多线程
单线程处理多任务

非阻塞IO,IO读写不再阻塞,而是返回0

基于block的传输,通常比基于流的传输更高效

更高级的IO函数,zero-copy

IO多路复用大大提高了java网络应用的可伸缩性和实用性

使用NIO = 高性能

NIO不一定更快的场景

客户端应用
连接数<1000
并发程度不高
局域网环境下

NIO完全屏蔽了平台差异(Linux poll/select/epoll, FreeBSD Kqueue)

NIO仍然是基于各个OS平台的IO系统实现的,差异仍然存在

使用NIO做网络编程很容易

离散的事件驱动模型,编程困难
陷阱重重

你可能感兴趣的:(Netty)