• 1. pysquoiadb异步化实践赖勇浩
  • 2. 内容提要自我介绍 因何结缘SequoiaDB SequoiaDB 初体验 安装与 shell Python Driver 为什么要异步化? 什么是异步? Python 的异步网络库 greenlet 与 geventpysequoiadb 的问题 借鉴MySQL Connector/Python pysequoiadb异步化 源码分析 过程和结果 后续的工作
  • 3. 自我介绍赖勇浩(http://laiyonghao.com/) 近10年开发经验,曾在网易(广州)、广州银汉等网游公司就职,目前已经创业(广州齐昌网络科技有限公司),仍然从事编码工作。 公司(http://gzqichang.com/)主要从事技术咨询与开发外包工作。 对 Python 编程语言有较深的积累和丰富经验,合著有《 编写高质量代码:改善Python程序的91个建议 》一书。
  • 4. 因何结缘SequoiaDB?开发外包业务带来需求的多样性 彩票、教育、餐饮行业应用 O2O 电子商务与电子政务 … 开发外包业务带来的成本压缩的必要性 NoSQL 更加自由,适合在此上建立自有的存储层 SequoiaDB本身的特点 本土研发,沟通方便;支持SQL;支持事务;
  • 5. SequoiaDB 初体验安装 git clone https://github.com/SequoiaDB/SequoiaDB.git SequoiaDB cd SequoiaDB scons --all cd driver/python scons python setup.py installhttp://scons.org/
  • 6. SequoiaDB 初体验开启服务 cd SequoiaDB/bin sdbstart -p 11810 --force 体验 shell shell 是一个 JavaScript 解析器 从命令行与 SequoiaDB 实例交互 ./sdb var db = new Sdb() ; db.help() ;最简部署
  • 7. Python Driver : pysequoiadbimport pysequoiadb as sdb db = sdb.client() cs = db.get_collection_space('foo’) # db[‘foo’] or db.foo cl = cs.get_collection('bar’) # cs[‘bar’] or cs.bar cr = cl.query() while True: try: record = cr.next() except sdb.error.SDBEndOfCursor: break print record
  • 8. 为什么要异步化?Global Interpreter Lock (GIL) The GIL ensures that only one thread runs in the interpreter at once Thread is expensive.
  • 9. 为什么要异步化?
  • 10. 为什么要异步化?
  • 11. 为什么要异步化?更少的线程,更好的IO性能。 对计算操作和 I/O 处理进行重叠处理的能力利用了处理速度与 I/O 速度之间的差异。 当一个或多个 I/O 请求挂起时,CPU 可以执行其他任务; 在发起其他 I/O 的同时对已经完成的 I/O 进行操作。 但编写代码也变得复杂了 Callback Hell
  • 12. Callback Hellvar fs = import('fs'); // Bring in the filesystem library fs.mkdir('test', function (err) { var f = fs.open('test/newfile', 'w', function (err) { fs.close(f, function (err) { fs.unlink('test/newfile', function (err) { fs.rmdir('test', function (err) { console.log('Finally finished!!'); }); } }); }); });
  • 13. Callback Hellvar fs = import('fs'); // Bring in the filesystem library fs.mkdir('test', function (err) { var f = fs.open('test/newfile', 'w', function (err) { fs.close(f, function (err) { fs.unlink('test/newfile', function (err) { fs.rmdir('test', function (err) { console.log('Finally finished!!'); }); } }); }); });
  • 14. Python 的异步网络库:twistedTwisted is an event-driven networking engine for Python. from twisted.internet import reactor from twisted.web.client import getPage from twisted.python.util import println getPage(sys.argv[1]).addCallbacks( callback=lambda value:(println(value),reactor.stop()), errback=lambda error:(println("an error occurred", error),reactor.stop())) reactor.run()
  • 15. greenlet 与 geventThe greenlet package is a spin-off of Stackless, a version of CPython that supports micro-threads called “tasklets”. Tasklets run pseudo-concurrently and are synchronized with data exchanges on “channels”.gevent is a coroutine-based Python networking library that uses greenlet to provide a high-level synchronous API on top of the libev event loop.greenletgevent
  • 16. greenlet 与 gevent>>> import gevent >>> from gevent import socket >>> urls = ['www.google.com', 'www.example.com', 'www.python.org'] >>> jobs = [gevent.spawn(socket.gethostbyname, url) for url in urls] >>> gevent.joinall(jobs, timeout=2) >>> [job.value for job in jobs] ['74.125.79.106', '208.77.188.166', '82.94.164.162']
  • 17. greenlet 与 geventdef echo(socket, address): print('New connection from %s:%s' % address) socket.sendall('Welcome to the echo server! Type quit to exit.\r\n') fileobj = socket.makefile() while True: line = fileobj.readline() ... fileobj.write(line) fileobj.flush() print("echoed %r" % line)
  • 18. greenlet 与 gevent同步代码,异步性能 C10K,轻松搞掂 跟 Callback 说再见 monkey patch 让遗留代码无遗憾 gevent/examples/concurrent_download.py 大量用户:gunicorn
  • 19. pysequoiadb 的问题依赖 Cpp Client,从其之上封装Python接口,内部实现全部是 C++ 代码。 与 gevent 兼容有问题,因为 IO 完全是 Cpp Client 自行管理的。 与PyPy 兼容有问题,因为PyPy调用C扩展兼容性还比较差。 某些细节并不那么 Pythonic。
  • 20. 借鉴MySQL Connector/PythonMySQL driver written in Python which does not depend on MySQL C client libraries and implements the DB API v2.0 specification (PEP-249). https://github.com/mysql/mysql-connector-python 兼容 gevent.db = mysql.connector.Connect(**config) cursor = db.cursor() cursor.execute("SHOW ENGINES”) rows = cursor.fetchall() for row in rows: print(repr(row))
  • 21. pysequoiadb:源码分析分析 cpp driver 代码 宏定义数据类型,确保不同平台(32/64bits)的一致性 大量使用 pImpl 惯用法,对类的接口和实现解耦合。 函数统一样式 数据包头定义在msg.h,每次先行数据包头,定长,后续序列化的bson对象,变长。 INT32 func(...) { INT32 rc = SDB_OK ; … done : return rc ; error : … goto done ; }
  • 22. pysequoiadb:源码分析struct _MsgHeader { SINT32 messageLength ; // total message size, including this SINT32 opCode ; // operation code UINT32 TID ; // client thead id MsgRouteID routeID ; // route id 8 bytes UINT64 requestID ; // identifier for this message } ; typedef struct _MsgHeader MsgHeader ;
  • 23. pysequoiadb:源码分析struct _MsgOpInsert { MsgHeader header ; // message header INT32 version ; // the collection catalog version SINT16 w ; // need w nodes to operator succeed SINT16 padding; // padding SINT32 flags ; // insert flag SINT32 nameLength ; // length of collection name CHAR name[1] ; // name of the object } ; typedef struct _MsgOpInsert MsgOpInsert ;
  • 24. pysequoiadb:源码分析SequoiaDB Connection 实现:sdb 通信: 建立 TCP 连接 请求数据库服务器的系统信息:_requestSysInfo,大小端等协商 账号密码验证 开始业务通信 重点文件: client.hpp/clientcpp.cpp/clientImpl.hpp/client.c/common.h/common.cpp/network.h/network.cpp/msg.h
  • 25. pysequoiadb:异步化过程在原有 pysequoiadb 的基础上修改 去除扩展模块 sdb 建立 Python package sdb,使用 Python 实现 SequoiaDB 的 Client/Server 协议。 Client/Server 协议 TCP-based,支持 SSL/TLS 建立连接后验证账号密码 开始通信,一问一答,类似 HTTP 直接使用标准库的 socket 模块,以利 gevent
  • 26. pysequoiadb:异步化结果目前仅实现少量业务协议 get_collection_space get_collection insert/query 对查询结果实现迭代器协议 class DLLEXPORT _sdbCursor { private : ... public : ... virtual INT32 next ( bson::BSONObj &obj ) = 0 ; virtual INT32 current ( bson::BSONObj &obj ) = 0 ; virtual INT32 close () = 0 ; } ;
  • 27. pysequoiadb:异步化结果cr = cl.query(condition=cond) while True: try: record = cr.next() except SDBEndOfCursor : break except SDBBaseError: raise pysequoiadb._print(record)cr = cl.query(condition=cnd) for record in cr: pysequoiadb._print(record) SDBEndOfCursor => StopIterationbeforeafter
  • 28. 后续的工作完善功能 借鉴umysql 抽象传输层,当使用gevent时,直接使用标准库的socket模块,否则使用系统的socket函数 使用 cython 序列化、反序列化代码 性能:mysql-connector-python < MySQLdb < umysql Python 3.x 兼容 PyPy 兼容 性能优化
  • 29. 致谢谢谢大家!欢迎交流! mail@laiyonghao.com 诚招Pythoneer。