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
sujin190
V2EX  ›  Python

分享一下完全不依赖 asyncio 也支持异步语法的库

  •  
  •   sujin190 ·
    snower · 2021-09-07 10:15:43 +08:00 · 3961 次点击
    这是一个创建于 955 天前的主题,其中的信息可能已经有所发展或是发生改变。

    https://github.com/snower/sevent

    异步语法的支持完全不依赖 asyncio,当然并没有说可以替代 asyncio 或者更好啥的,只是一种实现,如果有对异步 io 或者 python 异步语法实现感兴趣的可以一看吧。

    只要是用于代理流量转发这样的场景,所以接口毕竟简单,当然支持范围也就没 asyncio 那么广了,从 echo 测试来看,性能还是要好于 asyncio 一些的,helpers 中也简单实现了几个工具。

    HTTP 请求测试

    import sevent
    
    async def http_test():
        s = sevent.tcp.Socket()
        await s.connectof(('www.baidu.com', 80))
        await s.send(b'GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: Close\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\n')
    
        data = b''
        while True:
            try:
                data += (await s.recv()).read()
            except sevent.tcp.SocketClosed:
                break
        print(data.decode("utf-8"))
        await s.closeof()
    
    sevent.run( http_test)
    

    TCP 端口转发

    import sys
    import sevent
    
    async def tcp_port_forward_server():
        server = sevent.tcp.Server()
        server.listen(("0.0.0.0", int(sys.argv[1])))
    
        while True:
            conn = await server.accept()
            pconn = sevent.tcp.Socket()
            pconn.connect((sys.argv[2], int(sys.argv[3])))
            conn.link(pconn)
    
    sevent.run(tcp_port_forward_server)
    
    59 条回复    2021-10-12 15:29:44 +08:00
    abersheeran
        1
    abersheeran  
       2021-09-07 10:26:05 +08:00
    看到了 greenlet,我之前就有个想法,用 greenlet 把同步函数包装起来变成一个 awaitable 的对象,但是一直没空去做。你可以试试看做一下。
    sujin190
        2
    sujin190  
    OP
       2021-09-07 10:38:51 +08:00
    @abersheeran #1 这个很简单,很早就搞过了

    https://github.com/snower/TorMySQL
    封装的 pymysql 可在 asyncio 下用

    https://github.com/mongodb/motor
    封装的 pymongo
    youngce
        3
    youngce  
       2021-09-07 11:02:59 +08:00
    Twisted 的出现比 asyncio 还早,本质就是函数回调+事件循环。后来 Twisted 一部分核心贡献者转到 asyncio 去了,毕竟 asyncio 是未来
    abersheeran
        4
    abersheeran  
       2021-09-07 11:08:14 +08:00
    motor 用的线程池。你这个 TorMySQL 写的一言难尽……不过 https://github.com/snower/TorMySQL/blob/ad583aadc2844c4b4e32e948b1f3252582832022/tormysql/util.py 这个挺符合我之前的设想。测试结果怎么样?比 asyncio.to_thread 快吗?
    sujin190
        5
    sujin190  
    OP
       2021-09-07 11:11:40 +08:00
    @youngce #3 你说的对,但是 Twisted 要能用 async 和 await 语法底层 ioloop 必须是 asyncio,我分享这个并没有说比 asyncio 更好,只是分享下对不使用 asyncio 的情况下如何使用 async 和 await 语法,感兴趣的话可以看看一看,毕竟 python 的 async 和 await 语法可是在解释器层和 asyncio 耦合在一起的,异步 io 相关的实现就更多了,也不复杂

    而且吧其实 asyncio 为了使用更广,接口实现太复杂了,想简单搞个小工具啥的太麻烦了
    Kilerd
        6
    Kilerd  
       2021-09-07 11:12:16 +08:00
    sujin190
        7
    sujin190  
    OP
       2021-09-07 11:16:01 +08:00
    @abersheeran #4 motor 并没有用线程池,你没仔细看吧,greenlet 的切换肯定比线程切换快,款且还有同步锁的问题,实现要更复杂,性能肯定是 greenlet 更好了

    TorMySQL 我们自己用了很久了,并没有啥问题啊,只是为了让在 python2 的 tornado 上也能正常运行,并不是全都是 python3 语法的
    abersheeran
        8
    abersheeran  
       2021-09-07 11:22:11 +08:00
    @sujin190 Motor 的线程池代码: https://github.com/mongodb/motor/blob/master/motor/frameworks/asyncio/__init__.py#L73

    我还想问个 Greenlet 的问题:一个带系统中断操作的函数丢到 greenlet 里,它会自动在中断时 switch 到其他 greenlet 吗?
    sujin190
        9
    sujin190  
    OP
       2021-09-07 11:43:24 +08:00
    @abersheeran #8 好吧,我错了,没想到 0.5 版本之后这货就改成线程池了,只是不知道为啥这样改

    应该不会吧,greenlet 切换的是栈帧,系统中断打断的是底层线程调用栈,python 的栈帧似乎是分配在堆上的,线程切换并不会影响 python 栈帧,自然也就不会导致 greenlet 切换了吧
    abersheeran
        10
    abersheeran  
       2021-09-07 11:46:16 +08:00
    @sujin190 中断不会导致 greenlet 切换?那你这个封装甚至不如 asyncio.to_thread 快啊。我明白 motor 怎么换成线程池了。看来我还得看看 gevent 去
    sujin190
        11
    sujin190  
    OP
       2021-09-07 11:47:32 +08:00
    @abersheeran #8 如果是 signal 信号打断,signal 信号处理器结束的时候会恢复原来的栈帧,所以这种估计也不会导致 greenlet 切换吧
    sujin190
        12
    sujin190  
    OP
       2021-09-07 11:52:15 +08:00
    @abersheeran #10 你说的是系统 io 会不会导致 greenlet 切换吧?这个系统 io 处理都是运行在主 greenlet 里的,是你需要写数据读数据的时候,你主动切换的主 greenlet 去,不像线程池一样当系统 io 产生的时候由操作系统调度线程切换
    ysc3839
        13
    ysc3839  
       2021-09-07 12:06:50 +08:00 via Android
    借楼问一下,Python 里 async function 能否当成回调函数使用?
    比如说已经有一个队列了,在 await 某个对象时得到一个回调函数加入队列,后续执行这个函数恢复 async function 执行。
    abersheeran
        14
    abersheeran  
       2021-09-07 12:09:25 +08:00
    @sujin190 那只要一个 SQL 查询卡在那儿,后续所有 SQL 查询不都被阻塞了吗?
    sujin190
        15
    sujin190  
    OP
       2021-09-07 12:16:44 +08:00
    @abersheeran #14 不啊,主 greenlet 使用 epoll 可以同时处理很多个连接的,连接 io 事件产生的时候主 greenlet 处理完事件后会依次切换到对应的子 greenlet 做业务处理,子 greenlet 又可以产生更多的 io 操作主动切回主 greenlet 处理了啊,这样不就异步并行可以同时处理无数个 sql 查询请求了
    abersheeran
        16
    abersheeran  
       2021-09-07 12:30:40 +08:00
    @sujin190 😦好的我去看看
    sujin190
        17
    sujin190  
    OP
       2021-09-07 12:35:06 +08:00 via Android
    @ysc3839 应该是可以的吧,只不过普通队列并不能让这个函数运行起来,你需要从队列取出来后扔到 asyncio 中去运行起来
    ysc3839
        18
    ysc3839  
       2021-09-07 13:05:15 +08:00 via Android
    @sujin190 想问不用 asyncio 的情况能否实现。
    sujin190
        19
    sujin190  
    OP
       2021-09-07 13:40:55 +08:00
    @ysc3839 #18 也是可以的,只是不用 asyncio 这样的异步 io,那么异步方法似乎没啥用了吧,没啥用的必要了
    tmac010sjh
        20
    tmac010sjh  
       2021-09-07 15:19:11 +08:00
    苏神依旧这么牛逼,还在 mg 么?
    wamson
        21
    wamson  
       2021-09-07 15:25:50 +08:00
    正好对这个有兴趣,话说这个有什么比较有名的方案吗?比如底层是 c++,io_loop 是 lib_event,通过 pybind11 来驱动 python 脚本层级,那么 python 层该怎么使用 async 语法糖呢。眼馋 python 的 async 语法糖好久了。
    haoliang
        22
    haoliang  
       2021-09-07 15:56:43 +08:00
    没有文档,粗略爬过代码;我的理解中,`sevent.go(callback)` 对应 `asyncio.create_task`, 在 sevent 中如何等待这个 callback 的完成呢? callback 运行过程中抛错,该如何处理?
    sujin190
        23
    sujin190  
    OP
       2021-09-07 16:08:23 +08:00
    @wamson #21 async 语法糖本质就是一个迭代器,用迭代器的方法驱动运行就好了,搞 c++的话,去瞅一下 asyncio 的源码很快就知道了吧,而且你可以把 asyncio 的那部分源码搬过来放到你的 c++代码里啊
    sujin190
        24
    sujin190  
    OP
       2021-09-07 16:10:12 +08:00
    @haoliang #22 只是一看,没啥动力写文档啊,callback 出错只是单纯用 logging 输出调用栈信息了,其实一般来说如果你关注 callback 出的错,那么你应该在一进入 callback 时就自己加 try 就是了呀
    sujin190
        25
    sujin190  
    OP
       2021-09-07 16:10:23 +08:00
    @tmac010sjh #20 跑人了
    ysc3839
        26
    ysc3839  
       2021-09-07 17:03:00 +08:00 via Android
    @sujin190 有用的,主要就是为了避免回调地狱。
    sujin190
        27
    sujin190  
    OP
       2021-09-07 17:16:37 +08:00
    @ysc3839 #26 没有底层的 asyncio 的 ioloop,协程之间就缺少切换点了,难道你想用线程池配合 await 使用?可是多线程模式下 Lock 、Event 、Semaphore 就是用来解决你遇到的回调问题,并不需要多次一举强行又别扭的使用 await 啊

    await 本质就是个状态管理器,他也是注册 callback,只不过这个 callback 用来触发协程切换了,但是线程调度由操作系统来管理,所以这不又回到 Lock 、Event 、Semaphore 了
    ysc3839
        28
    ysc3839  
       2021-09-07 17:32:29 +08:00 via Android
    @sujin190 我就是为了用 async function 代替回调函数,并不是要用什么线程池,整个程序是单线程的,也不希望搞成多线程的模式。
    这也是我不能理解 Python 协程的地方,别的语言如 C++, JavaScript 的协程就是回调函数的语法糖,但 Python 好像要有个调度器才能跑。
    sujin190
        29
    sujin190  
    OP
       2021-09-07 17:44:16 +08:00
    @ysc3839 #28 JavaScript 语言直接自带调度器,你都不能不要,不知道你用的 C++的库是啥,其实你这个想法才挺奇怪,async await 语法本来就是 A 等待 B 的一个结果,本身就代表有两个独立调用栈,没用调度器,直接一个单一线程如何能驱动两个独立调用栈运行呢
    ysc3839
        30
    ysc3839  
       2021-09-07 17:48:31 +08:00 via Android
    @sujin190 但是 JavaScript 的调度器跟它的协程关系并不大,调度器执行的是回调函数,并不跟协程深度绑定。
    C++ 说的是 C++20 内置的协程。
    没有调度器,也能直接通过回调函数来驱动协程执行。比如 C++ 的协程在 await 的时候,被 await 的对象能拿到协程的“回调函数”,后续执行这个回调函数就是恢复协程执行了。
    sujin190
        31
    sujin190  
    OP
       2021-09-07 18:09:25 +08:00
    @ysc3839 #30 我知道你说的是啥,其实就是 future 对象吧,需要传 callback 的地方你可以构造一个 future 对象传过去,future 的 set_result 就是你说的 c++中的那个能拿到协程的“回调函数”了,Javascript 的异步函数返回时一个 Promise 对象,换言之,你需要传 callback 也可以改成传 Promise 对象,Javascript 不需要显示把协程放在调度器中运行时因为自动在全局包了一个调度器函数,其实底层和 python 是一样的
    sujin190
        32
    sujin190  
    OP
       2021-09-07 18:20:21 +08:00
    @ysc3839 #30 关于 C++协程据我之前看过几个库的实现来看,c++作为底层语言,自身就能够通过操作内存结构和长 jmp 指令改变调用栈,所以基于此是可以无需调度器就可以完成协程切换的,但是无论 Javascript 、python 、ruby 、php 这样的语言可是都做不到手动修改栈帧这样的操作,这点和 c++这样的更底层的语言还是很大不同的,Javascript 看起来有点像,也只是解释器会隐式加很多东西
    ysc3839
        33
    ysc3839  
       2021-09-07 19:01:32 +08:00
    我说的“协程的回调函数”不是 future 或者 JS 异步函数返回的 Promise,而是用于恢复协程执行的东西,类似 Promise 里面的 resolve 函数。
    我说的 C++ 协程不是第三方实现的有栈协程,而是语言本身实现的无栈协程,这种协程不依赖具体硬件平台,而是依赖编译器实现。
    可以看一下用 Babel 转换过的协程代码,或者反编译看看 C++ 协程编译后的代码,其实就是在 await 的地方拆分开。

    用伪代码举个例子:
    原始代码:
    ```
    async function test() {
    func_a();
    await func_b();
    await func_c();
    func_d();
    }

    test();
    ```

    处理后的代码:
    ```
    function test(state) {
    switch(state.step) {
    case 0:
    state.step = 1;
    func_a();
    func_b(test, state);
    break;
    case 1:
    state.step = 2;
    func_c(test, state);
    break;
    case 2:
    func_d();
    break;
    }
    }

    let state = {step: 0};
    test(state);
    ```

    此时 func_b 或者 func_c 要恢复协程执行的话,只需要调用 test(state); 即可,这样可以轻松对接那些使用回调函数的库,避免回调地狱。

    印象中 Python 也是类似这种模式的协程,按理说可以直接当回调函数用,但是实际上却捆绑了调度器,反而丧失了灵活性。
    就像前面 @wamson 所说的,很多情况下是已经有了一个现成的使用回调函数的事件循环,想要使用 async await 的语法简化代码,这种情况下 C++ 或 JS 的协程很容易就能对接,而 Python 就做不到了。
    0x0208v0
        34
    0x0208v0  
       2021-09-07 19:31:12 +08:00
    牛逼啊,好久没在 python 区看见这种高端技术贴了,求多发点
    sujin190
        35
    sujin190  
    OP
       2021-09-07 19:44:42 +08:00
    @ysc3839 #33 你这是把 c++和 JavaScript es5 的实现想完全搬到 python 上来啊,但事实上这两都是没有更好方法的时候不得已才这样用的,python 的实现更简单,用 future 对象把那些用 callback 的库包装一下就行了

    ```
    def func(callback):
    callback()

    def func_await():
    futuer = Futuer()
    def on_finish():
    futuer.set_result()
    a(on_finish)
    return futuer

    async def test():
    func1()
    await func_await()
    await func_await2()
    func2()
    ```

    大概就是这样样子就好了
    ysc3839
        36
    ysc3839  
       2021-09-08 10:27:06 +08:00
    @sujin190 自己写了段测试代码,但是运行时出错了
    https://gist.github.com/ysc3839/4445c6c2bc1e0ce02790a52fed0c8b38
    错误信息是:
    test.py:20: RuntimeWarning: coroutine 'test' was never awaited
    test()
    RuntimeWarning: Enable tracemalloc to get the object allocation traceback
    test.py:21: RuntimeWarning: coroutine 'test' was never awaited
    test()
    RuntimeWarning: Enable tracemalloc to get the object allocation traceback
    ysc3839
        37
    ysc3839  
       2021-09-08 10:48:41 +08:00
    @ysc3839 顺便写了个用 C++ 协程实现的 https://godbolt.org/z/sGnzTnvjr
    sujin190
        38
    sujin190  
    OP
       2021-09-08 10:54:52 +08:00
    @ysc3839 #36 https://gist.github.com/snower/c4f1d4d55281e9456daf1a66831db821

    async 函数调用后其实返回了一个 coroutine 对象,python 默认不会启动调度器,所以默认并不会运行这个 coroutine 对象,JavaScript 可以,那是因为 JavaScript 默认就启动了调度器,全局定义为被执行的 Promise 都会默认被直接执行,这两个没啥区别

    当然 python async 函数调用返回 coroutine 使用 send 函数就能手动运行起来了,但是注意 asyncio 的 Future 调用 set_result 触发 await 返回其实是又被放到当前 asyncio loop 中去执行了,所以你还是要用 asyncio 创建 loop 来运行

    我看 c++ 20 的协程也是需要调度器的吧
    sujin190
        39
    sujin190  
    OP
       2021-09-08 11:26:46 +08:00
    @ysc3839 #37 静态语言改编译器,改内存布局能,直接长 jmp 就是牛逼,这是在编译时直接把 test 函数拆成几个了啊
    ysc3839
        40
    ysc3839  
       2021-09-08 13:00:19 +08:00 via Android
    @sujin190 C++20 的协程不需要调度器,你可以看看我前面的代码。
    frostming
        41
    frostming  
       2021-09-08 14:38:19 +08:00
    > 毕竟 python 的 async 和 await 语法可是在解释器层和 asyncio 耦合在一起的,异步 io 相关的实现就更多了,也不复杂

    没有耦合啊,只要重新实现一套事件循环和异步 IO,就可以直接代替 asyncio,例子有:

    - Trio: https://github.com/python-trio/trio by njs
    - Curio: https://github.com/dabeaz/curio by David Beazely
    sujin190
        42
    sujin190  
    OP
       2021-09-08 15:50:48 +08:00
    @frostming #41 这个的主要问题是,无论你用 async 方法 await 调用多少层,最终肯定会落到一个不是 async 或者 c 扩展的方法去,而这个方法是不能 await 的,如果你还需要等等 io 完成无法立刻得到结果,此时就需要有方法可以切换到其它协程去,而这个过程解释器层和 asyncio 耦合在一起了,还有 future 对象,set_result 后并没有直接触发 await,而是又走了一次 ioloop 循环,这个也直接写了就是走 asyncio
    sujin190
        43
    sujin190  
    OP
       2021-09-08 16:36:32 +08:00
    @frostming #41 我看了下 trio 的实现,原理是调度器每 coroutine 迭代一步都需要重新压入调度器队列才能进行下一步,如果需要等待 io 就别压入调度器队列,这样调度器就可以切换到其它协程了,好吧,这也是一个方法,不过说起来 tornado 在 python2 上的实现其实也是这样的

    但似乎 asyncio 的做法是直接切换栈帧的,对 coroutine 的每步迭代也是 c 直接写的,这两者效率差距有点大啊,asyncio 的 future 对象也是直接 c 写的
    O5oz6z3
        44
    O5oz6z3  
       2021-09-08 21:30:29 +08:00
    @ysc3839 #36 虽然我不是很懂 asyncio 和 C++,也不知道你的需求要怎么实现,但我猜你的 python 代码出错是因为你用了 asyncio.Future 。`Future(loop=None)` 要么默认绑定 asyncio 的事件循环,要么你传进去一个自定义 loop,这个 loop 的接口要兼容 asyncio 的 eventloop,大概像是 #41 楼说的那样?

    而且 `async def test():...; test()` 本来也无法直接运行,大概需要这样运行 `asyncio.Task(test(), loop=None)`?

    以下是个人理解。如果你要借用 async/await 语法糖,就要实现一个兼容 async-function/coroutine 的事件循环,也就是 asyncio 的工作,相当于 js 自带的事件循环。

    虽然不知道要怎么实现 sync-callback 结合 async/await,个人理解就是 #19 楼那样,async 语法是用来等待 io 的,asyncio.sleep 就是模拟 io,所以也很好奇在没有 io 的情况下要怎么用 async 语法代替回调地狱。(顺便一提,上次在 /790207#90 的时候我的确说错了,py 协程可以是异步生成器,支持随意 suspend resume )
    caicaiwoshishui
        45
    caicaiwoshishui  
       2021-09-08 22:45:15 +08:00
    @O5oz6z3 歪个楼,想请教下大佬,在同步框架中比如 django,使用 asynico 时,这个异步协程是如何拿到主线程的事件循环的呀?
    O5oz6z3
        46
    O5oz6z3  
       2021-09-09 00:06:20 +08:00
    @caicaiwoshishui #45 问得好,这个问题,我也不会,有请真正的大佬指点……

    @ysc3839 #36 补充一下,虽然不靠谱,稍微改了改糊了一个实现:
    from asyncio import Future, Task, set_event_loop, AbstractEventLoop
    from collections import UserList

    class Loop(UserList, AbstractEventLoop):
    ...def call_soon(self, callback, *args, context=None):
    ......self.append(lambda: callback(*args))
    ...def get_debug(*_): pass
    ...__hash__ = lambda x:id(x)

    callback_list = Loop()
    set_event_loop(callback_list)
    ......
    Task(test())
    Task(test())
    ......
    sujin190
        47
    sujin190  
    OP
       2021-09-09 10:49:10 +08:00
    @sujin190 #43 好吧,纠正一下,asyncio 也是每次 coroutine 每次迭代之后如果没用遇到 io 操作啥的需要等待就重新加入调度器队列,否则就在 io 完成后再重新加入调度器队列,这样调度器就可以在多个协程中相互切换了,并没用直接切换栈帧,那么确实不算是完全耦合在一起,其实似乎还是直接切换栈帧来的效率更高吧
    sujin190
        48
    sujin190  
    OP
       2021-09-09 10:55:21 +08:00
    @caicaiwoshishui #45 asynico 的 ioloop 就是个 while True 死循环,所以如果你 django 运行在主线程,那么 asynico 就需要单独创建线程来运行了啊,而 asynico 的默认 get_event_loop 获取的是绑定到当前线程的,否则你就需要用个全局变量啥的保存这生成的 loop,这样就可以操作另外线程的 loop 了,再者 django 这种 web 程序又不是桌面 gui 程序,主线程哪里来的事件循环,asynico 似乎没有获取主线程事件循环的问题吧
    caicaiwoshishui
        49
    caicaiwoshishui  
       2021-09-09 23:48:50 +08:00 via iPhone
    @sujin190 感谢回答,不一定是拿到主事件循环,而是怎么在 django 的同步的主线程中切换到 asyncio 线程中?
    sujin190
        50
    sujin190  
    OP
       2021-09-10 10:22:41 +08:00   ❤️ 1
    @caicaiwoshishui #49 call_soon_threadsafe 就可以在另外一个线程给 asynico 添加一个 callback 了,django 需要等等 callback 结果那就是多线程编程 Lock 、Event 、Semaphore 的东西了,写了个 flask 简单示例可以看看

    https://gist.github.com/snower/b6d0288c60f4d40e544fb530a011ce62
    caicaiwoshishui
        51
    caicaiwoshishui  
       2021-09-10 12:59:20 +08:00 via iPhone
    @sujin190 感谢回答,清晰了很多
    abersheeran
        52
    abersheeran  
       2021-10-12 10:59:14 +08:00
    @sujin190 #15 我深入研究了一下,发现你说的这个不就是把 asyncio 重新实现了一遍吗?我以为切换过程是自动的,结果是手动的。那这个的意义在哪儿? greenlet 的效率比 generator 也高的有限吧。
    abersheeran
        53
    abersheeran  
       2021-10-12 11:00:58 +08:00
    async def async_call_method(func, *args, **kwargs):
    ioloop = asyncio.get_running_loop()
    future = ioloop.create_future()

    def finish():
    try:
    result = func(*args, **kwargs)
    if future._callbacks:
    ioloop.call_soon(future.set_result, result)
    else:
    future.set_result(result)
    except Exception as e:
    if future._callbacks:
    ioloop.call_soon(future.set_exception, e)
    else:
    future.set_exception(e)

    child_gr = greenlet.greenlet(finish)
    child_gr.switch()

    return await future


    我还试了你在 TorMySQL 里的函数,单纯用这个函数调用同步函数,比直接调用同步函数还慢一点。
    sujin190
        54
    sujin190  
    OP
       2021-10-12 11:31:06 +08:00
    @abersheeran #53 我又没说比 asyncio 更好以做参考,单纯想了解 asyncio 实现可以看看,觉得有场景方便就用,激动个啥,关于比直接调用慢不很正常么,多加了几步操作肯定要花时间的啊
    abersheeran
        55
    abersheeran  
       2021-10-12 14:07:00 +08:00
    @sujin190 不是,我的意思是 TorMySQL 你确定是一个能有效使用的 MySQL 异步驱动吗?如果这个比直接调用同步函数还慢,那 TorMySQL 岂不是仅挂了一个空壳?
    sujin190
        56
    sujin190  
    OP
       2021-10-12 14:11:50 +08:00
    @abersheeran #55 当然能用了,我们生产系统都跑好几年了,但是你这用异步函数和同步函数比是几个意思啊,TorMySQL 本来就是给 pymysql 套个壳让能在 asyncio 上使用异步 io,比同步直接请求慢本来就是正常的啊,你要比也应该和 aiomysql 比才对吧
    abersheeran
        57
    abersheeran  
       2021-10-12 14:51:56 +08:00
    @sujin190 啥?我上面说的是把那个函数包装一个同步 SQL 查询,然后丢进异步 Web 框架里测试的结果,还不如直接在异步框架里跑同步 SQL 查询快,如果用多线程包装的异步调用,那并发能达到四倍。

    TorMySQL 我没试,我只有 pg 环境,所以我才问你,你确定这么写能真正的把同步 SQL 查询包装成异步的以提高并发吗?
    abersheeran
        58
    abersheeran  
       2021-10-12 14:56:12 +08:00
    我查到了 https://github.com/snower/TorMySQL/blob/master/tormysql/platform/asyncio.py#L83

    原来内部还是异步的……只是用 greenlet 把 Coroutine 转成了 Greenlet 。
    sujin190
        59
    sujin190  
    OP
       2021-10-12 15:29:44 +08:00
    @abersheeran #57 python 的异步本来就不快,事实上甚至有可能协程调度消耗了太多 cpu,然后你会发现 rps 还不如同步的呢,但是异步有异步的好处吧,提高并发倒是可以,提高 rps 就不能了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3946 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 05:10 · PVG 13:10 · LAX 22:10 · JFK 01:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.