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

打印 上一主题 下一主题

主题 548|帖子 548|积分 1644

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

1、Selector

在使用 Selector 之前,处理 socket 连接还有以下两种方法
使用多线程技术
为每个连接分别开辟一个线程,分别去处理对应的 socket 连接

这种方法存在以下几个问题

  • 内存占用高

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

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

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

使用线程池技术
使用线程池,让线程池中的线程去处理连接

这种方法存在以下几个问题

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

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

  • 仅适合短连接场景

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

使用选择器
selector 的作用就是配合一个线程来管理多个 channel(fileChannel 因为是阻塞式的,所以无法使用 selector),,获取这些 channel 上发生的事件,这些 channel 工作在非阻塞模式下,当一个 channel 中没有执行任务时,可以去执行其他channel 中的任务。适合连接数多,但流量较少的场景

若事件未就绪,调用 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 读取文件中的内容
  1. public class TestByteBuffer {
  2.      public static void main(String[] args) {
  3.         try (FileChannel channel = new FileInputStream("stu.txt").getChannel()){
  4.             //给缓冲区 分配空间
  5.             ByteBuffer buffer = ByteBuffer.allocate(10);
  6.             int read = 0 ;
  7.             StringBuilder builder = new StringBuilder();
  8.             while ((read =channel.read(buffer))>0){
  9.                 //切换成 读模式 limit = position; position=0
  10.                 buffer.flip();
  11.                 while (buffer.hasRemaining()){
  12.                     builder.append((char)buffer.get());
  13.                 }
  14.                 //清空字节数组 切换成 写模式 position=0 ;limit = capacity
  15.                 buffer.clear();
  16.             }
  17.             System.out.println(builder.toString());
  18.         } catch (Exception e) {
  19.             e.printStackTrace();
  20.         } finally {
  21.             
  22.         }
  23.     }
  24. }
复制代码
打印结果:
  1. 0123456789abcdef
复制代码
核心属性

字节缓冲区的父类 Buffer 中有几个核心属性,如下
[code]// Invariants: mark > 4];    private static final String[] BYTE2HEX = new String[256];    private static final String[] BYTEPADDING = new String[16];    static {        final char[] DIGITS = "0123456789abcdef".toCharArray();        for (int i = 0; i < 256; i++) {            HEXDUMP_TABLE[i >> 4 & 0x0F];            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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

飞不高

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表