深入分析 Java I/O 的工作机制(网络 I/O 优化)
来源:互联网 发布:3d设计软件 编辑:程序博客网 时间:2024/05/22 08:02
深入分析 Java I/O 的工作机制(网络 I/O 优化)
网络 I/O 优化
网络 I/O 优化通常有一些基本处理原则:
- 一个是减少网络交互的次数:要减少网络交互的次数通常我们在需要网络交互的两端会设置缓存,比如 Oracle 的 JDBC 驱动程序,就提供了对查询的 SQL 结果的缓存,在客户端和数据库端都有,可以有效的减少对数据库的访问。关于 Oracle JDBC 的内存管理可以参考《 Oracle JDBC 内存管理》。除了设置缓存还有一个办法是,合并访问请求:如在查询数据库时,我们要查 10 个 id,我可以每次查一个 id,也可以一次查 10 个 id。再比如在访问一个页面时通过会有多个 js 或 css 的文件,我们可以将多个 js 文件合并在一个 HTTP 链接中,每个文件用逗号隔开,然后发送到后端 Web 服务器根据这个 URL 链接,再拆分出各个文件,然后打包再一并发回给前端浏览器。这些都是常用的减少网络 I/O 的办法。
- 减少网络传输数据量的大小:减少网络数据量的办法通常是将数据压缩后再传输,如 HTTP 请求中,通常 Web 服务器将请求的 Web 页面 gzip 压缩后在传输给浏览器。还有就是通过设计简单的协议,尽量通过读取协议头来获取有用的价值信息。比如在代理程序设计时,有 4 层代理和 7 层代理都是来尽量避免要读取整个通信数据来取得需要的信息。
- 尽量减少编码:通常在网络 I/O 中数据传输都是以字节形式的,也就是通常要序列化。但是我们发送要传输的数据都是字符形式的,从字符到字节必须编码。但是这个编码过程是比较耗时的,所以在要经过网络 I/O 传输时,尽量直接以字节形式发送。也就是尽量提前将字符转化为字节,或者减少字符到字节的转化过程。
- 根据应用场景设计合适的交互方式:所谓的交互场景主要包括同步与异步阻塞与非阻塞方式,下面将详细介绍。
所谓同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功,失败都失败,两个任务的状态可以保持一致。而异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。我们可以用打电话和发短信来很好的比喻同步与异步操作。
在设计到 IO 处理时通常都会遇到一个是同步还是异步的处理方式的选择问题。因为同步与异步的 I/O 处理方式对调用者的影响很大,在数据库产品中都会遇到这个问题。因为 I/O 操作通常是一个非常耗时的操作,在一个任务序列中 I/O 通常都是性能瓶颈。但是同步与异步的处理方式对程序的可靠性影响非常大,同步能够保证程序的可靠性,而异步可以提升程序的性能,必须在可靠性和性能之间做个平衡,没有完美的解决办法。
阻塞与非阻塞主要是从 CPU 的消耗上来说的,阻塞就是 CPU 停下来等待一个慢的操作完成 CPU 才接着完成其它的事。非阻塞就是在这个慢的操作在执行时 CPU 去干其它别的事,等这个慢的操作完成时,CPU 再接着完成后续的操作。虽然表面上看非阻塞的方式可以明显的提高 CPU 的利用率,但是也带了另外一种后果就是系统的线程切换增加。增加的 CPU 使用时间能不能补偿系统的切换成本需要好好评估。
两种的方式的组合
组合的方式可以由四种,分别是:同步阻塞、同步非阻塞、异步阻塞、异步非阻塞,这四种方式都对 I/O 性能有影响。下面给出分析,并有一些常用的设计用例参考。
表 3. 四种组合方式
这种方式通常能提升 I/O 性能,但是会增加 CPU 消耗,要考虑增加的 I/O 性能能不能补偿 CPU 的消耗,也就是系统的瓶颈是在 I/O 还是在 CPU 上。异步阻塞这种方式在分布式数据库中经常用到,例如在网一个分布式数据库中写一条记录,通常会有一份是同步阻塞的记录,而还有两至三份是备份记录会写到其它机器上,这些备份记录通常都是采用异步阻塞的方式写 I/O。
异步阻塞对网络 I/O 能够提升效率,尤其像上面这种同时写多份相同数据的情况。异步非阻塞这种组合方式用起来比较复杂,只有在一些非常复杂的分布式情况下使用,像集群之间的消息同步机制一般用这种 I/O 组合方式。如 Cassandra 的 Gossip 通信机制就是采用异步非阻塞的方式。
它适合同时要传多份相同的数据到集群中不同的机器,同时数据的传输量虽然不大,但是却非常频繁。这种网络 I/O 用这个方式性能能达到最高。
虽然异步和非阻塞能够提升 I/O 的性能,但是也会带来一些额外的性能成本,例如会增加线程数量从而增加 CPU 的消耗,同时也会导致程序设计的复杂度上升。如果设计的不合理的话反而会导致性能下降。在实际设计时要根据应用场景综合评估一下。
下面举一些异步和阻塞的操作实例:
在 Cassandra 中要查询数据通常会往多个数据节点发送查询命令,但是要检查每个节点返回数据的完整性,所以需要一个异步查询同步结果的应用场景,部分代码如下:
class AsyncResult implements IAsyncResult{ private byte[] result_; private AtomicBoolean done_ = new AtomicBoolean(false); private Lock lock_ = new ReentrantLock(); private Condition condition_; private long startTime_; public AsyncResult(){ condition_ = lock_.newCondition();// 创建一个锁 startTime_ = System.currentTimeMillis(); } /*** 检查需要的数据是否已经返回,如果没有返回阻塞 */ public byte[] get(){ lock_.lock(); try{ if (!done_.get()){condition_.await();} }catch (InterruptedException ex){ throw new AssertionError(ex); }finally{lock_.unlock();} return result_; } /*** 检查需要的数据是否已经返回 */ public boolean isDone(){return done_.get();} /*** 检查在指定的时间内需要的数据是否已经返回,如果没有返回抛出超时异常 */ public byte[] get(long timeout, TimeUnit tu) throws TimeoutException{ lock_.lock(); try{ boolean bVal = true; try{ if ( !done_.get() ){ long overall_timeout = timeout - (System.currentTimeMillis() - startTime_); if(overall_timeout > 0)// 设置等待超时的时间 bVal = condition_.await(overall_timeout, TimeUnit.MILLISECONDS); else bVal = false; } }catch (InterruptedException ex){ throw new AssertionError(ex); } if ( !bVal && !done_.get() ){// 抛出超时异常 throw new TimeoutException("Operation timed out."); } }finally{lock_.unlock(); } return result_; } /*** 该函数拱另外一个线程设置要返回的数据,并唤醒在阻塞的线程 */ public void result(Message response){ try{ lock_.lock(); if ( !done_.get() ){ result_ = response.getMessageBody();// 设置返回的数据 done_.set(true); condition_.signal();// 唤醒阻塞的线程 } }finally{lock_.unlock();} } }
总结
本文阐述的内容较多,从 Java 基本 I/O 类库结构开始说起,主要介绍了磁盘 I/O 和网络 I/O 的基本工作方式,最后介绍了关于 I/O 调优的一些方法。
参考资料
学习
- 查看文章 《深入分析Java中文编码问题》(developerWorks,2011 年 7 月):详细介绍 Java 中编码问题出现的根本原因,你将了解到:Java 中经常遇到的几种编码格式的区别;Java 中经常需要编码的场景;出现中文问题的原因分析;在开发 Java web 程序时可能会存在编码的几个地方,一个 HTTP 请求怎么控制编码格式?如何避免出现中文问题?
- 《Oracle JDBC内存管理》:这里详细分析 Oracle JDBC 内存管理的处理方式。
- 《Jetty 的工作原理和与 Tomcat 的比较》:这里介绍了 Jetty 是如何使用 NIO 技术处理 HTTP 连接请求的,以及与 Tomcat 处理有何不同之处。
- Java I/O Performance:sun.com 上的文章,介绍了一些 I/O 调优的基本方法。
- Java NIO.2:这里介绍了 JDK7 里面的新的 I/O 技术,可以参考学习下。
- Understanding Disk I/O:这里介绍了一点关于磁盘 I/O 一些检测和调优方法,本文也引用了一些知识点。
- developerWorks Java 技术专区:这里有数百篇关于 Java 编程各个方面的文章。
讨论
- 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。
地址:http://www.ibm.com/developerworks/cn/java/j-lo-javaio/
- 深入分析 Java I/O 的工作机制(网络 I/O 优化)
- 深入分析 Java I/O 的工作机制(网络 I/O 优化)
- 深入分析 Java I/O 的工作机制(网络 I/O 优化)
- 深入分析Java I/O工作机制
- 深入分析java I/O工作机制
- 深入分析Java I/O工作机制
- 深入分析Java I/O 工作机制
- 深入分析Java I/O 工作机制
- 深入分析Java I/O工作机制
- 深入分析Java I/O 工作机制
- 深入分析Java I/O 工作机制
- java深入分析I/O流工作机制05-I/O性能优化
- 深入分析 Java I/O 的工作机制
- 深入分析 Java I/O 的工作机制
- 转载:深入分析 Java I/O 的工作机制
- 深入分析 Java I/O 的工作机制
- 深入分析 Java I/O 的工作机制
- 深入分析 Java I/O 的工作机制
- bat批处理 for命令
- MySQL复制原理图解
- 因为数据库正在使用,所以无法获得对数据库的独占访问权---还原或删除数据库的解决方法
- 将表中的某个字段转换成标识字段,并保留原来的值
- 迷宫~哈哈~终于懂了。。
- 深入分析 Java I/O 的工作机制(网络 I/O 优化)
- vim中小写字母和大学字母的转换
- 我看TechEd 2012之App时代降临
- IOS中的编码格式—char*汉字转为NSString*
- UITableView 行自适应高度的实现
- /usr/bin/ld: cannot find -lGL
- GDI画sin曲线
- git push 操作
- Oracle 不同用户之间 大量数据转移的方法测试