【Netty】IO 模型简介 ( Netty 特点 | Netty 应用场景 | Java 三种 IO 模型 | BIO 模型 )

  • 时间:
  • 浏览:
  • 来源:互联网

文章目录

        • I . Netty 简介
        • II . Netty 应用场景
        • III . Java I/O 模型
        • IV . BIO 概念
        • V . BIO 开发流程
        • VI . BIO 实例
        • VII . BIO 模型实例分析





I . Netty 简介



1 . Netty 是网络开发框架 , 其有如下特点 ;


① 异步 : 与同步相对 , 操作之间 不产生阻塞 , 发出请求后可以不等待回应 , 继续执行后面的代码逻辑 ;

② 事件驱动 : 任何操作 , 都需要一个触发事件 , 如按钮点击 , 回调操作等 ;


2 . Netty 作用 :


① 用途 : 开发 高并发 的 网络 IO 程序 , 其性能 与 可靠性都很高 ;

② 服务器程序 : Netty 网络应用部署在服务器中 , 主要是与客户端进行高并发交互 ;

③ 点对点 ( P2P ) 程序 : 点对点数据传输 ;


3 . Netty 层次 : Netty 最底层是基于 TCP/IP 协议 , 然后封装了原生的网络编程及并发编程 , 在之上使用了 NIO 进行进一步封装 , 最上层才是 Netty 提供的服务 ;


① 底层协议 : TCP 协议 ;

② 原生 API 封装 : 该框架对原生的网络编程及并发操作进行了封装和优化 ;

③ 本质 : Netty 的本质是在 Java NIO 基础上封装的框架 , 适合开发网络服务器 , 如游戏服务器等 ;





II . Netty 应用场景



1 . 远程过程调用 ( RPC ) 框架 : 分布式系统中的远程过程调用框架 , 看重 Netty 的 高并发 , 高性能 的能力 , 将其作为分布式远程调用的网络通信组件 ; 这些框架的底层都是使用 Netty 实现的 ;


2 . 游戏服务器 : 手游 / 大型网游 等后台服务器基本都是基于 Netty 开发 , Netty 作为服务器 高性能 高并发 的通信模块 , 提供了 TCP / UDP / HTTP 协议通信底层功能 , 在这个基础上开发交互的业务逻辑 ;





III . Java I/O 模型



1 . Java IO 模型 : 收发数据的通道模式 , 工作模式 是 同步 还是 异步 , 等待机制是 阻塞 还是 非阻塞 ;


① IO 模型分类 : 根据上述特点可将 Java 中的网络 IO 模型分为 BIO , NIO , AIO , 33 类 ;

② 三种模型性能对比 : 三种模型性能依次从低到高排列为 BIO << NIO << AIO ;



2 . BIO 模型 : 同步阻塞模型 , 在服务器端 , 针对每个客户端的连接请求 , 都要启动一个线程处理相关的业务逻辑 ;


① 适用场景 : 连接数 少 ;

② 最小 JDK 支持版本 : 1.4 ;

③ 弊端 : 对服务器资源占用高 , 如果客户端只是连接 , 不做任何操作 , 那么也占用了服务器的资源 ;

④ 优点 : 程序简单 , 容易理解 ;

⑤ 瓶颈 : 传统的 BIO 处理大并发数据量时 , 有瓶颈 ;

⑥ BIO 模型中 客户端 与 服务器端 交互 图示 : 服务器端的线程数 与 客户端一样 ;
在这里插入图片描述

上图中 , 如果有 1 万个客户端 , 那么对应的服务器端就会有 1 万个线程 ;



3 . NIO 模型 : 同步非阻塞模型 , 在服务器端 , 一个线程处理多个客户端连接 , 客户端连接服务器时 , 会在多路复用器上注册 , 多路复用器会一直轮训是否有连接请求 , 如果有就处理 , 如果没有不做任何操作 ;


① 适用场景 : 连接数 多 , 都是短连接 ; 如 : 聊天室 , 游戏服务器 等 ;

② 最小 JDK 支持版本 : 1.4 ;

③ 多路复用器 Selector : 可以理解成一个选择器 ;

④ NIO 实现基础 : 客户端与服务器端不是时刻都在进行数据交互 , 而是间歇性的 , 大部分时间都是出于静默 ( 非活动 ) 状态 ;

⑤ NIO 模型中 客户端 与 服务器端 交互 图示 : 服务器端启动一个线程 , 线程中维护 Selector 选择器 , 该选择器会维护多个通道 , 当某个通道有事件发生 , 即客户端有请求进来 , 那么处理该事件 ;

在这里插入图片描述



4 . AIO 模型 : 异步非阻塞模型 , 引入异步通道概念 , 并调用操作系统参与并发任务 ;


① 适用场景 : 连接 的个数多 , 并且都是 长连接 ;

② 最小 JDK 支持版本 : 1.7 ;

③ 工作流程 : 先判定客户端请求的有效性 , 有效请求才启动线程 ;

④ 当前状态 : NIO 的进阶版 , 该技术是 JDK 1.7 引入 , 目前应用不是很广 ; Netty 是基于 NIO 模型的 ;





IV . BIO 概念



BIO 简介 : Blocking IO , 阻塞 IO , 传统 Java IO 编程 ;


