月经贴, tornado + sqlalchemy - V2EX
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 15635 次点击
    这是一个创建于 3962 天前的主题,其中的信息可能已经有所发展或是发生改变。
    网上搜了下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     Solana     1252 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 17:17 PVG 01:17 LAX 10:17 JFK 13:17
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86