用 Python 理解 Web 并发模型
用 Python 理解 Web 并发模型
http://www.jianshu.com/users/1b1fde012122/latest_articles
来源:MountainKing
链接:http://www.jianshu.com/p/80feb3bf5c70#
前言
虽然异步是我们急需掌握的高阶技术,但是不积跬步无以至千里,同步技术的学习是不能省略的。今天这篇文章主要用Python来介绍Web并发模型,直观地展现同步技术的缺陷以及异步好在哪里。
最简单的并发
import socket
response = 'HTTP/1.1 200 OK\r\nConnection: Close\r\nContent-Length: 11\r\n\r\nHello World'
server = socket.socket()
server.bind(('0.0.0.0', 9527))
server.listen(1024)
while True:
client, clientaddr = server.accept() # blocking
request = client.recv(1024) # blocking
client.send(response) # maybe blocking
client.close()
上面这个例子太简单了,访问localhost:9527,返回“Hello World”。用ab来测试性能,数据如下:
ab -n 100000 -c 8 http://localhost:9527/
Time taken for tests: 1.568 seconds
发送10万个请求,8(我的CPU核数为8)个请求同时并发,耗时1.568秒。
性能瓶颈在哪里呢?就在上面的两个半阻塞。
accept和recv是完全阻塞的,而为什么send是半个阻塞呢?
在内核的 socket实现中,会有两个缓存 (buffer)。read buffer 和 write buffer 。当内核接收到网卡传来的客户端数据后,把数据复制到 read buffer ,这个时候 recv阻塞的进程就可以被唤醒。
当调用 send的时候,内核只是把 send的数据复制到 write buffer 里,然后立即返回。只有 write buffer 的空间不够时 send才会被阻塞,需要等待网卡发送数据腾空 write buffer 。在 write buffer的空间足够放下 send的数据时进程才可以被唤醒。
如果一个请求处理地很慢,其他请求只能排队,那么并发量肯定会受到影响。
多进程
每个请求对应一个进程倒是能解决上面的问题,但是进程太占资源,每个请求的资源都是独立的,无法共享,而且进程的上下文切换成本也很高。
import socket
import signal
import multiprocessing
response = 'HTTP/1.1 200 OK\r\nConnection: Close\r\nContent-Length: 11\r\n\r\nHello World'
server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('0.0.0.0', 9527))
server.listen(1024)
def handler(client):
request = client.recv(1024)
client.send(response)
client.close()
#多进程里的子进程执行完后并不会死掉,而是变成僵尸进程,等待主进程挂掉后才会死掉,下面这条语句可以解决这个问题。
signal.signal(signal.SIGCHLD,signal.SIG_IGN)
while True:
client, addr = server.accept()
process = multiprocessing.Process(target=handler, args=(client,))
process.start()
Prefork
这是多进程的改良版,预先分配好和CPU核数一样的进程数,可以控制资源占用,高效处理请求。
import socket
import multiprocessing
response = 'HTTP/1.1 200 OK\r\nConnection: Close\r\nContent-Length: 11\r\n\r\nHello World'
server = socket.socket()
server.bind(('0.0.0.0', 9527))
server.listen(1024)
def handler():
while True:
client, addr = server.accept()
request = client.recv(1024)
client.send(response)
client.close()
processors = 8
for i in range(0, processors):
process = multiprocessing.Process(target=handler, args=())
process.start()
耗时:1.640秒。
线程池
import Queue
import socket
import threading
response = 'HTTP/1.1 200 OK\r\nConnection: Close\r\nContent-Length: 11\r\n\r\nHello World'
server = socket.socket()
server.bind(('0.0.0.0', 9527))
server.listen(1024)
def handler(queue):
while True:
client = queue.get()
request = client.recv(1024)
client.send(response)
client.close()
queue = Queue.Queue()
processors = 8
for i in range(0, processors):
thread = threading.Thread(target=handler, args=(queue,))
thread.daemon = True
thread.start()
while True:
client, clientaddr = server.accept()
queue.put(client)
耗时:3.901秒,大部分时间花在队列上,线程占用资源比进程少(资源可以共享),但是要考虑线程安全问题和锁的性能,而且python有臭名昭著的GIL,导致不能有效利用多核CPU。
epoll
import select
import socket
response = 'HTTP/1.1 200 OK\r\nConnection: Close\r\nContent-Length: 11\r\n\r\nHello World'
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False)
server_address = ('localhost', 9527)
server.bind(server_address)
server.listen(1024)
READ_ONLY = select.EPOLLIN | select.EPOLLPRI
epoll = select.epoll()
epoll.register(server, READ_ONLY)
timeout = 60
fd_to_socket = { server.fileno(): server}
while True:
events = epoll.poll(timeout)
for fd, flag in events:
sock = fd_to_socket[fd]
if flag & READ_ONLY:
if sock is server:
conn, client_address = sock.accept()
conn.setblocking(False)
fd_to_socket[conn.fileno()] = conn
epoll.register(conn, READ_ONLY)
else:
request = sock.recv(1024)
sock.send(response)
sock.close()
del fd_to_socket[fd]
最后祭出epoll大神,三大异步通信框架Netty、NodeJS、Tornado共同采用的通信技术,耗时1.582秒,但是要注意是单进程单线程哦。epoll真正发挥作用是在长连接应用里,单线程处理上万个长连接玩一样,占用资源极少。
用 Python 理解 Web 并发模型的更多相关文章
- 用python理解web并发模型
最简单的并发 import socket response = 'HTTP/1.1 200 OK\r\nConnection:Close\r\nContent-Length:11\r\n\r\nHel ...
- web并发模型
并发:cpu划分时间片,轮流执行每个请求任务,时间片到期后,换到下一个. 并行:在多核服务器上,每个cpu内核执行一个任务,是真正的并行 IO密集型的应用,由于请求过程中很多时间都是外部IO操作,CP ...
- 共享内存 & Actor并发模型哪个更快?
HI,前几天被.NET圈纪检委@懒得勤快问到共享内存和Actor并发模型哪个速度更快. 前文传送门: 说实在,我内心10w头羊驼跑过...... 先说结论 首先两者对于并发的风格模型不一样. 共享内存 ...
- 【并发编程】一文带你读懂深入理解Java内存模型(面试必备)
并发编程这一块内容,是高级资深工程师必备知识点,25K起如果不懂并发编程,那基本到顶.但是并发编程内容庞杂,如何系统学习?本专题将会系统讲解并发编程的所有知识点,包括但不限于: 线程通信机制,深入JM ...
- Java并发指南2:深入理解Java内存模型JMM
本文转载自互联网,侵删 一:JMM基础与happens-before 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实 ...
- python三大web框架Django,Flask,Flask,Python几种主流框架,13个Python web框架比较,2018年Python web五大主流框架
Python几种主流框架 从GitHub中整理出的15个最受欢迎的Python开源框架.这些框架包括事件I/O,OLAP,Web开发,高性能网络通信,测试,爬虫等. Django: Python We ...
- 理解Storm并发
作者:Jack47 PS:如果喜欢我写的文章,欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 注:本文主要内容翻译自understanding-the-parall ...
- Server Develop (五) Linux并发模型
Linux并发模型 目前可以实现并发程序的方法有Apache模型(Process Per Connection,简称PPC),TPC(Thread PerConnection)模型,以及select模 ...
- Linux并发模型
Linux并发模型 Linux并发模型 目前可以实现并发程序的方法有Apache模型(Process Per Connection,简称PPC),TPC(Thread PerConnection)模型 ...
随机推荐
- DataGuard总体结构
一.DataGuard总体结构 总体目标 1. 描述计划和非计划停机的不同因数 2. DataGuard的主要组件 3. 物理以及逻辑DataGuard的异同 4. 建立DataGua ...
- 最短路径问题(floyd)
http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=1867 #include<stdio.h& ...
- .Net Core项目上Azure Docker云
1.找到创建资源-容器-Container Instances 2.安装模板,填写私有映像表的相关信息 3.创建成功,运行测试.
- 离线安装 Rancher2.2.4 HA 集群
一.先决条件(所有主机执行) 1.1 基础设置 1.安装基础软件 yum install -y vim net-tools wget lrzsz 2.防火墙 sed -i 's/SELINUX=enf ...
- UIView动画基础
1 Position 平移 [UIView animateWithDuration:1.0 animations:^{ _blueView.centerX = self.view.width -100 ...
- ListView(2)最简单的上拉刷新、下拉刷新代码
效果 最简单的上拉刷新和下拉刷新,当listview滚动到底部时向上拉刷新数据.当listview滚动到最顶部时下拉刷新. 图1,上拉刷新 图2,下拉刷新 1.设置lisview 加载he ...
- C#:使用FastReport打印带图片传参模板的实现方法
大家都知道,C#打印图片可以直接调用PrintDocument控件的PrintPage事件,通过画刷对image对象直接进行绘制.但是这种方法存在局限,例如如果打印的图片需要按纸张大小进行缩放的话,那 ...
- python--2、数据类型
字符串 name='jinyudong' 按索引取值.正向取 与 反向取 name['3'] 'y' name['-3'] 'o' 切片(若要使用倒序指定步长为-1),开始或者结束不指定即为到最边上的 ...
- 本地Gradle配置方法,免去长时间的更新同步等待
通常gradle项目在gradle\wrapper\gradle-wrapper.properties中配置在线gradle: distributionBase=GRADLE_USER_HOME di ...
- dubbo之集群容错
在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试. 集群容错模式 1. Failover Cluster 失败自动切换,当出现失败,重试其它服务器 .通常用于读操作,但 ...