Loading [MathJax]/jax/input/TeX/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >客户端异步非阻塞 IO 新的流模式(基于 Nim 语言)

客户端异步非阻塞 IO 新的流模式(基于 Nim 语言)

作者头像
用户2870857
发布于 2020-04-22 04:02:47
发布于 2020-04-22 04:02:47
1K00
代码可运行
举报
文章被收录于专栏:Python高效编程Python高效编程
运行总次数:0
代码可运行

Nim 编程语言是一个新型的静态类型、命令式编程语言,支持过程式、函数式、面向对象和泛型编程风格而保持简单和高效。Nim 从Lisp继承来的一个特殊特性--抽象语法树(AST)作为语言规范的一部分,可以用作创建领域特定语言的强大宏系统。

官方网址: https://nim-lang.org 与 Python 交互: https://www.oschina.net/news/114888/nimporter-1-0-0-released

客户端异步非阻塞 IO 新的流模式(基于 Nim 语言)

Mostly Single Buffer 更精确点应该是 Mostly Single Buffer one connection,意思是:大多数时候,一个连接总是使用一个缓冲区。也就是说,会存在这样的情况,即一个连接使用两个甚至更多个缓冲区。

Mostly Single Buffer 的目标是提供绝对的 IO 一致性,并尽可能保证 IO 性能和减少内存占用,而最小化对用户编程效率的影响。

在对网络 IO 进行编程时,我们通常要处理各种各样的协议封装数据。拿 HTTP 来说,这些数据是以 “请求” (Request) 为单元进行封装的。对于同一个客户端连接,其发送 HTTP 数据包的过程类似这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
|---request 1---|---request 2---|---request 3---|

上面图中表示,客户端发送了 3 个请求。同一个客户端发送的 HTTP 请求总是连续的。站在服务器的视角,服务器会为每一个客户端创建一个 “缓冲区”,将请求数据读入缓冲区,进行处理,然后作出响应。通常,服务器对于缓冲区的态度是,对每个客户端连接创建一个独立的缓冲区。也就是说,如果同时有 2000 个客户端连接到服务器,服务器通常会有 2000 个缓冲区,分别对应每一个客户端。对于每一个客户端,服务器都使用一个完全独立的缓冲区处理,这就保证了 IO 一致性,即各个客户端的处理不会交叉在一起;同时,也意味着更多的内存占用,每一个缓冲区都要占用一块内存。

现在,把视角转回客户端。作为客户端,通常向服务器建立一个连接,然后不断发起请求。让我们谈谈 MySQL 连接,这会非常有代表性,特别是涉及到异步非阻塞 IO 时,其内部的操作过程会变得非常负载和不稳定。看看下面这段伪代码 (1):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var mysql = newMysqlClient()

await mysql.query("select * from users") # 第一个请求
await mysql.query("select * from blogs") # 第二个请求

这段代码发起了两次查询请求,需要注意的是 await 适时地等待第一个请求完成,然后才发起第二个请求。请看下面伪代码(2):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var mysql = newMysqlClient()

var req1 = mysql.query("select * from users") # 第一个请求
var req2 = mysql.query("select * from blogs") # 第二个请求

await req1
await req2

仍是发起两次查询请求,不同之处在于,第二个请求还没等第一个请求处理完成,就开始发起。相同的是,先等待第一个响应完成,然后等待第二个响应完成。再请看下面伪代码(3):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var mysql = newMysqlClient()

var req1 = mysql.query("select * from users") # 第一个请求
var req2 = mysql.query("select * from blogs") # 第二个请求

await req2
await req1

这段代码先等待第二个响应完成,然后等待第一个响应完成。

以上三段伪代码执行了相同的操作,但是其涉及的影响却远远不同。上面说过,服务器会为每一个客户端连接创建一个缓冲区,那么客户端呢?通常的方法是,客户端为每一个连接创建一个缓冲区。创建多个缓冲区是没有必要的,因为每个连接一次只能处理一个问题,额外的缓冲区通常都是浪费。

然而,上面三段伪代码会牵扯出很多问题。我们现在假设 mysql 的客户端连接只有一个缓冲区,发出两个查询请求,那么返回来的结果则是这样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
|---response 1---|---response 2---|
  ^

