coroutine也是一种并发模型,但不同于thread和callback,它的所有task都是可以在一个线程里面执行,然后可以通过在一个task里面主动放弃执行来切换到另一个task执行,它的调度是程序级的,不像thread是系统级的调度。gevent就是一个基于coroutine的python网络开发框架,不像twisted那样集成了很多库和协议,gevent非常精简,当然文档也很少,在性能上的话,看了PyCon上的视频,对比了twisted和其他的几个库,在内存的性能上非常地优秀,代码简洁而且也支持多核。
做了个相当简单的ChatService,有登录、群发消息、退出这3个消息,协议就是用简单的json了,有时间再试试xmpp看看。”monkey.patch_all()” 这个调用就把python原生的一些socket对象和方法替换成非阻塞的异步调用,写起来感觉还是很方便。
| 06 | from gevent import monkey; monkey.patch_all(); |
| 07 | from gevent.server import StreamServer |
| 08 | from common import Protocol, gen_pro_data, parse_pro_data |
| 14 | Protocol.Connect : self.connect, |
| 15 | Protocol.SendMsg : self.sendmsg, |
| 16 | Protocol.Quit : self.quit, |
| 19 | def connect(self, sock, data): |
| 20 | userinfo = self._clients.get(sock, None) |
| 22 | self._clients[sock] = data['name'] |
| 23 | print "New User : %s" % (data['name'],) |
| 25 | def sendmsg(self, sock, data): |
| 26 | msg = gen_pro_data(data) |
| 27 | for s, u in self._clients.items(): |
| 31 | def quit(self, sock, data=None): |
| 32 | if sock in self._clients: |
| 33 | print "User [%s] quit." % (self._clients[sock],) |
| 34 | del self._clients[sock] |
| 37 | def process(self, sock, promsg): |
| 39 | func = self._handlers[cmd] |
| 43 | chatservice = ChatService() |
| 48 | length = ord(sock.recv(1)) |
| 49 | content = sock.recv(length) |
| 50 | # protocol : length + json({'cmd' : 'sendmsg', 'msg' : 'hallo'}) |
| 51 | promsg = parse_pro_data(content) |
| 52 | chatservice.process(sock, promsg) |
| 54 | chatservice.quit(sock) |
| 57 | if __name__ == "__main__": |
| 59 | port = int(sys.argv[2]) |
| 60 | print "Start chat server on %s:%s" % (host, port) |
| 62 | chatserver = StreamServer((host, port), serve) |
| 63 | chatserver.serve_forever() |
写了一个简单的测试,创建了5000个连接,然后定时发消息,服务器也没显得有什么压力。
| 03 | from gevent.pool import Pool |
| 05 | def robot(idx, host, port): |
| 07 | chatservice = ChatService(host, port) |
| 08 | chatservice.connect("user%s" % (idx,)) |
| 10 | chatservice.sendmsg("hallo from %s" % (idx,)) |
| 13 | port = int(sys.argv[2]) |
| 16 | for i in range(count): |
| 17 | pool.spawn(robot, i, host, port) |