既然跟网络内容有关就不得不学习网络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. CSS Sprites (css精灵)

    CSS Sprites CSS Sprites在国内很多人叫css精灵,是一种网页图片应用处理方式.它允许你将一个页面涉及到的所有零星图片都包含到一张大图中去,这样一来,当访问该页面时,载入的图片就不 ...

  2. sqlserver的一些小知识点

    1.高效分页sql和储存过程 select top 每页条数 * from ( select ROW_NUMBER() over (order by id)as nid ,* from table01 ...

  3. 本人开发的JavaWeb急速框架Blast上线了

    JAVA 急速WEB框架Blast --对JavaWeb的学习性框架,参考了spring的实现 --阅读Blast源码可以快速掌握JavaWeb常用技术和方法论,并付诸实践 Blast 是基于 Jav ...

  4. java基础:数组查询,同一数组一个元素最多出现两次

  5. hdoj1016 [dfs]

    http://acm.hdu.edu.cn/showproblem.php?pid=1016 题意: 已知一个数n,在1-n(包含 n ,0 < n < 20)里组合形成一个环形,使得每两 ...

  6. Office 365 开发概览系列文章和教程

    Office 365 开发概览系列文章和教程 原文于2017年2月26日首发于LinkedIn,请参考链接 引子 之前我在Office 365技术社群(O萌)中跟大家提到,3月初适逢Visual St ...

  7. 因为本地没有配置 localhost 导致的 eclipse 的奇葩问题

    因为电脑没有配置 127.0.0.1 localhost,已经碰到两次奇葩问题了. 问题一: 我的博文http://www.cnblogs.com/sonofelice/p/5143746.html中 ...

  8. SQL Server里书签查找的性能伤害

    在我的博客上,以前我经常谈到SQL Serverl里的书签查找,还有它们带来的很多问题.在今天的文章里,我想从性能角度进一步谈下书签查找,还有它们如何拉低你整个SQL Server性能. 书签查找—— ...

  9. 对JDBC的优化,BeanUtils和DBUtils

    为了进一步简化jdbc的使用,就是用组件进一步的及优化 BeanUtils工具包,代替java本身蹩脚的javaBean,使对象的封装更加的简单易行 DBUtils工具包,是jdbc的操作更加的简单 ...

  10. 【树莓派】Linux 测网速及树莓派源

    这篇文章比较杂,其中包含三点:linux环境中测试网络速度,树莓派下载软件的源,部分我写好的脚本: 一.Linux 测网速 Linux 测网速: sar -n DEV 1 100 1代表一秒统计并显示 ...