飞不高 发表于 2022-12-28 16:02:25

一分钟搞定Netty 三大组件,如果搞不定,再看3遍

1. 三大组件简介

Channel 与 Buffer
Java NIO 系统的核心在于:通道 (Channel) 和缓冲区 (Buffer)。通道表示打开到 IO 设备 (例如:文件、套接字) 的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道 以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理
简而言之,通道负责传输,缓冲区负责存储
常见的 Channel 有以下四种,其中 FileChannel 主要用于文件传输,其余三种用于网络通信

[*]FileChannel
[*]DatagramChannel
[*]SocketChannel
[*]ServerSocketChannel
Buffer 有以下几种,其中使用较多的是 ByteBuffer

[*]ByteBuffer

[*]MappedByteBuffer
[*]DirectByteBuffer
[*]HeapByteBuffer

[*]ShortBuffer
[*]IntBuffer
[*]LongBuffer
[*]FloatBuffer
[*]DoubleBuffer
[*]CharBuffer
https://img2023.cnblogs.com/other/2893049/202212/2893049-20221228150119648-488317266.png
1、Selector

在使用 Selector 之前,处理 socket 连接还有以下两种方法
使用多线程技术
为每个连接分别开辟一个线程,分别去处理对应的 socket 连接
https://img2023.cnblogs.com/other/2893049/202212/2893049-20221228150119875-621166407.png
这种方法存在以下几个问题

[*]内存占用高

[*]每个线程都需要占用一定的内存,当连接较多时,会开辟大量线程,导致占用大量内存

[*]线程上下文切换成本高
[*]只适合连接数少的场景

[*]连接数过多,会导致创建很多线程,从而出现问题

使用线程池技术
使用线程池,让线程池中的线程去处理连接
https://img2023.cnblogs.com/other/2893049/202212/2893049-20221228150120090-374076516.png
这种方法存在以下几个问题

[*]阻塞模式下,线程仅能处理一个连接

[*]线程池中的线程获取任务(task)后,只有当其执行完任务之后(断开连接后),才会去获取并执行下一个任务
[*]若 socke 连接一直未断开,则其对应的线程无法处理其他 socke 连接

[*]仅适合短连接场景

[*]短连接即建立连接发送请求并响应后就立即断开,使得线程池中的线程可以快速处理其他连接

使用选择器
selector 的作用就是配合一个线程来管理多个 channel(fileChannel 因为是阻塞式的,所以无法使用 selector),,获取这些 channel 上发生的事件,这些 channel 工作在非阻塞模式下,当一个 channel 中没有执行任务时,可以去执行其他channel 中的任务。适合连接数多,但流量较少的场景
https://img2023.cnblogs.com/other/2893049/202212/2893049-20221228150120300-912283552.png
若事件未就绪,调用 selector 的 select () 方法会阻塞线程,直到 channel 发生了就绪事件。这些事件就绪后,select 方法就会返回这些事件交给 thread 来处理
2、ByteBuffer

使用案例

使用方式


[*]向 buffer 写入数据,例如调用 channel.read (buffer)
[*]调用 flip () 切换至
读模式

[*]flip 会使得 buffer 中的 limit 变为 position,position 变为 0

[*]从 buffer 读取数据,例如调用 buffer.get ()
[*]调用 clear () 或者 compact () 切换至
写模式

[*]调用 clear () 方法时 position=0,limit 变为 capacity
[*]调用 compact () 方法时,会将缓冲区中的未读数据压缩到缓冲区前面

[*]重复以上步骤
使用 ByteBuffer 读取文件中的内容
public class TestByteBuffer {
   public static void main(String[] args) {
      try (FileChannel channel = new FileInputStream("stu.txt").getChannel()){
            //给缓冲区 分配空间
            ByteBuffer buffer = ByteBuffer.allocate(10);
            int read = 0 ;
            StringBuilder builder = new StringBuilder();
            while ((read =channel.read(buffer))>0){
                //切换成 读模式 limit = position; position=0
                buffer.flip();
                while (buffer.hasRemaining()){
                  builder.append((char)buffer.get());
                }
                //清空字节数组 切换成 写模式 position=0 ;limit = capacity
                buffer.clear();
            }
            System.out.println(builder.toString());
      } catch (Exception e) {
            e.printStackTrace();
      } finally {
            
      }
    }
}打印结果:
0123456789abcdef核心属性

字节缓冲区的父类 Buffer 中有几个核心属性,如下
// Invariants: mark > 4];    private static final String[] BYTE2HEX = new String;    private static final String[] BYTEPADDING = new String;    static {      final char[] DIGITS = "0123456789abcdef".toCharArray();      for (int i = 0; i < 256; i++) {            HEXDUMP_TABLE;            HEXDUMP_TABLE[(i > 4;      final int remainder = length & 0xF;      // Dump the rows which have 16 bytes.      for (int row = 0; row < fullRows; row++) {            int rowStartIndex = (row
页: [1]
查看完整版本: 一分钟搞定Netty 三大组件,如果搞不定,再看3遍