用 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 并发模型的更多相关文章

  1. 用python理解web并发模型

    最简单的并发 import socket response = 'HTTP/1.1 200 OK\r\nConnection:Close\r\nContent-Length:11\r\n\r\nHel ...

  2. web并发模型

    并发:cpu划分时间片,轮流执行每个请求任务,时间片到期后,换到下一个. 并行:在多核服务器上,每个cpu内核执行一个任务,是真正的并行 IO密集型的应用,由于请求过程中很多时间都是外部IO操作,CP ...

  3. 共享内存 & Actor并发模型哪个更快?

    HI,前几天被.NET圈纪检委@懒得勤快问到共享内存和Actor并发模型哪个速度更快. 前文传送门: 说实在,我内心10w头羊驼跑过...... 先说结论 首先两者对于并发的风格模型不一样. 共享内存 ...

  4. 【并发编程】一文带你读懂深入理解Java内存模型(面试必备)

    并发编程这一块内容,是高级资深工程师必备知识点,25K起如果不懂并发编程,那基本到顶.但是并发编程内容庞杂,如何系统学习?本专题将会系统讲解并发编程的所有知识点,包括但不限于: 线程通信机制,深入JM ...

  5. Java并发指南2:深入理解Java内存模型JMM

    本文转载自互联网,侵删   一:JMM基础与happens-before 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实 ...

  6. python三大web框架Django,Flask,Flask,Python几种主流框架,13个Python web框架比较,2018年Python web五大主流框架

    Python几种主流框架 从GitHub中整理出的15个最受欢迎的Python开源框架.这些框架包括事件I/O,OLAP,Web开发,高性能网络通信,测试,爬虫等. Django: Python We ...

  7. 理解Storm并发

    作者:Jack47 PS:如果喜欢我写的文章,欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 注:本文主要内容翻译自understanding-the-parall ...

  8. Server Develop (五) Linux并发模型

    Linux并发模型 目前可以实现并发程序的方法有Apache模型(Process Per Connection,简称PPC),TPC(Thread PerConnection)模型,以及select模 ...

  9. Linux并发模型

    Linux并发模型 Linux并发模型 目前可以实现并发程序的方法有Apache模型(Process Per Connection,简称PPC),TPC(Thread PerConnection)模型 ...

随机推荐

  1. 【POJ 1964】 City Game

    [题目链接] http://poj.org/problem?id=1964 [算法] 记f[i]表示第i行最多向上延伸的行数 然后,对于每一行,我们用单调栈计算出这一行向上延伸的最大矩形面积,取最大值 ...

  2. hihoCoder 1187

    今天BC爆0了....但是日子还是要过的....要回学校毕业了~~大学就这么“荒废”了. 这个是hihoCoder的1187,比较基础的一道题. 题目链接: http://hihocoder.com/ ...

  3. 使用filezella服务器配置ftp

    使用FileZilla配置FTP站点,可参考以下步骤: 1.打开Filezilla Server服务端: 点击[Edit]->[Users],或者点击如下图标新增用户. 2.添加FTP帐号后,设 ...

  4. go之数组

    一.数组概念 go语言提供了数组类型的数据结构 数组是具有 [唯一类型] 的一组 [固定长度] 的数据项序列,这种类型可以是任意类型 二.数组声明 var variable_name [SIZE]va ...

  5. .net MVC成长记录(二)

    今天上班的任务完成了,接下来写一下博客,巩固一下,再学习一些新知识. 闲话不多说,我们言归正传.昨天讲到了如何mvc框架在微软下,已经变成了一个非常灵活非常‘干净’的开发框架了, 同时也讲述了如何创建 ...

  6. JQuery 兼容所有浏览器的复制到剪切板功能

    灵机一动想的点子,应该不难理解 <textarea onmousedown='selectAll(this);'>11111</textarea> function selec ...

  7. HTML5标签构成

    一个HTML5文件是由一些列的元素和标签组成的.元素是HTML5文件的重要组成部分,例如title(文件标题).img(图像)及table(表格)等.元素名不区分大小写,而HTML5用标签来规定元素的 ...

  8. angular2之组件通讯

    定义父组件,在父组件中以路由插座形式引入子组件,定义相关输入输出属性 可以在同一模块内部定义多个组件,将一个组件引入另一个组件中去:也可以该模块整体导出,将该模块导入到其他模块,这样此模块中的组件就能 ...

  9. jQueryAjax模拟按键消抖(可设置抖动延迟时间)

    在硬件中,按键等都会有抖动现象,如何消除抖动,不重复触发事件呢,这就要用到消抖机制了. 这是我用jQuery模拟硬件消抖原理,额,可能是吧...又不对的地方,希望有高手指点指点. <!DOCTY ...

  10. dubbo之泛化引用

    使用泛化调用 泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 Gene ...