① 特点 : 同步阻塞 ;

② 连接 对应 线程 : 服务器端 每维护 一个连接 , 都要启动一个相应的线程 ; 这样就会造成性能浪费 ;

③ BIO 改进方案 : 使用线程池机制改进 BIO , 每个线程可以处理客户端连接 ;

④ NIO 基础 : BIO 是 NIO 的基础 ;





V . BIO 开发流程



1 . 连接流程 : 以 TCP 连接为例 ;


① 服务器端 监听 : 服务器端创建 ServerSocket , 监听接口 ;

② 客户端 连接 : 创建 Socket , 向服务器端申请连接 ;

③ 服务器端 线程 : 接受客户端连接 , 创建一个线程 , 专门与该客户端进行通信 ;



2 . 交互过程 :


① 客户端请求 : 客户端通过建立的连接 , 向服务器端发送请求 , 服务器端如果有线程响应该请求 , 那么处理该请求 , 如果没有线程响应 , 那么等待 , 之后进行超时处理 ;

② 服务器端响应 : 服务器端响应了客户端请求 , 客户端在请求返回后 , 继续执行后面的代码逻辑 ;





VI . BIO 实例



1 . BIO 示例 :

① 服务器端 : 编写服务器端 , 监听 8888 端口 , 阻塞等待客户端连接 , 连接成功后 , 创建线程 , 线程中阻塞等待客户端发送请求数据 ;

② 客户端 : 编写一个客户端 , 请求服务器的 8888 端口号 , 客户端发送 “Hello World” 字符串给服务器端 ;

③ Telnet 客户端 : 使用 Telnet 客户端向上述服务器端 8888 端口 发送 “Hello World” 字符串请求 ;


2 . 服务器代码示例 :

package kim.hsl.bio;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TCPServer {
    public static void main(String[] args) {
        try {
            //创建线程池
            ExecutorService threadPool = Executors.newCachedThreadPool();
            //创建服务器套接字
            ServerSocket serverSocket = new ServerSocket(8888);
            System.out.println("服务器启动,监听 8888 端口");
            while (true){
                //阻塞, 等待客户端连接请求 ( 此处是第一个阻塞点 )
                Socket socket = serverSocket.accept();
                System.out.println("客户端连接成功");
                //线程池启动线程
                threadPool.execute(new ClientRquest(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 与客户端交互类
     */
    static class ClientRquest implements Runnable {
        private Socket socket;
        public ClientRquest(Socket socket) {
            this.socket = socket;
        }
        @Override
        public void run() {
            try {
                clientRequest();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //最终要将 Socket 关闭, 如果出异常继续捕获
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        public void clientRequest() throws IOException {
            //获取输入流, 读取客户端写入的信息
            byte[] buffer = new byte[1024];
            InputStream is = socket.getInputStream();

            System.out.println("等到客户端请求");
            //此处会阻塞等待客户端的请求 ( 此处是第二个阻塞点 )
            int count = is.read(buffer);
            String request = new String(buffer, 0, count);
            System.out.println("客户端请求到达 : " + request);
        }
    }
}


3 . 客户端代码示例 :

package kim.hsl.bio;

import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.Socket;

public class TCPClient {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket();
            InetSocketAddress inetSocketAddress =
                    new InetSocketAddress(
                            Inet4Address.getLocalHost(),   //本机IP地址
                            8888                      //端口号
                    );
            System.out.println("客户端开始连接 ...");
            //此处会阻塞等待连接成功
            socket.connect(inetSocketAddress);
            System.out.println("客户端连接成功");
            //连接成功后, 开始执行后续操作
            socket.getOutputStream().write("Hello World".getBytes());
            System.out.println("客户端写出 Hello World 成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

4 . 执行结果 :

① 启动服务器 :

在这里插入图片描述

② 启动客户端 :

在这里插入图片描述

在这里插入图片描述


③ 使用 Telnet 客户端测试 localhost 8888 端口 :

  • 建立连接 : 在命令行工具中 , 使用 telnet localhost 8888 连接本机的 8888 端口 ;
    在这里插入图片描述

  • 连接成功后 , 按下 Ctrl + ] 快捷键 , 进入 Telnet 命令行 , 输入 send Hello World 命令 , 向本机的 8888 端口发送 Hello World 字符串 ;
    在这里插入图片描述

  • 服务器端显示 :
    在这里插入图片描述





VII . BIO 模型实例分析



BIO 模型实例分析 : 针对上述 BIO 实例 , 从性能 , 线程个数 , 阻塞 等角度分析 BIO 模型 ;


① 线程维护个数 : 在服务器端 , 需要针对每个客户端连接都创建一个线程 , 有多少连接 , 就需要有多少线程 ;

② 性能分析 : 如果客户端数量很多 , 那么大量客户端同时连接 , 其并发数量很大 , 对系统的资源占用较高 ;

③ 阻塞分析 : BIO 模型中 , 服务器端有两处阻塞 , 一个是等待客户端连接 , 一个是连接后 , 等待客户端发出请求数据 , 后者的阻塞等待完全就是对资源的浪费 , 没有数据交互 , 一直占用资源 ;

本文链接http://element-ui.cn/news/show-340085.aspx