结果是顺序排列在同一个缓冲区当中。

对于伪代码(1),这不会产生问题,因为总是先等待第一个响应完成,再处理第二个响应;伪代码(2),也构不成问题,因为其对响应的处理,类似伪代码(1)。然而,对于伪代码(3),却产生了大问题,因为其先等待第二个响应完成,然后再处理第一个响应。这就表示,响应 2 不处理完成,响应 1 就不会处理。看看上图,因为响应 1 和响应 2 被顺序存储到同一个缓冲区,这就导致只有响应 1 从缓冲区提取后,响应 2 才会获得操作。“死锁” 产生了!

这有点像多线程编程中常常提到的 “死锁”,锁 A 先于锁 B 锁住,然而程序却先处理锁 B,导致 “死锁”。异步非阻塞 IO 并没有锁的概念,然而在此处却也产生了 “死锁” 的问题。这是因为 await 的问题。await 将一行程序操作拆成了两行,将原来本可以一次处理变成了两次处理,“破坏了” 原子操作。然而,我们不能强制要求用户总是将 await 写作一行,而且,当处理大数据流的时候,我们还必须使用多次 await 来处理 “小块” 数据。看看这段伪代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var stream = mysql.queryLargeResult(...)

while stream.next():
  await stream.readRow()

然而,问题总要解决。回想起服务器对于缓冲区的态度,我们可以想要使用同样的思路,为每一次请求创建一块单独的缓冲区。好了,客户端 IO 的解决方法是,对于每一个客户端连接,不再是创建一个单独的缓冲区,而是每一个请求创建一个单独的缓冲区。看看下面的伪代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var mysql = newMysqlClient()

var req1 = mysql.query("select * from users") # 第一个请求
var req2 = mysql.query("select * from blogs") # 第二个请求
var req3 = mysql.query("select * from blogs") # 第三个请求
var req4 = mysql.query("select * from blogs") # 第四个请求

await req2
await req1
await req4
await req3

上面的代码创建 4 块独立的缓冲区,但是它们都是位于同一个客户端连接。

不过,这也同时带来问题,即内存被大量的占用,而且被浪费,因为在每一时刻,客户端只有一块缓冲区是有用的。特别是当你建立一个 Web Server,然后对 HTTP 请求进行一些 Mysql 查询时,你的服务器内存开始飙升。这可能不是你所想要的。

Mostly Single Buffer 期望解决这些问题。客户端建立网络 IO 时,对每一个连接,该方案尽可能只创建一块缓冲区,并使用一个标记,标记该缓冲区是处于 “忙” 状态,还是处于 “空闲” 状态。当处于 “忙”状态时,如果收到新的请求操作,则自动创建一块新的缓冲区。当一块 “忙” 缓冲区重新变为 “空闲” 的时候,自动将其回收。

倾倒:如果用户对同一连接申请了多个请求,当处理响应的时候,MSB 会查看用户提供的读操作。比如发起请求 [q1, q2, q3],MSB 收到响应数据后,查看请求队列,先查询 q1 的读操作,然后使用读操作处理数据,如果 q1 的所有读操作都工作完,q1 的响应数据仍然未完全 “读” 完,那么 MSB 就进行 “倾倒”,将 q1 剩余的数据倒入一块新的缓冲区暂存起来,以便于后续 q1 的相关读操作进行处理。然后依次处理 q2,q3,… 这自然会形成一种情况,即 q1、q2、q3 剩余的数据及其引用会一直暂存在内存,直到内存溢出。.clear(q1) 允许立刻清除 q1 相关响应数据。

使用 Mostly Single Buffer,如果你是经验丰富的程序员,总能合理安排你的程序,即在合理时刻 await 每一个请求,那么你总能最小化缓冲区的内存占用。比如 (只创建一块缓冲区) :

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var req1 = mysql.query("select * from users")    # 第一个请求
await req1

var req2 = mysql.query("select * from blogs")    # 第二个请求
await req2

var req3 = mysql.query("select * from comments") # 第三个请求
await req3

