引言 

从 T 跳槽到 A 之后,我的编程语言也从 C++ 转为 了 Java。在 T 做的偏服务器端开发,而在 A 更偏向于业务开发。上周在 A 公司组内做了一个《服务器端高性能网络编程》的分享,我讶异于组内的十个人竟然没有一个人做过直接基于 TCP/IP 协议的开发,更多的是 Web 后台的业务开发。连 Java 最强大的网络库 Netty,用过的人也只有一个。但也不难理解---A 公司的中间件平台,将业务与底层进行了隔离,让程序员可以专心于业务开发。

孰优孰劣?不能一概而论,还记得跳槽 A 公司之前面试过 N 公司,面试官问我 HTTP 协议、RPC 框架等知识,我也是一知半解。要什么 HTTP 协议、要什么 RPC ,我们直接 TCP/IP。其实这也是 T 公司基础设施不够完善的一种表现,这些在我的另一篇文章《我在腾讯和阿里的见闻》中谈过,感兴趣的可以移步。

作为 Java Web 程序员,你有想过 Web 服务器(Nginx,Tomcat,Jetty等)是如何接受你的 HTTP 请求的吗?你知道 其实 HTTP 也是基于 TCP/IP 的文本协议吗?之所以取这样的标题,希望 Java Web 程序员对服务器端的某些工作原理有一些简单的了解,也不至于在面试的时候一问三不知。

概念

在编写服务器端网络程序时,我们最常见到阻塞、非阻塞、同步和异步这四个词。它们的解释分别如下:

阻塞: 阻塞调用是指调用返回之前,当前线程会被挂起,只有当调用得到结果后才返回。

非阻塞:与阻塞相反,非阻塞调用是指在不能立即得到结果之前,该函数不会将当前线程阻塞,而是立即返回。

同步:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。等前一件做完了才能做下一件事。

异步:异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

常常有人弄不清阻塞/非阻塞与同步/异步之间的关系,容易将他们混为一谈。阻塞/非阻塞更多的用来形容某次调用的属性(比如 read(),write() 是否是阻塞/非阻塞 )所以应用范围比较窄;而同步/异步则更上层,通常指各个功能/线程之间的关系(比如 Thread1 和 Thread2 是同步执行还是异步执行)。

IO 模型

服务器端 IO 主要分为两种:磁盘 IO 和网络 IO,在讲服务器端高性能网络编程时更多时候我们讲的是网络 IO 模型。一次完整的服务器端处理网络请求流程图如下(简化版,以 Web 服务器为例):

这张图比较简单,但是很多人在没看到这张图之前肯定都以为每次网络读(recvfrom())或者写(sendto())都是在网卡与用户进程之间进行操作,其实不是。从上图可以看出,数据无论从网卡到用户空间还是从用户空间到网卡都需要经过内核。从磁盘上读写数据也是如此。所以就有了 mmap 技术,感兴趣的可以自行百度。应用进程(Web 服务器也属于应用进程,这里需要再统一几个概念:用户进程、应用程序、Web 服务器程序,它们相对于内核来说都是应用进程,所以后面文章中统一成应用进程)需要通过系统调用(recvfrom/sendto)向内核读写数据,内核再进一步操作网卡。

根据应用进程系统调用方式的阻塞、非阻塞,操作系统在处理应用程序请求时处理方式的同步、异步处理的不同,参考《UNIX 网络编程卷 I》可以分为 5 种 IO 模型:

1、阻塞 IO 模型(blocking IO)

描述:应用程序进行 recvfrom 系统调用时将阻塞在此调用,直到该套接字上有数据并且复制到用户空间缓冲区。该模式一般配合多线程使用,应用进程每接收一个连接,为此连接创建一个线程来处理该连接上的读写以及业务处理。

优点:编程简单,适合教学。《UNIX网络编程卷I》上很多例子都是基于这种模式。

缺点:如果套接字上没有数据,进程将一直阻塞。这时其他套接字上有数据也不能进行及时处理。如果是多线程方式,除非连接关闭否则线程会一直存在,而线程的创建、维护和销毁非常消耗资源,所以能建立的连接数量非常有限。

