Python实现异步IO的示例


Posted in Python onNovember 05, 2020

前言

用阻塞 API 写同步代码最简单,但一个线程同一时间只能处理一个请求,有限的线程数导致无法实现万级别的并发连接,过多的线程切换也抢走了 CPU 的时间,从而降低了每秒能够处理的请求数量。为了达到高并发,你可能会选择一个异步框架,用非阻塞 API 把业务逻辑打乱到多个回调函数,通过多路复用与事件循环的方式实现高并发。

磁盘 IO 为例,描述了多线程中使用阻塞方法读磁盘,2 个线程间的切换方式。那么,怎么才能实现高并发呢?

Python实现异步IO的示例

把上图中本来由内核实现的请求切换工作,交由用户态的代码来完成就可以了,异步化编程通过应用层代码实现了请求切换,降低了切换成本和内存占用空间。异步化依赖于 IO 多路复用机制,比如 Linux 的 epoll 或者 Windows 上的 iocp,同时,必须把阻塞方法更改为非阻塞方法,才能避免内核切换带来的巨大消耗。Nginx、Redis 等高性能服务都依赖异步化实现了百万量级的并发。

下图描述了异步 IO 的非阻塞读和异步框架结合后,是如何切换请求的。

Python实现异步IO的示例

然而,写异步化代码很容易出错。因为所有阻塞函数,都需要通过非阻塞的系统调用拆分成两个函数。虽然这两个函数共同完成一个功能,但调用方式却不同。第一个函数由你显式调用,第二个函数则由多路复用机制调用。

这种方式违反了软件工程的内聚性原则,函数间同步数据也更复杂。特别是条件分支众多、涉及大量系统调用时,异步化的改造工作会非常困难。

Python如何实现异步调用

from flask import Flask
import time
app = Flask(__name__)


@app.route('/bar')
def bar():
  time.sleep(1)
  return '<h1>bar!</h1>'

@app.route('/foo')
def foo():
  time.sleep(1)
  return '<h1>foo!</h1>'
if __name__ == '__main__':
  app.run(host='127.0.0.1',port=5555,debug=True)

采用同步的方式调用

import requests
import time

starttime = time.time()
print(requests.get('http://127.0.0.1:5555/bar').content)
print(requests.get('http://127.0.0.1:5555/foo').content)
print("消耗时间: ",time.time() -starttime)

b'<h1>bar!</h1>'
b'<h1>foo!</h1>'
消耗时间:  2.015509605407715

采样异步的方式调用:

重点:

1.将阻塞io改为非阻塞io;

2.多路复用io监听内核事件,事件触发通过回调函数;

3.用户态代码采取事件循环的方式获取事件,执行事件的回调函数;

import selectors
import socket
import time
# from asynrequest import ParserHttp
class asynhttp:
  def __init__(self):
    self.selecter = selectors.DefaultSelector()

  def get(self,url,optiondict = None):
    global reqcount
    reqcount += 1
    s = socket.socket()
    s.setblocking(False)
    try:
      s.connect(('127.0.0.1',5555))
    except BlockingIOError:
      pass
    requset = 'GET %s HTTP/1.0\r\n\r\n' % url
    callback = lambda : self.send(s,requset)
    self.selecter.register(s.fileno(),selectors.EVENT_WRITE,callback)

  def send(self,s,requset):
    self.selecter.unregister(s.fileno())
    s.send(requset.encode())
    chunks = []
    callback = lambda: self.recv(s,chunks)
    self.selecter.register(s.fileno(),selectors.EVENT_READ,callback)

  def recv(self,s,chunks):
    self.selecter.unregister(s.fileno())
    chunk = s.recv(1024)
    if chunk:
      chunks.append(chunk)
      callback = lambda: self.recv(s,chunks)
      self.selecter.register(s.fileno(), selectors.EVENT_READ, callback)
    else:
      global reqcount
      reqcount -= 1
      request_first,request_headers,request_content,_ = ParserHttp.parser(b''.join(chunks))
      print("解析数据:",request_first,request_headers,request_content)
      print((b''.join(chunks)).decode())
      return (b''.join(chunks)).decode()

starttime = time.time()
reqcount = 0
asynhttper = asynhttp()
asynhttper.get('/bar')
asynhttper.get('/foo')
while reqcount:
  events = asynhttper.selecter.select()
  for event,mask in events:
    func = event.data
    func()
