用 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)模型 ...
随机推荐
- 【BZOJ 1602】 牧场行走
[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1602 [算法] 倍增求LCA [代码] #include<bits/stdc+ ...
- 洛谷P3809 后缀数组模板
题目:https://www.luogu.org/problemnew/show/P3809 刚学了后缀数组,看人家手写演示了半天,大概明白了过程,但完全写不出来代码: 于是借鉴了许多,不过都差不多, ...
- bzoj3957
数学+模拟 细节很多 首先我们发现,如果两个区间已经包含,那么可以输出empty,一个数能通过变换得到另一个区间的数,这个区间的大小必须小于等于终点区间的大小.加法不会改变区间大小,只有乘法会改变,而 ...
- 树莓派(raspberry)启用root账户
树莓派使用的linux是debian系统,所以树莓派启用root和debian是相同的. debian里root账户默认没有密码,但账户锁定. 当需要root权限时,由默认账户经由sudo执行,Ras ...
- Nightmare --- 炸弹时间复位
题目大意: 该题为走迷宫,其条件有如下6个: 1, 迷宫用二维数组来表示: 2, 人走动时不能越界,不能在墙上走: 3, 当走到出口时,若剩余时间恰好为0,则失败: 4, 找到炸弹复位装置,若剩余时间 ...
- 【贪心】小Y的炮[cannon]题解
模拟赛的题目,做的时候由于第二题表打太久了,只剩下40分钟,想都没想就写了一个爆搜20分... 这道题单调性很关键,下面会解释 P.S.解释在代码里 #include<cstdio> #i ...
- CSS的常用属性(二)
盒子模型之边框 border-(top/bottom/left/right)-style: solid 边框的风格 如(solid 实线,dotted 点线,dashed 虚线) border-top ...
- Django学习案例一(blog):二. 连接数据库
本例使用了django默认的sqlite3数据库,配置文件不需要作调整: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite ...
- 3B课程笔记分享_StudyJams_2017
昨晚才发现 Study Jams China的官方论坛也支持MarkDown,所以直接发在了那上面.http://www.studyjamscn.com/thread-21807-1-1.html
- Android 解析XML—pull解析方式
在Android中,常见的XML解析器分别为SAX解析器.DOM解析器和PULL解析器,其中PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部 ...