最近用python写一个网络服务的模拟测试程序,先是用同步socket实现了一个简单的server,后来发现要没法添加定时器来定时处理一些任务,于是网上搜索python的异步事件框架库,发现了gevent。
1. gevent介绍
gevent是一个python的网络库,它使用greenlet轻量级线程,使用libevent实现事件驱动。我写的模拟测试程序只使用了libevent的事件驱动,下面就只介绍下gevent的事件驱动编程。
2. 事件驱动
事件驱动顾名思义,就是通过事件来驱动程序的运行,它主要包括三个部分:事件,事件处理,事件分发。gevent是事件驱动是对libevent的封装,因此它关于上述三个部分的描述也类似于libevent。
gevent的事件驱动处理都在gevent.core模块,主要包含以下类:
class gevent.core.event(evtype, handle, callback[, arg])
使用callback事件处理回调函数创建一个事件对象。
evtype - 事件类型,EV_READ or EV_WRITE, or EV_SIGNAL
handle - 事件句柄,对于EV_READ和EV_WRITE来说handle是一个socket, 对于EV_SIGNAL来说就是一个信号值
callback - 事件发生时回调该函数来对事件进行处理
arg - 可选参数
add([timeout])
调用该方法将事件添加到事件调度器,该函数有一个可选的timeout参数,以秒为单位。设置了该参数后事件timout秒后将被执行。
cancel()
取消事件的调度。
pending
事件的属性,如果事件仍然在事件调度器中等候调度将返回True。
class gevent.core.read_event
创建一个类型为EV_READ的事件
class gevent.core.write_event
创建一个类型为EV_READ的事件
class gevent.core.timer
创建一个定时器
class gevent.core.signal
创建一个信号事件
gevent.core.init()
初始化事件队列
gevent.core.dispatch()
分发事件队列中所有的事件,成功返回0,当没有事件注册时返回1.
gevent.core.loop()
分发队列中所有待处理的事件,只执行一次。成功返回0,没有事件注册时返回1。该函数与dispatch的区别是该函数单趟执行。
3. 开发流程
1. gevent.core.init()
2. 创建事件
3. 添加事件
4. gevent.core.loop()
4. 程序示例
使用gevent实现的echo,同时有一个定时器
from gevent import core
from socket import *
from sys import *
class mon_client_handler:
def __init__(self, s_fd, addr ):
self.s_fd = s_fd
self.addr = addr
self.r_ev = core.read_event( self.s_fd.fileno(), self.on_read )
self.w_ev = core.write_event( self.s_fd.fileno(), self.on_write )
self.t_ev = core.timer( -1, self.on_timeout )
self.r_buffer = ''
self.w_buffer = ''
def on_read(self, ev, ev_type ):
#print 'on_read'
#print self.addr
chunk = self.s_fd.recv(1024)
print 'read chunk len:', len(chunk)
self.r_buffer += chunk
#print 'buffer len:', len(self.r_buffer)
self.process()
self.r_ev.add()
def on_write(self, ev, ev_type ):
#print self,ev,ev_type
if len(self.w_buffer) > 0:
size = self.s_fd.send(self.w_buffer)
print len(self.w_buffer), size
self.w_buffer = self.w_buffer[size:]
if len(self.w_buffer) > 0:
self.w_ev.add()
def write(self, buf):
#print 'write', len(buf)
self.w_buffer += buf
if len(self.w_buffer) > 0:
self.w_ev.add()
def start(self):
self.r_ev.add()
self.t_ev.add(10)
def stop(self):
self.r_ev.cancel()
self.w_ev.cancel()
self.t_ev.cancel()
self.s_fd.close()
def process(self):
self.write(self.r_buffer)
self.r_buffer=''
def on_timeout(self):
print 'timeout...'
self.t_ev.add(5)
class mon_server:
def __init__(self, port, max_clt):
self.port = port
self.max_clt = max_clt
self.fd = None
self.event = None
self.started = False
def start(self):
if self.started:
print 'mon_server alread started!'
return False
self.fd = socket(AF_INET, SOCK_STREAM)
self.fd.bind(('0.0.0.0', self.port))
self.fd.setblocking(0)
self.event = core.read_event( self.fd.fileno(), self.on_request )
self.event.add()
self.fd.listen(self.max_clt)
self.clients = []
return True
def stop(self):
self.event.cancel()
self.fd.close()
def on_request( self, e, evtype ):
(clt_fd, addr) = self.fd.accept()
clt_fd.setblocking(0)
print 'Connected by', addr
clt = mon_client_handler( clt_fd, addr )
self.clients.append(clt)
clt.start()
def main():
core.init()
svr = mon_server( 8888, 1 )
svr.start()
core.dispatch()
print 'Server exit!'
if __name__ == '__main__':
main()