既然跟网络内容有关就不得不学习网络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. sql 语句优化

    sql语句性能达不到你的要求,执行效率让你忍无可忍,一般会时下面几种情况. 网速不给力,不稳定. 服务器内存不够,或者SQL 被分配的内存不够. sql语句设计不合理 没有相应的索引,索引不合理 没有 ...

  2. 【2-26】string/math/datetime类的定义及其应用

    一string类 (1)字符串.Length    Length作用于求字符串的长度,返回一个int值 (2)字符串.TrimStart();  TrimStart():可删除前空格,返回一个stri ...

  3. mongoDB & Nodejs 访问mongoDB (二)

    非常详细的文档http://mongodb.github.io/node-mongodb-native/2.2/quick-start/quick-start/ 连接数据库 安装express 和 m ...

  4. arcgisserver成功发布服务后,浏览服务,无地图显示

    软件:ArcMap10.2,ArcgisCatalog10.2 方法:ArcMap10.2添加数据库连接,成功登陆数据库后,拖拽目标图层至Map窗口,对各个图层进行符号化设置 ArcCatalog中找 ...

  5. django进阶补充

    前言: 这篇博客对上篇博客django进阶作下补充. 一.效果图 前端界面较简单(丑),有两个功能: 从数据库中取出书名 eg: 新书A 在form表单输入书名,选择出版社,选择作者(多选),输入完毕 ...

  6. 如何在container中编译dotnet的eShopOnContainers

    准备的软件   问题 Image下载问题 以下就是为啥要有最后一个软件(我是使用版): SQLSever for Linux 内存需求 需要编译Image   成功搞定 参考 Welcome to t ...

  7. 一次SocketException:Connection reset 异常排查

    问题描述 上一期的需求上线之后,线上多了一个异常:Connection reset.如下: [2017-03-22 00:45:00 ERROR] [creativeAuditTaskSchedule ...

  8. ie11强制兼容模式打开

    <meta http-equiv="X-UA-Compatible" content="IE=edge">

  9. Jenkins集成Docker

    大概过程如下图: 由于需要用到docker打包镜像,jenkins宿主机上需要安装docker,原先的jenkins server安装在centos6上无法运行docker,所以这里单独用一台cent ...

  10. 大数据时代日志分析平台ELK的搭建

    A,首先说说ELK是啥,  ELK是ElasticSearch . Logstash 和 Kiabana 三个开源工具组成.Logstash是数据源,ElasticSearch是分析数据的,Kiaba ...