Java面试基础篇-IO

UNIX提供5种I/O模型

var code = “7cfcb088-556d-478a-b21d-12b255236dbd”

BIO模型

在进程空间调用recvfrom时被阻塞,直到有数据才返回。
在这里插入图片描述

NIO模型

调用recvfrom时先返回EWOULDBLOCK错误,然后轮询是否有数据。
在这里插入图片描述

I/O复用

linux提供select/poll,其支持多个fd的NIO,但是select/poll本身是阻塞的。epoll采用事件驱动的方式代替顺序扫描,其性能更高。
在这里插入图片描述

信号驱动I/O模型

调用信号处理函数,返回SINGO信号时开始调用recvfrom。
在这里插入图片描述

异步I/O

通知内核某个操作,并整个操作完成的时候通知我们。
在这里插入图片描述

I/O多路复用技术

  • select打开FD限制1024,epoll最大限制是操作系统最大文件句柄数
  • select/poll在socket很大时,需要扫描全部集合。epoll通过fd回调函数实现。
  • 使用mmap技术进行消息传递
  • epoll API更简单

Java的IO演进

  • 1.4 NIO
    增加NIO包。
  • 1.7 NIO2.0

TCP粘包/拆包问题的解决之道

TCP粘包/拆包问题

TCP协议是”流“协议,流是没有间隔的。tcp会根据缓存大小将业务上的大包划分成多个小包发送出去、也可能多个小包合成一个大包发送出去。

TCP粘包/拆包发生的原因

应用层:应用程序写入的字节大小大于套接字接口缓冲区大小。
TCP层:进行MSS大小的tcp分段。
IP层:以太网帧的payload大于MTU进行ip分片。

TCP为了避免被发送方分片,它主动把数据分成小段再交给网络层。 最大的分段大小称为 MSS(Maximum Segment Size )

TCP粘包/拆包问题的解决策略

  1. 消息定长len,例如每个报文固定200字节。那么读取到定长len后就重置计数器开始读取下一个包。
  2. 包尾加换行符分割,如ftp。
  3. 消息头+消息体。消息头包含消息总长度的字段。
  4. 更复杂的应用协议。

Netty解决tcp粘包问题

为了解决tcp粘包/拆包导致的半包读写问题,Netty默认提供了多种编解码器用于处理半包。

LineBasedFrameDecoder:原理是遍历ByteBuf中字节,以换行符分割。
StringDecoder:将接收的byte对象转换为字符串,然后调用后面的handler
如果发送的消息不是以换行符结束的,netty也有其他解码器支持。

分隔符和定长解码器的应用

TCP以流的方式进行数据传输,上层应用协议为了对消息进行区分,通常采用以下4中方式:

  • 消息长度固定,累计读取到长度综合为定长LEN的报文后,就认为读取到了一个完整的消息,将计数器置位,重新开始读取下一个数据报;
  • 将回车换行符作为消息结束符,例如FTP协议,这种方式在文本协议中应用比较广泛;
  • 将特殊的分隔符作为消息的结束标志,回车换行符就是一种特殊的分隔符;
  • 通过在消息头中定义长度字段来标识消息的总长度。

DelimiterBasedFrameDecoder
支持任意字符为分隔符
支持设置单条消息最大长度,如果找了最大长度还没找到分隔符就抛出异常。

FixedLengthFrameDecoder
使用简单,指定包长度就ok。

编解码技术

Java序列化的目的主要有两个:网络传输和对象持久化。

Java序列化缺点

  • 不支持跨语言
  • 序列化后的码流太大
  • 序列化性能低

业界主流的编解码框架

  • Google 的protobuf
  • Facebook的Thrift
  • JBoss的Marshalling

Netty高性能之道

  1. 异步非阻塞通信
    Netty提供了SocketChannel和ServerSocketChannel两种不同的套接字通道实现,并且这两种都支持阻塞和非阻塞模式。

    NioEventLoop由于聚合了Selector(多路复用器),可以同时并发处理上千个SocketChannel,这可以充分提升I/O线程的运行效率。避免频繁I/O阻塞导致的线程挂起。

  2. 高效的Reactor线程模型
    单线程Reactor线程模型
    一个accpetThread接受任务,之后转发到reactor线程中进行处理。
    多线程reactor多线程模型
    有多个accpet线程
    主从Reactor多线程模型
    有多个accpet线程

  3. 无锁的串行化设计
    为了尽可能地避免锁竞争带来的性能损耗,可以通过串行化设计,即消息的处理尽可能在同一个线程内完成,期间不进行线程切换,这样就避免了多线程竞争和同步锁。

  4. 高效的并发编程

  5. 高效的系列化框架

  6. 零拷贝

  7. 内存池

  8. 灵活的TCP参数配置能力

posted @   ByteX  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示