漫谈Java IO之基础篇

Java的网络编程如果不是专门搞服务器性能开发或者消息分发,几乎可能涉及不到。但是它却是面试找工作必问的一个知识点,涵盖的知识体系也非常广泛,从Java底层IO原理到操作系统内核组成,再到网络TCP、UDP、HTTP的应用实践….因此,即便是职场多年的老油条,仍然需要时刻复习,更别提我这种只有七秒钟记忆的小菜鸟了。

Java网络IO的演化,从最开始JDK1.4之前是基于阻塞的IO;发展到1.4发布后的Nio提供了selector多路复用的机制以及channel和buffer,再到1.7的NIO升级提供了真正的异步api;再发展到后来崭露头角的MINA和Netty。因此整个网络IO编程的学习整理,也会按照下面的几个步骤来进行:

  1. 网络IO的基本知识与概念
  2. 普通IO以及BIO服务器
  3. NIO的使用与服务器Hello world
  4. Netty入门与服务器Hello world
  5. Netty深入浅出

今天就简单的了解下网络IO需要具备的基本知识与概念。

同步、异步和阻塞、非阻塞

经常听人提起,同步阻塞服务器或者异步非阻塞服务器,网上有很多的文章针对这个概念作出了讲解,每个人理解的貌似都不太一样。最容易把异步和非阻塞搞混….我这里简单的说下自己的理解:

同步synchronous、异步asynchronous,他们的区别就是发起任务后,本身的一个状态——如果是一直等待结果,那就是同步;如果立即返回,并采用其他的方式得到结果就是异步(比如,状态、通知、回调)。

举个例子:

  1. 在过去科技不发达的时候,银行取钱都是排队的模式。想取钱就得去排队,直到轮到自己,这就是同步;
  2. 现在去银行一般直接叫号,然后去休息位置休息打游戏,登到轮到自己的时候,会有通知,这就是异步;

阻塞blocking、非阻塞non-blocking,则聚焦的是CPU在等待结果的过程中的状态。比如前面的例子,排队的过程中什么也不做就是阻塞;一边排队,一遍玩王者荣耀就是非阻塞的。

用户空间与内和空间

这个概念就涉及操作系统了,为了保护操作系统的安全,会将内存分为用户空间内核空间两个部分。如果用户想要操作内核空间的数据,需要把数据从内和空间拷贝到用户控件。

举个例子:

服务器接收客户端发过来的请求,想要进行处理,大致会经过下面几个步骤:

  1. 服务器的网络驱动接收到消息,去内核上申请空间;并等待完整的数据包到达(有可能分组传送,没传完…),复制到内核空间;
  2. 数据从内核空间拷贝到用户空间
  3. 用户程序进行处理

因此大致可以把接收消息理解为两个阶段:1. 等待数据到达 2. 拷贝到用户空间

了解了这个过程,就能明白为什么会出现经典的5大网络模型了.

五大网络模型

这几个网络模型还是学生时代的时候也看过,但是理解的不够透彻,也不知道到底有什么区别。最近网上也看过不少的文章,发现有一些文章引用的小例子不错,能很简单的了解这些模型的意思。所以我这边也借鉴一下:

在大连高新万达后面有一条叫做金街的小吃街,有一个露天的路边摊叫做“小红旗”,主要是做炸臭豆腐和冷面,然后用冷面把臭豆腐卷起来,刷上臭烘烘的酱料,非常好吃。每次路过都能看到不少人在排队,队伍长的有种想让人辞职加盟的感觉….基本上排个队伍都得半个小时-一个小时吧。

这个排队的过程,明显就是上面所说的同步阻塞模式….那我这边就设想下,如果小红旗的生意做大了,可以怎么发展?正好套用下网络模型的概念…

1. 同步阻塞IO

这个就不详细说了,排队的过程哪也去不了,如果你还没有带手机,排队的过程中就只能干瞪眼了。这就是很明显的同步+阻塞模式。

2. 同步非阻塞IO

如果小红旗的老板搞了一个点菜机,来点单的顾客把自己想吃的划上,然后等着老板去做,自己可以在这一个小时的时间里去周围商场溜达下。但是由于没有任何通信方式,只能不停的回来问老板,做好了没有。

回来询问的时间是由顾客自己掌控的,如果时间很短,那么可以尽量早的知道臭豆腐炸好没,但是也会影响逛街的体验;如果时间很长,有可能臭豆腐早就做好了..结果放的时间长了,反而不好吃了。

因此非阻塞IO基于状态轮训的方式,虽然能让程序在等待的过程中做点其他的事情,但是频繁的切换运行程序,反而会造成很大的压力。

3. IO多路复用/事件驱动

小红旗老板升级了系统,放弃使用点菜机,改用麦当劳那种点餐大屏。同样是点餐,但是一个大屏里面显示了很多人的臭豆腐进度,即节省了资源,也避免大家不停的询问。

其实Nio活着Netty就是基于这种模式,一个线程就可以监听很多IO操作,这样在IO等待上就高效多了。具体实现是依赖于操作系统的,windows和linux都有不同的实现方式。最初的select或者poll,都有并发数的限制,并且NIO的select还有空轮训的问题;epool则突破了连接数的限制,一个线程就可以监听大量的IO操作。这个感兴趣的朋友,可以深入了解下select、poll、epool的原理。

4. 信号驱动IO

小红旗老板又时髦了,搞了一个升级版的美味不用等。顾客基于微信小程序点菜,菜做好了自动提醒顾客取餐….这个提醒的过程,就像是发射了一个特殊的信号。

不过UNIX网络编程里面的信号驱动,可没这么简单,这个信号是依赖于操作系统底层的,捕获信号或者处理都很麻烦,所以现在应用的也不是很广泛。

5. 异步非阻塞IO

一对小情侣李雷和韩梅梅,韩梅梅口味很重,特别喜欢吃臭豆腐,但是李雷完全不感兴趣,闻到味道就想吐。于是李雷就跟韩梅梅约定,让韩梅梅自己去吃,李雷跑到旁边的咖啡厅喝咖啡。韩梅梅自己去排队买臭豆腐,买完顺便吃完,然后回来找李雷….

这个过程就是异步非阻塞的,消息的等待和处理都在服务器端完成,用户只要最后接收到消息处理完的通知就行了。

总结

总结来说,这几种网络模型:

  1. 同步阻塞:强调的是我要做! —— 别的啥也别说,就是要做!
  2. 同步非阻塞:强调的是我想做! —— 在想的过程中,干点其他的事情更好。
  3. 异步非阻塞:强调的是我做完了!—— 等得到结果通知的时候,工作已经做完了。

其中细节,还需慢慢体会…后面的文章将会挑几个模型做代码的演示,更多内容还请持续关注。

参考

  1. 并发编程网
  2. Netty源码分析
  3. Netty源码分析
  4. Netty源码分析
  5. IO多路复用之select、poll、epoll
  6. 聊聊Linux五种IO模型
  7. 聊聊同步、异步、阻塞、非阻塞
  8. Netty官方文档
  9. 一篇文章,读懂Netty的高性能架构之道
  10. 代码示例分享
作者:xingoo
Github:https://github.com/xinghalo

评论关闭。