了解python异步web框架原理

今天给大家介绍下使用python自带的库如何编写一个异步的server。

先看下同步的方式,定义一个socket对象,绑定完端口后开始监听,在一个无限循环内接收到的数据,经过处理后返回给客户端,并通过with关闭。

import socket

HOST ='localhost'
PORT=8888
with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.bind((HOST,PORT))
s.listen(128)
while True:
conn,addr = s.accept()
print('Connected from:',addr)
with conn:
while 1:
data = conn.recv(1024)
if not data:
break
conn.sendall(data)

我们可以使用浏览器直接测试这个服务器,就可以看到浏览器收到服务器返回的数据。

下面开始异步的讲解,建议大家可以直接去官网的链接Selectors模块看它的说明。其实大部分的异步框架都是通过系统提供的异步I/O机制实现的。

简单了解下selectors:通过它我们可以同时监听多个socket连接,并且注册一个回调函数,这样就能实现非阻塞的读写。我们可以使用BaseSelector来根据平台自动选择最合适的机制。我们只要通过几个几个简单的函数就可以完成对它的使用register方法注册一个socket对象,这里是官网的定义:

abstractmethod register(fileobj, events, data=None)
Register a file object for selection, monitoring it for I/O events.

还有与之相对的unregister方法。接下来是select来实现事件的循环。官网首页有一个使用的例子,下面就这个例子我们扩展开来具体看看select的使用。

定义一个EventLoop类:

class EventLoop:
def __init__(self,selector=None):
if selector is None:
selector= selectors.DefaultSelector()
self.selector=selector

def run_forever(self):
while True:
events =self.selector.select()
for key,mask in events:
if mask == selectors.EVENT_READ:
callback = key.data
callback(key.fileobj)
else:
callback,msg = key.data
callback(key.fileobj,msg)

在这个类里的初始化中我们使用DefaultSelector新建了一个selectors的对象,然后通过run_forever的函数来执行事件循环。

接着再定义一个TCPEchoServer的类:

class TCPEchoServer:
def __init__(self,host,port,loop):
self.host = host
self.port = port
self._loop = loop
self.s = socket.socket()

def run(self):
self.s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
self.s.bind(self.host,self.port)
self.s.listen(128)
self.s.setblocking(False)
self._loop.selector.register(self.s,selectors.EVENT_READ,selef._accept)
self._loop.run_forever()

在初始化里,我们接受host地址和端口还有刚才定义的EventLoop实例;

在run函数里开始对socket进行各种设置:

1.绑定端口

2设置最大监听上限128

3设置非阻塞

4.设置回调函数

5启动事件循环

继续编写回调函数:

def _accept(self,scok):
conn,addr = sock.accept()
print('accepted',conn,'from',addr)
conn.setblocking(False)
self._loop.selector.register(conn,selectors.EVENT_READ,self._on_read)

当受到请求时,我们print一些提示,接着继续注册读事件的回调函数:

def _on_read(self,conn):
msg = conn.recv(1024)
if msg:
print('echoing',repr(msg),'to',conn)
self._loop.selector.modify(conn,selectors.EVENT_WRITE,(self._on_wirte,msg))
else:
print('closing',conn)
self._loop.selector.unregister(conn)
conn.close()

先通过recv方法获取到请求的信息;

1如果请求信息不为空就print到控制台,然后为写事件注册回调函数。

2如果请求为空,则直接关闭连接,并且在事件循环中关闭连接。

的回调函数:

def _on_wirte(self ,conn,msg):
conn.sendall(msg)
self._loop.selector.modify(conn,selectors.EVENT_READ,self._on_read)

使用sendall将信息发送给客户端,并且使用modify修改为读事件。

这样server就完成了,启动这个server:

event_loop = EventLoop()
echo_server = TCPEchoServer('localhost',8888,event_loop)
echo_server.run()

到此为止就将官网的例子按照class的方式重写完了,大家可以使用压力测试工具批量测试下这个server。

版权所属,如需转载,请注明出处:搜闲鱼

2,445 次浏览

发表评论

邮箱地址不会被公开。 必填项已用*标注