既然跟网络内容有关就不得不学习网络IO模型,时代在进步,技术也在进步,采取使用那种网络IO模型就已经确定应用程序规模

阻塞IO(blocking IO)

在linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样:

图1 阻塞IO

大部分的IO接口都是阻塞型的。所谓阻塞型接口是指系统调用(一般是IO接口)不返回调用结果并让当前线程一直阻塞,只有当该系统调用获得结果或者超时出错时才返回。

  当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候kernel就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来

所以,blocking IO的特点就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了。

  对于网络编程,在第一阶段系统内核阻塞等侍接收完整数据包,主进程/线程无法执行运算同响应其它请求,非常浪费硬件资源,解决方案是每个socket开个线程/进程独立处理

 public final class ServerBio {
private static int DEFAULT_PORT = 12345;
private static ServerSocket server;
private static AtomicInteger ai = new AtomicInteger(); public static void main(String[] args) throws Exception {
try {
server = new ServerSocket(DEFAULT_PORT);
System.out.println("服务器已启动,端口号:" + DEFAULT_PORT);
while (true) {
Socket socket = server.accept();
ai.incrementAndGet();
new Thread(new ServerHandler(socket)).start();
}
} finally {
if (server != null) {
System.out.println("服务器已关闭。");
server.close();
server = null;
}
}
} public static class ServerHandler implements Runnable {
private Socket socket;
private BufferedReader in = null;
private PrintWriter out = null; public ServerHandler(Socket socket) {
this.socket = socket;
} @Override
public void run() {
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
String body;
while (true) {
if ((body = in.readLine()) == null) {
continue;
}
System.out.println("服务器收到消息:" + body);
out.println(ai.get());
}
} catch (Exception e) { } finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
out.close();
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
 public class ClientBio {
private static int DEFAULT_SERVER_PORT = 12345;
private static String DEFAULT_SERVER_IP = "127.0.0.1";
private static AtomicInteger ai = new AtomicInteger(); private Socket socket = null;
private BufferedReader in = null;
private PrintWriter out = null; public static void main(String[] args) throws InterruptedException {
while (true) {
send("xxxxxx");
Thread.sleep(1);
}
} public static void send(String body) {
new ClientBio().send(DEFAULT_SERVER_PORT, body);
} public void send(int port, String body) {
try {
socket = new Socket(DEFAULT_SERVER_IP, port);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
out.println(body);
ai.incrementAndGet();
System.out.println("客户端 接收:" + in.readLine());
} catch (Exception e) {
e.printStackTrace();
}
}
}

BIO有个致命的缺点,由于线程/进程资源是有限的,在测试发现当开了500线程左右每接收/创建一个socket时间变得越来越长,也就是说采用BIO模型的瓶颈在500左右(大众机器)

解决方案也简单:线程是有限的,那么就用池程线来重用线程

ServerBioPool.class 只需要添加ExecutorService pool 替换掉Thread即可

     private static ExecutorService pool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*10);
public static void main(String[] args) throws Exception {
try {
server = new ServerSocket(DEFAULT_PORT);
System.out.println("服务器已启动,端口号:" + DEFAULT_PORT);
while (true) {
Socket socket = server.accept();
ai.incrementAndGet();
pool.submit(new ServerHandler(socket));
}
} finally {
// 一些必要的清理工作
if (server != null) {
System.out.println("服务器已关闭。");
server.close();
server = null;
}
}
}

上面应用场景比较有限,如果是长连接不释放socket资源的话,每个socket占用一个thread,用thread pool只能优化thread创建和销毁的频率并不能解决thread不足问题,读者可以试下把线程数改成800再测试。

现实与理想差距还是很大的

我们来论证瓶颈是否出现在线程上,屏蔽掉 ServerBioPool.class ClientBio.class 发送接收处理

    public static void main(String[] args) throws Exception {
server = new ServerSocket(DEFAULT_PORT);
System.out.println("服务器已启动,端口号:" + DEFAULT_PORT);
while (true) {
Socket socket = server.accept();
ai.incrementAndGet();
System.out.println(ai.get());
}
}

ClientBio.class

    public void send(int port, String body) {
try {
socket = new Socket(DEFAULT_SERVER_IP, port);
} catch (Exception e) {
e.printStackTrace();
}
}

结果打印出的数字能突破成千上万

如果client使用bio是无影响的,因为由始至终只有一个socket

小结:使用BIO模型,性能屏颈受线程/进程数上限影响,client可以使用bio

[编织消息框架][网络IO模型]BIO的更多相关文章

  1. [编织消息框架][网络IO模型]aio

    asynchronous I/O (the POSIX aio_functions)—————异步IO模型最大的特点是 完成后发回通知. [编织消息框架][网络IO模型]NIO(select and ...

  2. [编织消息框架][网络IO模型]NIO(select and poll)

    上面测试论证系统内核在read data时会阻塞,如果我们在把第一个阶段解决掉那么性能就会提高 NIO 编程 JDK 1.4中的java.nio.*包中引入新的Java I/O库,其目的是提高速度.实 ...

  3. [编织消息框架][网络IO模型]Netty Reactor

    严格来讲Netty Reactor是一种设计模式,一听模式两字就知道了吧,套路哈哈 Reactor中文译为“反应堆”. 看图netty处理流程 1.netty server 至少有两组reactor. ...

  4.  打开APP  04 | 网络通信:RPC框架在网络通信上更倾向于哪种网络IO模型? 2020-02-26 何小锋

     打开APP  04 | 网络通信:RPC框架在网络通信上更倾向于哪种网络IO模型? 2020-02-26 何小锋

  5. Socket-IO 系列(一)Linux 网络 IO 模型

    Socket-IO 系列(一)Linux 网络 IO 模型 一.基本概念 在正式开始讲 Linux IO 模型前,先介绍 5 个基本概念. 1.1 用户空间与内核空间 现在操作系统都是采用虚拟存储器, ...

  6. 网络IO模型与Reactor模式

    一.三种网络IO模型: 分类: BIO 同步的.阻塞式 IO NIO 同步的.非阻塞式 IO AIO 异步非阻塞式 IO 阻塞和同步的概念: 阻塞:若读写未完成,调用读写的线程一直等待 非阻塞:若读写 ...

  7. [编织消息框架][消息服务]rmi

    RMI(即Remote Method Invoke 远程方法调用) 远程对象: 用于远程客户端调用 必需继承java.rmi.Remote,每个调用方法必须添加java.rmi.RemoteExcep ...

  8. 通过实例理解Java网络IO模型

    网络IO模型及分类 网络IO模型是一个经常被提到的问题,不同的书或者博客说法可能都不一样,所以没必要死抠字眼,关键在于理解. Socket连接 不管是什么模型,所使用的socket连接都是一样的. 以 ...

  9. Unix 网络IO模型介绍

    带着问题阅读 1.什么是同步异步.阻塞非阻塞 2.有几种IO模型,不同模型之间有什么区别 3.不同IO模型的应用场景都是什么 同步和异步.阻塞和非阻塞 同步和异步 广义上讲同步异步描述的是事件中发送方 ...

随机推荐

  1. 【Zookeeper】源码分析之服务器(二)

    一.前言 前面阐述了服务器的总体框架,下面来分析服务器的所有父类ZooKeeperServer. 二.ZooKeeperServer源码分析 2.1 类的继承关系 public class ZooKe ...

  2. 读书笔记 effective c++ Item 29 为异常安全的代码而努力

    异常安全在某种意义上来说就像怀孕...但是稍微想一想.在没有求婚之前我们不能真正的讨论生殖问题. 假设我们有一个表示GUI菜单的类,这个GUI菜单有背景图片.这个类将被使用在多线程环境中,所以需要mu ...

  3. Java XML DOM解析(xPath)

    (一) XML概念 在电子计算机中,标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种的信息比如文章等.它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的 ...

  4. 1709: [Usaco2007 Oct]Super Paintball超级弹珠

    1709: [Usaco2007 Oct]Super Paintball超级弹珠 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 339  Solved:  ...

  5. 1191: [HNOI2006]超级英雄Hero

    1191: [HNOI2006]超级英雄Hero Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1996  Solved: 946[Submit][S ...

  6. Android 启动模式--任务(Task)--桟 的误区

    Android 启动模式--任务(Task)--桟 的误区 写这篇文章是因为前几天的一次面试,面试官说SingleInstance模式会新建一个桟,而SingleTask不会.首先不说这个对不对(非要 ...

  7. Docker基础入门及示例

    Docker近几年的发展可谓一日千里,特别从是2013年随着一个基于LXC的高级容器引擎开源,到现在,其在linux和windows上都有了很好的支持,并且已经有很多公司将docker用于实际的生产环 ...

  8. fopen中的mode(20161115)

    mode mode 参数指定了所要求到该流的访问类型.可以是以下: fopen() 中 mode 的可能值列表 mode 说明 'r' 只读方式打开,将文件指针指向文件头. 'r+' 读写方式打开,将 ...

  9. 解决WebStorm无法连接到Chrome

    问题: 点击 中的chrome时,出现了错误,如下: 解决办法: 找到 File>setting>Web Browser 修改为 C:\Program Files (x86)\Google ...

  10. 第一次"正经面试"之发现自己的缺陷和不足

    1:初试之校园招聘~~~ 如果你细心,可能发现了"正经面试"加了双引号,说起这次面试(昨天面的技术试),要从上礼拜六,距今已经一礼拜了吧.现在这个时候校园招聘已经蠢蠢欲动了吧,(说 ...