协程
什么是协程
协程,又称微线程,纤程。英文名Coroutine。协程是一条执行序列,拥有自己独立的栈、局部变量和指令指针,同时又与其他的协同程序共享全局变量。子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。协程看上去也是子程序,但执行过程中,在“子程序”(协程)内部可中断,然后转而执行别的“子程序”,在适当的时候再返回来接着执行。
线程与协程
- 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU。
- 线程进程都是同步机制,而协程则是异步
- 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态
- 协程需要人为的设置使其互相让渡CPU控制权,而不是抢占。协程既能够共享数据结构,又不需要显式的互斥控制,因为只有当一个协程让出了控制权后其他的协程才能访问彼此共享的数据结构。
协程的好处
- 1、每个协程都有自己私有的栈和局部变量
- 2、同一时间只有一个协程在运行,无需对全局变量进行加锁
- 3、顺序可控,完全是由程序控制执行的顺序。而通常多线程一旦启动,运行时序是没法预测的,因此通常会给测试所有的程序带来问题。
eventlet
什么是 eventlet
Eventlet is a concurrent networking library for Python that allows you to change how you run your code, not how you write it.
- It uses epoll or kqueue or libevent for highly scalable non-blocking I/O.
- Coroutines ensure that the developer uses a blocking style of programming that is similar to threading, but provide the benefits of non-blocking I/O.
- The event dispatch is implicit, which means you can easily use Eventlet from the Python interpreter, or as a small part of a larger application.
Eventlet 主要是用于网络并发的库,专注于代码运行逻辑。
eventlet 基本API
孵化绿色线程
eventlet.spawn(func, *args, **kw)
该函数创建一个使用参数 *args 和 **kw 调用函数 func 的绿色线程,多次孵化绿色线程会并行地执行任务。该函数返回一个greenthread.GreenThread 对象,可以用来获取函数 func 的返回值
eventlet.spawn_n(func, *args, **kw)
作用类似于spawn(),只不过无法获取函数 func 执行完成时的返回值或抛出的异常。该函数的执行速度更快
eventlet.spawn_after(seconds, func, *args, **kw)
作用同于spawn(),等价于 seconds 秒后执行spawn()。可以对该函数的返回值调用 GreenThread.cancel() 退出孵化和阻止调用函数 func
控制绿色线程
eventlet.sleep(seconds=0)
挂起当前的绿色线程,允许其他的绿色线程执行
class eventlet.GreenPool
控制并发的绿色线程池,可以控制并发度,进而控制整个并发所消耗的内存容量,或限制代码某一部分的连接数等
class eventlet.GreenPile
GreenPile 对象代表了工作块。该对象是一个可以向其中填充工作的迭代器,便于以后从其中读取结果
class eventlet.Queue
便于执行单元之间进行数据交流的基本构件,用于绿色线程之间的通信
class eventlet.Timeout
可以向任何东西添加超时,在 timeout 秒后抛出异常 exception。当 exception 被忽视或为None时,Timeout 实例自身会被抛出。Timeout 实例是上下文管理器(context manager),因此可以在 with 语句中使用
补丁函数
eventlet.import_patched(modulename, *additional_modules, **kw_additional_modules)
引入标准库模块绿化后的版本,这样后续代码以非阻塞的形式执行,所需要的参数就是目标模块的名称
eventlet.monkey_patch(all=True, os=False, select=False, socket=False, thread=False, time=False)
在全局中为指定的系统模块打补丁,补丁后的模块是“绿色线程友好的”,关键字参数指示哪些模块需要被打补丁,如果 all 是真,那么所有的模块会被打补丁而无视其他参数;否则才由具体模块对应的参数控制对指定模块的补丁。多数参数为与自己同名的模块打补丁,如os, time, select,但是 socket 参数为真时,如果 ssl 模块也存在,会同时补丁socket模块和ssl模块,类似的,thread参数为真时,会补丁thread, threading 和 Queue 模块。
WSGI
什么是 WSGI
Python web开发中,服务端程序可分为2个部分:
- 1、服务器程序(用来接收、整理客户端发送的请求)
- 2、应用程序(处理服务器程序传递过来的请求)
在开发应用程序的时候,我们会把常用的功能封装起来,成为各种框架,比如Flask,Django,Tornado(使用某框架进行web开发,相当于开发服务端的应用程序,处理后台逻辑)但是,服务器程序和应用程序互相配合才能给用户提供服务,而不同应用程序(不同框架)会有不同的函数、功能。 此时,我们就需要一个标准,让服务器程序和应用程序都支持这个标准,那么,二者就能很好的配合了
WSGI 是 python web 开发的标准,类似于协议。它是服务器程序和应用程序的一个约定,规定了各自使用的接口和功能,以便二和互相配合
WSGI应用程序的部分规定
- 1、应用程序是一个可调用的对象
可调用的对象有三种:
1、一个函数
2、一个类,必须实现call()方法
3、一个类的实例 - 2、这个对象接收两个参数
这两个参数是environ, start_response. 以可调用对象为一个类为例:
class application:
def __call__(self, environ, start_response):
pass
- 3、可调用对象需要返回一个可迭代的值。以可调用对象为一个类为例:
class application:
def __call__(self, environ, start_response):
return [xxx]
WSGI服务器程序的部分规定
服务器程序需要调用应用程序
def run(application): #服务器程序调用应用程序
environ = {} #设定参数
def start_response(xxx): #设定参数
pass
result = application(environ, start_response) #调用应用程序的__call__函数(这里应用程序是一个类)
def write(data):
pass
def data in result: #迭代访问
write(data)
Middleware
middleware是介于服务器程序和应用程序中间的部分,middleware对服务器程序和应用程序是透明的。
- 对于服务器程序来说,middleware就是应用程序,middleware需要伪装成应用程序,传递给服务器程序
- 对于应用程序来说,middleware就是服务器程序,middleware需要伪装成服务器程序,接受并调用应用程序
综合实例
使用 eventlet 模块搭建 WEB 服务器。首先使用 eventlet 创建两个协程,在每个协程中使用 eventlet 模块创建 SWGI 服务器,并在每个服务器上绑定相关的业务。Openstack 项目 nova-api 的 Restfull API就是使用这样的模型实现的。
import eventlet
from eventlet import wsgi
from eventlet import greenpool
def hello_world(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['Hello, World!\r\n']
def hello_demo(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['Hello, Demo!\r\n']
def run_service_world(listen_fd):
"""Service start wrapper.
:param service: service to run
:returns: None
"""
try:
eventlet.wsgi.server(listen_fd, site=hello_world)
except Exception:
print('Error starting thread.')
raise SystemExit(1)
def run_service_demo(listen_fd):
"""Service start wrapper.
:param service: service to run
:returns: None
"""
try:
eventlet.wsgi.server(listen_fd, site=hello_demo)
except Exception:
print('Error starting thread.')
raise SystemExit(1)
pool = greenpool.GreenPool(10)
listen_fd_1 = eventlet.listen(('', 8090))
listen_fd_2 = eventlet.listen(('', 8091))
pool.spawn_n(run_service_world, listen_fd_1)
pool.spawn_n(run_service_demo, listen_fd_2)
pool.waitall()
运行效果: