java io模型浅谈
Posted 小技一碟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java io模型浅谈相关的知识,希望对你有一定的参考价值。
作为一个应用程序,除了单机版演示程序外,基本上都需要与外部进行通讯。如文件读写,mysql,redis,mq等第三方数据存储通讯。而应用系统内组件间的通信就需要自定义协议进行数据交换,常见的应用层如http、socket都涉及到io模型机制。
网上大把的讲解boi和nio原理的文章,下面主要根据Doug Lea 大神在State University of New York at Oswego 教程结合自己的个人理解进行java io的设计思路的分析,有疑问的地方大家可以拿出来讨论。
对于一个后台服务程或者中间库组件,大部分有如下的基础处理逻辑:
Read Request 读取请求内容
Decode Request 解码请求内容
Process Service 业务逻辑处理
Encode Response 编码应答消息
Send Response 发送请求应答
因业务逻辑不同,应用程序在上面每个步骤所进行的处理不同,比如读取请求内容,就有json、xml、二进制等内容格式。 Decode Request就是根据具体的协议进行解码。
基于上面的处理步骤,常见的io模型有如下几种:
BIO(典型IO模型)
每个request开启一个线程,处理上面所有的步骤。下面简单伪代码说明
server接受client请求
每个请求启动一个线程进行处理(这里可以使用线程池)
优点:编码简单,业务逻辑清晰。 适用于业务量不大情景;
缺点:每个client request一个线程进行处理,业务量持续增长,过多线程会增加程序内存,且频繁线程切换,会增加业务处理时间。即使采用线程池,线程池无空闲线程时,多余请求就会处理等待状态,无法及时应答客户端。同时,无法做到平滑扩容。
NIO(异步IO模型)
异步io的思想就是分而治之,我们可以这样来理解,上面的所有步骤(read request、decode request 、handler server、encode response 、send response)分成不同的非阻塞事件(可以类似button click event),并给上面的所有事件添加handler回调处理(类似 click listener)。当事件被触发时候(button be clicked),执行响应的handler事件。
button 异步事件驱动
当然 异步io只是类似上面的思想,具体的实现和设计有一定区别,大家可以通过熟悉的button事件来进行理解。
优点:
更少的资源,不需要每个client一个线程进行处理;
不需要频繁的线程间调度,更少的锁操作
编程难度增加,一个完整的逻辑被切分成不同事件进行处理
所有的事件分发通过一个线程进行,有一定成都的延时
缺点:
NIO(reactor 单线程模型)
单线程reactor模型,直接上图类比
参照上图的button 点击事件类比:
Ractor模块负责将io事件分发到对应的handler,类似button 中awt thread;
acceptor、read、send 负责io事件的处理,类似awt ActionListeners;
io事件的绑定和handler绑定,类似act addActionListeners;
IO事件在这里包括socket的连接和断开,数据的读写。其中acceptor负责处理socket连接事件,read 负责数据读,send负责数据写;
这里的实现需要java nio的支持,包括 channles、buffers、Selectors、SelectionKeys;
下面就按照上面的总体思想,用java nio 来实现reactor模型。
reactor io事件分发模块
acceptor事件处理(client connect)
Read Write io事件NioHandler处理(这里展示client连接上了,就发送ping ,server收到会用pong)
向selector注册read 事件
selector 当read准备好后,会通过reactor的将read事件 分发到此handler(reactor代码的dispatch方法),这里将读写事件在同一个handler中进行处理。
以上为简单的nio reactor模型的实现,大家可以对照button click 流程进行理解。
NIO(reactor 多线程模型)
在上面的单线程reactor模型中,所有io事件处理都是一个线程中进行,对于一些小容量应用场景,可以使用单线程模型。但是对于高负载、大并发的应用场景却不合适。
work Thread 线程池:reactor只负责分发io事件,事件处理由work Thread处理。下图可以看到,hander收到读写事件后,先可以放入一个queue中,保证io 线程的处理效率,在通过一个thread pool,来消费task queue中的事件。
多reacotr 线程:上面的方案中,reactor io线程还是只有一个,对于高负载、大并发的应用场景仍会无法满足。
可以直接在acceptor中对于不同的client 连接注册到到对应的selector中,这样每个连接上上来的client可以注册到对应的selector中,使用对应的io thread 来处理io事件:
本篇文章主要从设计思路上面介绍nio reactor原理,大名鼎鼎的netty框架其实也是按照reacotr模型进行设计,大家在了解reactor模型的基础上,可以看下netty的原理,有时间可以和大家分享下,可以达到更好的效果。
以上是关于java io模型浅谈的主要内容,如果未能解决你的问题,请参考以下文章