Gevent
特点: 是一个基于libev的并发库 它为各种并发和网络相关的任务提供了整洁的API 基于libev的高效时间循环 基于greenlet的轻量级执行单元 重用Python标准库API内容(比如Event Queues) socket谢童ssl 利用线程池或者c-cares来执行DNS查询
a. 进程 线程 协程
进程: 概念: 进程是正在运行程序的实例 特点: 具有独立地址空间 是操作系统资源分配的基本单元 进程上下文: 进程的物理实体与支持进程运行的物理环境,包括:地址空间 系统栈 打开文件表 。。。 上下文切换: 由一个进程的上下文转到另一个进程的上下文 系统开销: 运行操作系统程序完成系统管理工作所话费的时间和空间 一个进程可以包括多个线程 线程: - 线程是程序执行的最小单位 - 多线程的优点: 提高程序的并发性 协程: - 协程可以认为是一种用户态的线程,与系统提供的线程不同点是 他需要主动让出cpu时间,而不是由系统进行调度,即控制权在程序员手上。 优缺点: - 进程创建销毁成本高 - 线程开销比进程低,但切换成本高,线程间同步变复杂。 - 协程不陷入内核的情况进行上下文切换,没有同步问题,但需要手动切换。 下图解释: 进程是有状态的 刚启动时就绪态,如果进程调度算法调度到这个进程 就会变成执行态。 分配的算法是有时间片的 当时间片使用完 会继续切回就绪态 如果时间片没有使用完 当有IO发生就会阻塞态 阻塞完成 继续切回用户态
b. 阻塞 非阻塞 同步 异步
阻塞与非阻塞: 网络IO - 对于网络IO主要指socket,socket是进程间通信的一种方法 对于阻塞与非阻塞的情况就是在使用socket中在调用它的API时它的connect read write 会发生阻塞,也可以设置为非阻塞。 - socket会在connect/read/write时发生阻塞 阻塞 - 阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回 非阻塞 - 非阻塞指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回 同步与异步: 同步: 同步就是发生调用时,一定等待结果返回,整个调用才结束。 异步: 异步就是发生调用后,立即返回,不等待结果返回,被调用者通过状态/通知来通知调用者,或通过回调函数处理这个调用。 同步异步 与 阻塞非阻塞区别: 阻塞/非阻塞 它们是程序在等待消息(无所谓同步或者异步)时的状态。 同步/异步 是程序获得关注消息通知的机制。 同步阻塞: 效率最低(日志程序) 同步非阻塞: 效率也不高(需要轮询) 异步阻塞: 一般模式线程回调 异步非阻塞: IOCP
connect 阻塞做的操作 客户端和服务端做交互的时候,客户端连接服务器,connect时候阻塞会自动打开,会向服务端发送一个序列号,服务端接受后处于阻塞。
数据接受阻塞 应用层recvfrom调用过程中会发生一个系统调用。系统调用会陷入内核里面。如果没有数据准备好,会等待数据直到数据准备好,内核得到数据把数据复制到用户空间,在返回。
c. 并发 并行
#并发 并发是指两个或多个事件在同一时间间隔发生。就是同时处理很多事情,比如串行同时处理一件事情。 在单核系统中,为了提高cpu利用率,系统采用时间片轮询等调度方式,对多个线程轮换执行,在宏观上看线程是同时执行的 从微观上看某一时刻只执行一个线程。在发生资源竞争或者大量的上下文切换会导致性能消耗。
d. monkey patch
# monkey patch - 在动态语言中,不去改变源码而对功能进行追加和变更就叫做 Monkey Patching(猴子补丁) 1. 追加功能 2. 功能变更 3. 修正程序错误 4. 增加钩子,在执行某个方向的同时执行一些其他的处理 如打印日志 实现AOP等 #模块 - socket - dns - time - select - thread - os - ssl - subprocess - sys - builtins - signal 默认阻塞的模块都被替换成非阻塞,协作式的。 gevent支持异步协作的域名解析系统。 time.sleep只是协作内休眠,并不阻塞线程。
import gevent from gevent import monkey urls = ["http://www.baidu.com","http://www.zhihu.com","http://www.xiaohuar.com/"] import urllib2 import timeit def print_head(url): print("start %s"%url) gevent.sleep(0) data = urllib2.urlopen(url).read() print("%s:%s bytes: %r" %(url,len(data),data[:50])) def test(): jobs = [gevent.spawn(print_head,url) for url in urls] gevent.joinall(jobs) monkey.patch_all() #加上这行效率明显提升 print timeit.Timer(stmt='test()',setup="from __main__ import test").timeit(number=10)
e. gevent.server
# 服务器端 import socket import sys host = "localhost" port = 5000 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.bind((host,port)) s.listen(100) while True: conn,addr = s.accept() print 'connected with',addr[0], ':',str(addr[1]) data = conn.recv(1024) print data conn.close() s.close() # 客户端 from gevent import monkey monkey.patch_all() import gevent import socket def do_connect(addr,index): sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.connect(addr) gevent.sleep(0.5) sock.send("hello world") addr = ('localhost',5000) greenlets = [] num = 1 for i in xrange(num): g = gevent.spawn(do_connect,addr,i) greenlets.append(g) gevent.joinall(greenlets)
# gevent server from gevent.server import StreamServer import gevent from gevent import monkey monkey.patch_all() import socket def handler(sock,addr): while True: data = sock.recv(1024) if not data: print addr, "closed" return print "get ",data server = StreamServer(('127.0.0.1',3000),handler) server.serve_forever()
f. gevent.WSGIServer
WSGI 介绍 web服务器网关接口(Python Web Server Gateway Interface 缩写为WSGI) 是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口 WSGI server WSGI server所做的工作仅仅是将从客户端收到的请求传递给WSGI application,然后将WSGI application的返回值作为响应传给客户端。 WSGI application 可以是Flask,Diango 等web框架。