V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
ryanking8215
V2EX  ›  Python

月经贴, tornado + sqlalchemy

  •  
  •   ryanking8215 ·
    ryanking8215 · 2014-12-05 09:57:04 +08:00 · 15159 次点击
    这是一个创建于 3643 天前的主题,其中的信息可能已经有所发展或是发生改变。
    网上搜了下tornado+sqlalchemy,发现都是直接用的,那tornado的non-blocking特性不是用不到了吗?
    38 条回复    2016-02-17 20:12:44 +08:00
    tanywei
        1
    tanywei  
       2014-12-05 10:54:57 +08:00
    不知道什么是celery?
    binux
        2
    binux  
       2014-12-05 10:59:15 +08:00
    如果数据库block了,异步也没用,所有人都卡而已。你应该优化数据库
    davidli
        3
    davidli  
       2014-12-05 11:19:23 +08:00
    同1楼, 用celery.
    ryanking8215
        4
    ryanking8215  
    OP
       2014-12-05 12:50:23 +08:00
    感觉celery好重量级啊。
    能不能用tornado.cocurrent.run_on_executor()和coroutine连接起来呢?是不是用这种方法解决的?
    lianghui
        5
    lianghui  
       2014-12-05 14:07:33 +08:00
    不知道你要做什么玩意,如果你的负载有4k req/s, 就告诉一个方法如何用tornado 和数据库异步落地架构(当然不是搞个什么python mysql异步库版本之类的)
    ryanking8215
        6
    ryanking8215  
    OP
       2014-12-05 14:21:45 +08:00
    @lianghui 不做什么玩意,就是评估一下,如果用tornado做一个api服务,需要用到数据库,但是我对sql不熟,所以想用orm来解决数据库的问题,我想知道在这样情况下,orm是同步的,但是web framework是异步的,如何来协调。再比如我用asyncio呢?如何和sqlalchemy配合使用?

    "就告诉一个方法如何用tornado 和数据库异步落地架构"
    就是这个问题。
    zhouquanbest
        7
    zhouquanbest  
       2014-12-05 14:22:55 +08:00
    Celery是最优解

    当然 如果你能处理好session的问题(即不要让一个进程里的所有Request都是用一个session) 可以用Douban的黑科技Greenify做到Mysqldb异步
    lianghui
        8
    lianghui  
       2014-12-05 15:03:03 +08:00
    @ryanking8215 你把自己绑死在sqlalchemy了。

    @zhouquanbest celery只是一个解,但绝对不是最优的
    zhouquanbest
        9
    zhouquanbest  
       2014-12-05 15:33:00 +08:00
    @lianghui
    celery毕竟方便嘛
    求推荐更优雅的方式 tornado+sqlalchemy合体确实恶心
    zenliver
        10
    zenliver  
       2014-12-05 15:46:44 +08:00
    用celery只是绕过了这个问题, 而不是解决了, 找找有没有人实现过, 没有, 自己轮一个吧, 用tornado ioloop 以及iostream去处理io,,,good luck
    ryanking8215
        11
    ryanking8215  
    OP
       2014-12-05 15:49:15 +08:00
    @zhouquanbest ORM一般都有连接池,难道连接池是分进程的?就算是非阻塞架构的,例如nodejs 上的sequelizejs,也有连接池,一个sql请求由于某些问题会阻塞(不是阻塞式api),其他请求会用连接池里的继续用。和进程没有关系啊。就算mysql的driver是异步的,但ORM的api不是啊,例如不能yield的。


    @lianghui ORM不是sqlalchemy最好了吗?其他的ORM,也是同步类型的,和sqlalchemy没区别啊,难道有异步的ORM吗?


    起因是这次有个项目使用nodejs+sequelize来做的,由于nodejs的并发模型一致性,其他的组件通过promise或者callback都能集成起来,或者本来就是自然的。
    但是python就不是,并发模型有可多线程,可多进程,可twisted的callback或者deferred,可tornado,gevent的coroutine,现在asyncio的coroutine又和tornado和gevent是不同的,在这种多样选择下,各种并发模型不同的库如何配合起来呢?这个是不是pythoner的痛点之一呢?
    当然nodejs是好坑的,包括很多第三方的库,本着多学一点的原则,所以想和大家探讨探讨。
    ryanking8215
        12
    ryanking8215  
    OP
       2014-12-05 15:51:06 +08:00
    @binux 数据库block,肯定是数据库的设计有问题,但是在ORM在请求时是同步socket,由于网络问题也会block,那tornado的异步就没有意义了。
    zenliver
        13
    zenliver  
       2014-12-05 15:52:58 +08:00
    https://github.com/mayflaver/AsyncTorndb 这是我轮的一个 不过还是有些问题, 仅供参考
    binux
        14
    binux  
       2014-12-05 15:56:30 +08:00
    @ryanking8215 只能说 tornado 不认为连 mysql 都要堵是个问题。
    lianghui
        15
    lianghui  
       2014-12-05 15:59:07 +08:00
    @zhouquanbest


    先说最简单的方法, 使用OpenResty lua 编写数据库落地接口

    tornado 优化一个能够长连接的http client 或者直接使用自带的异步client 发送请求 如

    req: /query?op=user.find_by_user&uid=1222

    res : {"uid" : 1222, "name", ...... }

    req /execute?op=user.save&data=<User.as_json>

    req: 200 0k

    这类架构 数据库使用openresty 作为dbproxy

    上面的例子中使用的http协议,数据可是用json或者google protobuf(这个比较高效)

    效率在 3k-4k req/s


    ### 使用长连接


    如果绝对http协议不够高效,那么使用tcp自行封装一个简单长连接的协议。 不过这样dbproxy serverlength需要使用c++之类的做个异步server了。协议可以选高效的二进制协议 protobuf只是一个选择。



    req: {"op": "user.find_by_uid", "data": {"uid": 12}}\r\n

    使用这类行协议

    回复如下:

    7200 ok
    23{"uid" : 1222, "name", ...... }

    回复消息定义首4位标识消息状态长度 上面是状态200 msg为 ok 之间是空格分隔
    然后紧接着32位标识数据长度,后面自定义解析的raw数据,觉得要省事可以使用protobuf。

    这类优化后到 5-6k req/s问题不大。但这时,妈蛋去多关心你的数据库压力,然后去优化吧。
    lianghui
        16
    lianghui  
       2014-12-05 16:07:17 +08:00
    @ryanking8215 抱歉呀,sqlalchemy没用过,做游戏的,不关心这类的,更喜欢直接使用data-mapper模式。

    http://martinfowler.com/eaaCatalog/dataMapper.html

    推荐看这位作者企业架构书 http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420
    ryanking8215
        17
    ryanking8215  
    OP
       2014-12-05 16:48:57 +08:00
    @lianghui RPC?就好比tornado收到request后,和后端的db_proxy通信,db_proxy可以用啥哈nodejs+sequelize也可以啊,那就没有这个问题了,呵呵。大概你们做游戏的都是分层的,如果只是撸个blog,那就太厚了,这儿问题还是存在的。
    openroc
        18
    openroc  
       2014-12-05 17:02:40 +08:00
    用nodejs吧,异步到你爽YY~~
    qbeenslee
        19
    qbeenslee  
       2014-12-05 17:12:25 +08:00
    可以开ThreadPool来异步处理
    ryanking8215
        20
    ryanking8215  
    OP
       2014-12-05 17:21:49 +08:00
    @qbeenslee 貌似只能这样了
    qbeenslee
        21
    qbeenslee  
       2014-12-05 17:25:31 +08:00
    @ryanking8215 楼主在tornado上可以多多交流, 我最近也想用tornado做毕业设计...
    halfelf
        22
    halfelf  
       2014-12-05 19:45:28 +08:00
    有没有人用过gevent + sqla + pymsql
    sujin190
        23
    sujin190  
       2014-12-05 21:24:20 +08:00
    sujin190
        24
    sujin190  
       2014-12-05 21:25:56 +08:00
    使用greenlet实现异步,和pymongo的motor的实现机理一样,现在用在自己项目中
    ponyfk
        25
    ponyfk  
       2014-12-05 22:14:44 +08:00
    一定要用orm的话, 我推荐 ponyorm(配合ultramysql) + gevent + flask 完美解决异步
    wingyiu
        26
    wingyiu  
       2014-12-05 22:21:15 +08:00
    @zenliver
    @sujin190

    正好在找db async的解决方案
    sujin190
        27
    sujin190  
       2014-12-05 22:28:43 +08:00
    @zenliver mysql协议是二进制解析的,有非常多的自己读取,每次调用iostream的read其实是效率非常低的
    zenliver
        28
    zenliver  
       2014-12-07 15:06:23 +08:00
    @sujin190 的确如此
    zhicheng
        29
    zhicheng  
       2014-12-07 21:03:06 +08:00   ❤️ 2
    这个问题不用太担心,
    一,一般 WebServer 和 DB 都是局域网直连,网络性能损失很小,在要求不是极高的情况下,能够满足需求。
    二,因为 RDBMS 的特殊性,目前一个连接并不能同时执行多个请求。所以如果使用非阻塞IO,不仅会导致前端非常复杂,并且依然会卡在一个执行时间超长的 SQL 上,除非针对每次请求创建一个 DB 连接,这样一定程度上增加了 latency 和 DB 的负荷。
    三,如果用多核 CPU (现在几乎全部都是)的服务器,可以同时创建多个 Web Server 的进程,能够缓解DB阻塞的问题。
    四,不建议自己实现 DB Driver ,除非你知道你在做什么。
    五,自己实现协议和DB的,都是走火入魔的标志。
    ryanking8215
        30
    ryanking8215  
    OP
       2014-12-07 21:31:31 +08:00
    @zhicheng
    一. 提出tornado,就是想探讨一下非阻塞模型下如何使用同步ORM的问题,不是说一定不能直接使用。只是如果后端没有压榨出non-blocking的效率,于心不忍。

    二. 不是有连接池吗?

    三. 同意,单线程的无法使用多核,需要多进程加入。目前看来,除了自带调度器的golang,erlang啥的,其他的语言要网络io的高并发,就是多进程+单线程的事件循环了。

    四. 看个人角度了,对有些人来说是对的,对某些人来说这句话是错的。

    五. 同上
    zhicheng
        31
    zhicheng  
       2014-12-07 23:26:13 +08:00
    一,如果实在想解决这个问题,中间加一层 API ,负责处理 Web Server 和 DB 的连接。
    二,连接池确实能解决一定问题。
    三,这跟是否自带调度器无关,而是因为 Python 有 GIL 。我现在在 C 部分的代码,即使多线程也是每个线程使用一个 event loop 。跟进程模型几乎一样的,只是节省了共享内存的部分。
    四,如果你写过类似的,就会知道,像这样的产品,从开始到可用,最少需要一年的时间。
    五,同上。
    六,优秀的工程师必备条件之一就是不要高估自己的能力。
    zhouquanbest
        32
    zhouquanbest  
       2014-12-08 00:24:32 +08:00
    @lianghui
    这确实是个解法 和celery一回事 就是让db操作和tornado分离
    好像百度是这么干的吧

    不过小项目这样做也挺累
    Tornado麻烦在于 小项目sql也堵死你 只能提前优化
    异步真是不能省心
    toooddchen
        33
    toooddchen  
       2014-12-08 00:42:27 +08:00
    tornado和sqlalchemy的使用, 对你提到的非阻塞特性没有什么影响.
    db访问层面阻塞了, 不会影响tornado对其它请求的处理.

    影响单线程ioloop性能的, 是cpu密集的操作, db访问不属于这一类.
    MasterYoda
        34
    MasterYoda  
       2014-12-08 09:57:04 +08:00
    @toooddchen
    你起一个tornado进程,然后来一个复杂的sql查询请求,阻塞后再来别的请求看看影响它对其它请求的 处理不。
    ryanking8215
        35
    ryanking8215  
    OP
       2014-12-08 10:06:25 +08:00
    @MasterYoda 同意,其实time.sleep()就可以了

    @toooddchen 为什么cpu密集操作会影响eventloop性能呢?因为event loop无法及时“归位”,同理,同步的ORM会阻塞当前执行的协程。影响event loop性能的不单单是cpu密集操作,比如time.sleep()。这是那啥充分必要条件,好久没整,整不清楚了。
    beef9999
        36
    beef9999  
       2014-12-17 11:32:27 +08:00
    没错,tornado的特性之一是异步非阻塞,但是这不是根本,根本上来说它是一个使用了epoll的web server,能保证大量网络连接送到python代码的get或者Post的这个过程是高效的,只要你不阻塞get 或者post,剩下的你想干什么都行。不阻塞的方法很多,你可以用异步调用,这样语法就比较难看,即使是用了生成器,还是很反直觉,另外在python 2还有raise Return(response)这种诡异的东西。当然这些都不是问题,最主要的问题是很多ORM数据库不支持异步,所以最佳实践还应该是去开线程,使用线程池。tornado能够支持线程,并且经过仔细编写的代码也是能够达到线程安全的。最后有人说用gevent,理论可以,这样你就能写出不阻塞的同步代码,也支持数据库了。但是怎么集成tornado是个问题,目前看到的似乎没有成熟的项目
    Pegasus
        37
    Pegasus  
       2016-01-19 15:49:50 +08:00   ❤️ 1
    http://techspot.zzzeek.org/2015/02/15/asynchronous-python-and-databases/
    这个是 SQLArchemy 作者的文章,可以看看为什么不推荐使用异步的 orm
    dantangfan
        38
    dantangfan  
       2016-02-17 20:12:44 +08:00
    @Pegasus 只是不推荐 python 实现的异步吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1006 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 21:45 · PVG 05:45 · LAX 13:45 · JFK 16:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.