print("消耗时间:" ,time.time() - starttime)

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 13
Server: Werkzeug/1.0.1 Python/3.7.7
Date: Thu, 15 Oct 2020 03:28:16 GMT

<h1>bar!</h1>
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 13
Server: Werkzeug/1.0.1 Python/3.7.7
Date: Thu, 15 Oct 2020 03:28:16 GMT

<h1>foo!</h1>
消耗时间: 1.0127637386322021

以上就是Python实现异步IO的示例的详细内容,更多关于python 异步IO的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
python多线程http下载实现示例
Dec 30 Python
python日志记录模块实例及改进
Feb 12 Python
Python内存管理方式和垃圾回收算法解析
Nov 11 Python
基于Django filter中用contains和icontains的区别(详解)
Dec 12 Python
使用 Python 实现微信公众号粉丝迁移流程
Jan 03 Python
python队列queue模块详解
Apr 27 Python
通过python改变图片特定区域的颜色详解
Jul 15 Python
pygame实现俄罗斯方块游戏(基础篇3)
Oct 29 Python
Python内建序列通用操作6种实现方法
Mar 26 Python
Django 解决阿里云部署同步数据库报错的问题
May 14 Python
pycharm 对代码做静态检查操作
Jun 09 Python
Pytorch 如何实现LSTM时间序列预测
May 17 Python
Python requests HTTP验证登录实现流程
Nov 05 #Python
Python包资源下载路径报404解决方案
Nov 05 #Python
如何一键升级Python所有包
Nov 05 #Python
python实现磁盘日志清理的示例
Nov 05 #Python
Python常用外部指令执行代码实例
Nov 05 #Python
Python Pandas数据分析工具用法实例
Nov 05 #Python
Python jieba结巴分词原理及用法解析
Nov 05 #Python
You might like
php中hashtable实现示例分享
2014/02/13 PHP
一个图片地址分解程序(用于PHP小偷程序)
2014/08/23 PHP
PHP经典算法集锦【经典收藏】
2016/09/14 PHP
PHP 根据key 给二维数组分组
2016/12/09 PHP
php实现的pdo公共类定义与用法示例
2017/07/19 PHP
php使用yield对性能提升的测试实例分析
2019/09/19 PHP
关于Laravel参数验证的一些疑与惑
2019/11/19 PHP
html中table数据排序的js代码
2011/08/09 Javascript
DWR实现模拟Google搜索效果实现原理及代码
2013/01/30 Javascript
javascript禁用键盘功能键让右击及其他键无效
2013/10/09 Javascript
在NodeJS中启用ECMAScript 6小结(windos以及Linux)
2014/07/15 NodeJs
jQuery中trigger()方法用法实例
2015/01/19 Javascript
jQuery插件HighCharts绘制2D带有Legend的饼图效果示例【附demo源码下载】
2017/03/10 Javascript
基于JavaScript实现百度搜索框效果
2020/06/28 Javascript
提高Node.js性能的应用技巧分享
2017/08/10 Javascript
angular4 共享服务在多个组件中数据通信的示例
2018/03/30 Javascript
可能被忽略的一些JavaScript数组方法细节
2019/02/28 Javascript
uni-app 组件里面获取元素宽高的实现
2019/12/27 Javascript
[50:17]Newbee vs Serenity 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/18 DOTA
Python获取单个程序CPU使用情况趋势图
2015/03/10 Python
在Debian下配置Python+Django+Nginx+uWSGI+MySQL的教程
2015/04/25 Python
python实现识别相似图片小结
2016/02/22 Python
python遍历 truple list dictionary的几种方法总结
2016/09/11 Python
Python之pandas读写文件乱码的解决方法
2018/04/20 Python
numpy中的ndarray方法和属性详解
2019/05/27 Python
Django使用模板后无法找到静态资源文件问题解决
2019/07/19 Python
Python3与fastdfs分布式文件系统如何实现交互
2020/06/23 Python
非凡女性奢华谦虚风格:The Modist
2017/10/28 全球购物
车间机修工岗位职责
2014/02/28 职场文书
廉洁自律承诺书
2014/03/27 职场文书
县政府领导班子“四风”方面突出问题整改措施
2014/09/23 职场文书
申报优秀教师材料
2014/12/16 职场文书
大学生助学金感谢信
2015/01/21 职场文书
暑期辅导班宣传单
2015/07/14 职场文书
2016年“5.12”护士节慰问信
2015/11/30 职场文书
Redis+Lua脚本实现计数器接口防刷功能(升级版)
2022/02/12 Redis