而如果你对 IO 编程掌握还不够熟练,或者编程的程序比较随意,Mostly Single Buffer 总能保证你的程序正确运行,但是会消耗一些内存。比如 (创建三块缓冲区):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var req1 = mysql.query("select * from users")    # 第一个请求
var req2 = mysql.query("select * from blogs")    # 第二个请求
var req3 = mysql.query("select * from comments") # 第三个请求

await req3
await req1
await req2

这个 IO 缓冲区方案将会应用在 https://github.com/iocrate/netkit --- 一个正在积极开发的 Nim Network 工具包,以及其他的一些网络包中,比如 mysql connector。顺便一提的是,https://github.com/tulayang/asyncmysql 使用了回调函数来处理 IO 一致性问题,但是却导致 API 调用比较难以使用,未来的连接器将会获得改善。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python高效编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
PHP异步非阻塞MySQL客户端连接池
AMPHP是一个事件驱动的PHP库集合,设计时考虑了纤程和并发性。amphp/mysql是一个异步MySQL客户端。该库通过在可用连接的可伸缩池中透明地分发查询来实现并发查询。客户端透明地将这些查询分布在一个可扩展的可用连接池中,并使用100%的用户态PHP,没有外部扩展依赖性(例如ext/mysqli,ext/pdo等)。
Tinywan
2024/09/10
2360
PHP异步非阻塞MySQL客户端连接池
Java NIO、BIO、 AIO 与 同步、阻塞、非阻塞、异步IO 简析
我相信大部分人看到这些名词,都是一头雾水的,如果你去搜索引擎搜索,那么恭喜你,你又会被各种文章中的高大上的名词搞得云里雾里。那么,我们应该怎么理清这么名词之间的关系呢?
卢衍飞
2023/02/16
4490
阻塞、非阻塞、多路复用、同步、异步、BIO、NIO、AIO 一锅端
本文会涉及到阻塞、非阻塞、多路复用、同步、异步、BIO、NIO、AIO等几个知识点,知识点虽然不难但经常容易搞混,这次带领大家再回顾一遍。
码农编程进阶笔记
2021/07/20
4330
阻塞、非阻塞、多路复用、同步、异步、BIO、NIO、AIO 一锅端
workerman5.0 异步非阻塞HTTP协程客户端
Workerman 5.0 版本中的异步HTTP协程客户端组件是一个基于PHP协程的高性能HTTP客户端,它能够充分利用PHP的异步特性来提高HTTP请求的效率和性能。这个组件允许开发者在编写PHP代码时,以同步的方式发送异步HTTP请求,从而使得编写的代码更加简洁易懂,同时也能够处理大量的并发请求。
Tinywan
2024/04/19
6310
workerman5.0 异步非阻塞HTTP协程客户端
socket阻塞与非阻塞,同步与异步、I/O模型
在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式:
用户6280468
2022/03/21
3.6K0
socket阻塞与非阻塞,同步与异步、I/O模型
socket阻塞与非阻塞,同步与异步、I/O模型
在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式:
黄规速
2022/04/14
3.2K0
socket阻塞与非阻塞,同步与异步、I/O模型
细谈Select,Poll,Epoll阻塞 io 模型 blocking IO非阻塞 io 模型 nonblocking IOio多路复用模型 IO multiplexing细谈 io 多路复用技术
而根据这两个阶段而不同的操作方法,就会产生多种io模型,本文只讨论select,poll,epoll,所以只引出三种io模型。
desperate633
2018/08/22
8100
细谈Select,Poll,Epoll阻塞 io 模型 blocking IO非阻塞 io 模型 nonblocking IOio多路复用模型 IO multiplexing细谈 io 多路复用技术
理解一下5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO
看了一些文章,发现有很多不同的理解,可能是因为大家入切的角度、环境不一样。所以,我们先说明基本的IO操作及环境。
Java团长
2020/08/24
36.7K1
理解一下5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO
手把手教你同步阻塞IO与同步非阻塞IO
上篇文章介绍了最基础的Socket 通信,本篇文章小鱼将继续就网络编程方向介绍下BIO 和NIO。
鳄鱼儿
2024/06/10
1630
手把手教你同步阻塞IO与同步非阻塞IO
【JavaEE初阶】网络编程TCP协议实现回显服务器以及如何处理多个客户端的响应
和前一期的UDP基本是大差不差的,但是这里提供的方法来模拟对于网卡的操作是有一定的区别的,所示API如下:
用户11288949
2024/10/24
2040
【JavaEE初阶】网络编程TCP协议实现回显服务器以及如何处理多个客户端的响应
socket阻塞与非阻塞,同步与异步I/O模型
作者:huangguisu 原文出处:http://blog.csdn.net/hguisu/article/details/7453390
bear_fish
2018/09/20
3.6K0
socket阻塞与非阻塞,同步与异步I/O模型
聊聊同步与异步、阻塞与非阻塞、I/O模型
程序员:假如我们执行A,B两个IO操作的时候,如果必须等待A完成后才能执行B那么这个就是
咻一咻
2020/05/29
1.3K0
【网络】TCP回显服务器和客户端的构造,以及相关bug解决方法
不像 UDP 有 DatagramPacket 是专门的“UDP 数据报”,TCP 没有专门的“TCP 数据报”
椰椰椰耶
2024/09/20
1330
【网络】TCP回显服务器和客户端的构造,以及相关bug解决方法
Netty服务端与客户端(源码一)
  (4)创建Selector,将之前创建的ServerSocketChannel注册到Selector上,监听SelectionKeyACCEPT。