2、非阻塞 IO 模型(nonblocking IO)

  描述:应用进程每次调用 recvfrom 即使没有数据准备好也不会阻塞,会继续往下执行,避免了进程阻塞在某个连接上的弊端。

优点:代码编写相对简单,进程不会阻塞,可以在同一线程中处理所有连接。

缺点:需要频繁的轮询,比较耗 CPU,在并发量很大的时候将花费大量时间在没有任何数据的连接上轮询。所以该模型只在专门提供某种功能的系统中才会出现。

3、IO 复用模型(IO multiplexing)

描述:应用进程阻塞于 select/poll/epoll 等系统函数等待某个连接变成可读(有数据过来),再调用 recvfrom 从连接上读取数据。虽然此模式也会阻塞在 select/poll/epoll 上,但与阻塞IO 模型不同它阻塞在等待多个连接上有读(写)事件的发生,明显提高了效率且增加了单线程/单进程中并行处理多连接的可能。

优点:统一管理连接,不一定采用多线程的方式,同时也不需要轮询。只需要阻塞于 select 即可,可以同时管理多个连接。

缺点:当 select/poll/epoll 管理的连接数过少时,这种模型将退化成阻塞 IO 模型。并且还多了一次系统调用:一次 select/poll/epoll 一次 recvfrom。

4、信号驱动 IO 模型(signal-driven IO)

描述:应用进程创建 SIGIO 信号处理程序,此程序可处理连接上数据的读写和业务处理。并向操作系统安装此信号,进程可以往下执行。当内核数据准备好会向应用进程发送信号,触发信号处理程序的执行。再在信号处理程序中进行 recvfrom 和业务处理。

优点:非阻塞

缺点:在前一个通知信号没被处理的情况下,后一个信号来了也不能被处理。所以在信号量大的时候会导致后面的信号不能被及时感知。

5、异步 IO 模型(asynchronous IO)

描述:应用进程通过 aio_read 告知内核启动某个操作,并且在整个操作完成之后再通知应用进程,包括把数据从内核空间拷贝到用户空间。信号驱动 IO 是内核通知我们何时可以启动一个 IO 操作,而一部 IO 模型是由内核通知我们 IO 操作何时完成。

 注:前 4 种模型都是带有阻塞部分的,有的阻塞在等待数据准备好,有的阻塞在从内核空间拷贝数据到用户空间。而这种模型应用进程从调用 aio_read 到数据被拷贝到用户空间,不用任何阻塞,所以该种模式叫异步 IO 模型。这五种模型的取名和并列方式我是保留意见的,感觉容易迷惑读者。 

优点:没有任何阻塞,充分利用系统内核将 IO 操作与计算逻辑并行。

缺点:编程复杂、操作系统支持不好。目前只有 windows 下的 iocp 实现了真正的 AIO。linux 下在 2.6 版本中才引入,目前并不完善,所以 Linux 下一般采用多路复用模型。

  对比

前四种模型的主要区别于第一阶段,因为他们的第二阶段都是一样的:在数据从内核拷贝到应用进程的缓冲区期间,进程阻塞于 recvfrom 调用。相反,异步 IO 模型在这两个阶段都需要处理,从而不同于其他四种模型。

  以上图片所有原型都来自于《UNIX网络编程卷 I》,里面有很多跟网络编程有关的知识点和例子,是程序员必备书籍,即使你是业务程序员也应该购买一本,知其然,知其所以然!

    总结

JDK 的网络编程相关的类、接口虽然不像 C++ 是直接依赖于操作系统的,但它的 IO 模型是离不开以上五种模型的。毕竟这是模型,与语言、操作系统无关。 IO 模型只是高性能网络编程中的基础部分,光有好的 IO 模型还不行,我们还需要好的架构(线程模型)。线程模型是高性能网络编程的核心部分,在后面的文章中应该还会分析。

服务器端网络编程之 IO 模型的更多相关文章

  1. 网络编程之IO模型

    IO模型的分类 blocking IO:阻塞IO nonblocking IO:非阻塞IO IO multiplexing:IO多路复用 signal driven IO:异步IO 通常情况下IO默认 ...

  2. linux网络编程之IO模型

    本文转自作者:huangguisu 1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式:同步:      所谓 ...

  3. Python并发编程之IO模型

    目录 IO模型介绍 阻塞IO(blocking IO) 非阻塞IO(non-blocking IO) IO多路复用 异步IO IO模型比较分析 selectors模块 一.IO模型介绍 Stevens ...

  4. python并发编程之IO模型,

    了解新知识之前需要知道的一些知识 同步(synchronous):一个进程在执行某个任务时,另外一个进程必须等待其执行完毕,才能继续执行 #所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调 ...

  5. 并发编程之IO模型

    一.阻塞IO(blocking IO) from concurrent.futures import ThreadPoolExecutor import socket server = socket. ...

  6. Python之旅:并发编程之IO模型

    一 IO模型介绍 为了更好地了解IO模型,我们需要事先回顾下:同步.异步.阻塞.非阻塞 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非 ...

  7. python全栈开发从入门到放弃之socket并发编程之IO模型

    一 IO模型介绍 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问 ...

  8. Python socket编程之IO模型介绍(多路复用*)

    1.I/O基础知识 1.1 什么是文件描述符? 在网络中,一个socket对象就是1个文件描述符,在文件中,1个文件句柄(即file对象)就是1个文件描述符.其实可以理解为就是一个“指针”或“句柄”, ...

  9. 33 python 并发编程之IO模型

    一 IO模型介绍 为了更好地了解IO模型,我们需要事先回顾下:同步.异步.阻塞.非阻塞 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非 ...

随机推荐

  1. mysql的group_concat列转行函数

    SELECT auditor,sum(count) total, GROUP_CONCAT(type,'=', count) AS type_count FROM auditor_dm_ol GROU ...

  2. windows 系统验证是否为正版

    博客园里边写这种帖子,足以证明我有多无聊.话不多说,上干货. 一台计算器如果没有操作系统,就是一块大的板砖,拿起来抡人太重,放地上做床又太小. 如何查看自己操作系统呢?windows7 桌面找到我的电 ...

  3. springboot+thymeleaf+pageHelper带条件分页查询

    html层 <div> <a class="num"><b th:text="'共 '+ ${result.resultMap['pages ...

  4. lnmp 系统500 报错

    分析点: 1 文件目录权限不足 如果日志缓存目录没有写入权限 chmod -R 775 目录 2 lnmp 一键安装包 查看.user.ini ,其中open_basedir  不要设置到public ...

  5. Kafka启动报错 : ERROR Processor got uncaught exception

    参照我之前的一篇博文Kafka学习之(二)Centos下安装Kafka安装了kafka并启动,状况并不像我之前最初的预期,报错了,并且我在当前Linux环境下安装的Java版本.Kafka版本都是和之 ...

  6. 02:安装 Kerberos

    1.1 环境介绍   参考博客:https://www.cnblogs.com/xiaodf/p/5968178.html https://www.douban.com/note/701660289/ ...

  7. 最长(大)回文串的查找(字符串中找出最长的回文串)PHP实现

    首先还是先解释一下什么是回文串:就是从左到右或者从右到左读,都是同样的字符串.比如:上海自来水来自海上,bob等等. 那么什么又是找出最长回文串呢? 例如:字符串abcdefedcfggggggfc, ...

  8. android TextView描边

    前言 上一篇已经讲了如何实现textView中粗字体效果,里面主要重写了onDraw方法. 这一边讲一个进阶功能,实现textView的描边效果. 上效果图. 上代码: public class St ...

  9. vue-router的简单理解

    Vue-router原理了解一下: 找到一篇文章,分析的很透彻 从vue-router看前端路由的两种实现,文章写的很好,看完这篇文章 ➕ 看源码应该可以理解,这里根据我浅显的理解概括一下: vue- ...

  10. Pandas 基础(15) - date_range 和 asfreq

    这一节是承接上一节的内容, 依然是基于时间的数据分析, 接下来带大家理解关于 date_range 的相关用法. 首先, 引入数据文件: import pandas as pd df = pd.rea ...