用户3003813
2018/09/06
1.6K0
Netty服务端与客户端(源码一)
[NodeJS]创建HTTP、HTTPS服务器与客户端
当从客户端请求流中读取到数据时会触发data事件,当读取完客户端请求流中的数据时触发end事件。
奋飛
2019/08/15
5.6K0
Java 非阻塞 IO 和异步 IO
上一篇文章介绍了 Java NIO 中 Buffer、Channel 和 Selector 的基本操作,主要是一些接口操作,比较简单。 本文将介绍非阻塞 IO 和异步 IO,也就是大家耳熟能详的 NIO 和 AIO。很多初学者可能分不清楚异步和非阻塞的区别,只是在各种场合能听到异步非阻塞这个词。 本文会先介绍并演示阻塞模式,然后引入非阻塞模式来对阻塞模式进行优化,最后再介绍 JDK7 引入的异步 IO,由于网上关于异步 IO 的介绍相对较少,所以这部分内容我会介绍得具体一些。 希望看完本文,读者可以对非阻塞
用户1257393
2018/03/21
1.3K0
workerman5.0篇〡异步非阻塞协程HTTP客户端
workerman/http-client 是一个异步http客户端组件。所有请求响应异步非阻塞,内置连接池,消息请求和响应符合PSR7规范。
Tinywan
2025/01/10
1500
workerman5.0篇〡异步非阻塞协程HTTP客户端
DBeaver客户端工具结果集缓存实现的猜测
《MySQL客户端预读数据的区别》文章中提到了DBeaver设置"集数获取大小",我猜测是通过在执行的SQL上添加limit得到的,
bisal
2021/12/16
1.1K0
DBeaver客户端工具结果集缓存实现的猜测
图文吃透Golang net/http 标准库--客户端
在上一期服务端这一章《图文讲透Golang标准库 net/http实现原理 -- 服务端》我们知道了如何进行路由注册,以及服务的注册和请求处理过程了。
小许code
2024/02/19
4290
图文吃透Golang net/http 标准库--客户端
Java IO(3)非阻塞式输入输出(NIO)
  在上篇《Java IO(2)阻塞式输入输出(BIO)》的末尾谈到了什么是阻塞式输入输出,通过Socket编程对其有了大致了解。现在再重新回顾梳理一下,对于只有一个“客户端”和一个“服务器端”来讲,服务器端需要阻塞式接收客户端的请求,这里的阻塞式表示服务器端的应用代码会被挂起直到客户端有请求过来,在高并发的应用场景有多个客户端发起连接下非阻塞式IO(NIO)是不二之选(且只需要在服务器端使用1个线程来管理,并不需要多个线程来处理多个连接)。在现实情况下,Tomcat、Jetty等很多Web服务器均使用了N
用户1148394
2018/01/09
1K0
Java IO(3)非阻塞式输入输出(NIO)
推荐阅读
相关推荐
PHP异步非阻塞MySQL客户端连接池
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验