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

哎, socket.io 对于 python 容器 多 worker 模式 来说好像是个死局

  •  
  •   awanabe · 2016-07-22 12:32:57 +08:00 · 6513 次点击
    这是一个创建于 3053 天前的主题,其中的信息可能已经有所发展或是发生改变。
    不管是 gunicorn 或者 uwsgi , 只要开启了多 worker , 就是多进程。
    socket.io polling 的时候就会大部分的找不到对应 connection 。

    所以官方推荐用 gunicorn 的原因是,用单 worker 可以发挥 协程 的最大作用。

    而 uwsgi 因为对协程支持不好,转而用 多 worker 突破 python 全局锁提高并发的方式, 对于 socket.io 来说就是死局。
    34 条回复    2016-07-28 09:32:22 +08:00
    playniuniu
        1
    playniuniu  
       2016-07-22 12:49:28 +08:00
    试试这个

    https://github.com/miguelgrinberg/python-socketio

    用 gevent ,貌似还可以
    2225377fjs
        2
    2225377fjs  
       2016-07-22 12:55:53 +08:00
    单 worker 多 worker 跟协程其实并没有太大关联啊,为啥多 worker 就不能最大发挥协程作用了。。。。
    其实脱离 uwsgi , gunicorn 这些环境,单纯 python 也能够很好的解决楼主这问题。
    (感觉现在 python 的生产环境大家都被一些东西困住了,就类似于总是用人提到 Java 就是 Spring , Tomcat 啥的)
    awanabe
        3
    awanabe  
    OP
       2016-07-22 13:24:00 +08:00 via iPhone
    @playniuniu gevent eventlet 都是协程 gunicorn 都支持,只是没发用多 worker 模式
    awanabe
        4
    awanabe  
    OP
       2016-07-22 13:26:22 +08:00 via iPhone
    @2225377fjs 主要问题是 多 worker 多进程导致 socketio 没法找到对应的 connection

    你说的不在点上。 我说的主要是为了解决 python GIL 问题而出现的多进程模式存在的问题
    BOYPT
        5
    BOYPT  
       2016-07-22 13:28:08 +08:00
    这类情况一般是需要用一个 redis 之类的来共享 session ,不过 socketio 的 socket 的实现可能要改了
    awanabe
        6
    awanabe  
    OP
       2016-07-22 13:34:18 +08:00
    @BOYPT 这个共享 session 也没办法吧, 每次请求都是 送到容器的 master 进程, 然后 master 去随机去负载均衡, 去分配其中一个 worker ,分配的 worker 不是之前那个的话,这样长连接就断了。。
    BOYPT
        7
    BOYPT  
       2016-07-22 13:35:04 +08:00
    看了下模块, Feature 里面不就已经有多服务支持了吗,正是用 redis 做消息共享的
    https://pypi.python.org/pypi/python-socketio

    Optional support for multiple servers, connected through a messaging queue such as Redis or RabbitMQ.
    BOYPT
        8
    BOYPT  
       2016-07-22 13:37:43 +08:00
    @awanabe 长链接只要建立就不可能一直切换了吧,一直换 worker 那还能叫长链接咩
    至于断开重连,那对 socket.io 来说已经是另外一个 session ,但是对业务上还是同一个 session ,要你代码处理好业务逻辑
    aisk
        9
    aisk  
       2016-07-22 13:38:55 +08:00
    反正你迟早要到多节点上部署的,不找个第三方的东西来共享 session 怎么都是死局。
    glasslion
        10
    glasslion  
       2016-07-22 14:12:50 +08:00
    @awanabe " 每次请求都是 送到容器的 master 进程, 然后 master 去随机去负载均衡, 去分配其中一个 worker"

    这个推论是不正确的。
    参考 今年 Pycon 上 Django Channels 的演讲 https://speakerdeck.com/pycon2016/andrew-godwin-reinventing-django-for-the-real-time-web , 负载均衡不是随机的,用一致性哈希去解决
    glasslion
        11
    glasslion  
       2016-07-22 14:17:21 +08:00
    @awanabe 当然用来做 websocket 通信的这个组件确实不能用 gunicorn 或 uwsgi 来跑。 WSGI 本来就不支持持久化连接和服务端推送
    awanabe
        12
    awanabe  
    OP
       2016-07-22 14:17:38 +08:00
    @glasslion 涨姿势
    awanabe
        13
    awanabe  
    OP
       2016-07-22 14:19:04 +08:00
    @glasslion 那是否可以独立一个 app ,比如用 tornado 单独做一个只做 websocket 的应用, 单独服务?
    rrfeng
        14
    rrfeng  
       2016-07-22 14:24:46 +08:00
    大概和你们讨论的重点没什么关系

    但是容器里为啥还要开多个进程?难道不应该是单容器单进程……

    之前看过 docker 和 systemd 之争的文章,本来觉得有点奇怪,但是看到这个问题忽然间理解了一点。
    2225377fjs
        15
    2225377fjs  
       2016-07-22 14:25:31 +08:00
    @awanabe 我的意思是你可以考虑脱离 uwsgi 这些环境,直接 python 就可以有很好的解决方案。
    clino
        16
    clino  
       2016-07-22 14:28:22 +08:00
    "uwsgi 因为对协程支持不好" 哪里支持不好啊? 详细说下?
    glasslion
        17
    glasslion  
       2016-07-22 14:41:35 +08:00
    @awanabe 可以。同理还可以用第三方的服务推送服务
    awanabe
        18
    awanabe  
    OP
       2016-07-22 14:43:26 +08:00   ❤️ 1
    awanabe
        19
    awanabe  
    OP
       2016-07-22 14:44:51 +08:00
    @rrfeng python 的 GIL 导致 单进程多线程 效率很低, 只能换用多进程换取效率
    awanabe
        20
    awanabe  
    OP
       2016-07-22 14:45:54 +08:00
    @glasslion 单独的服务 对应的麻烦点就是 用户识别
    ccl
        21
    ccl  
       2016-07-22 14:48:08 +08:00
    socket.io 为了兼容旧浏览器开启的 polling 让多核化环境 handshake 报错。
    引入 socket.io-client 时,请覆盖 `transports`,例如 `io( '/chat', { transports: [ 'websocket' ]})`,只用 websocket 。
    此时,后端负载均衡器就不用每次都绑定同一台机了。
    yxaaa123
        22
    yxaaa123  
       2016-07-22 14:51:08 +08:00
    可以把 polling 关掉。
    rrfeng
        23
    rrfeng  
       2016-07-22 14:52:35 +08:00
    @awanabe
    如果用容器的话就是一个容器一个进程,开启多个容器,在容器外部做负载均衡。
    50vip
        24
    50vip  
       2016-07-22 14:54:39 +08:00
    确实这个挺坑的,但是这个问题搜都搜不到,后面老老实实看官方文档,才发现这个~~~囧
    awanabe
        25
    awanabe  
    OP
       2016-07-22 15:08:20 +08:00
    @2225377fjs 不是很明白你说的 python 直接的解决方案
    awanabe
        26
    awanabe  
    OP
       2016-07-22 15:31:37 +08:00
    @rrfeng 你可以把 uwsgi , gunicorn 的这些多 worker 进程的看做一个容器。 他们外面也是要套一层负载均衡的。
    这个容器 是为 python 服务的, 所有就有因为 PythonGIL 问题做了多进程模型。。

    所以还是 Python GIL 的问题

    我们两个不在一个维度上说事情
    jeffreylea
        27
    jeffreylea  
       2016-07-22 15:47:28 +08:00
    已经放弃 socket.io 改用 ws.js
    2225377fjs
        28
    2225377fjs  
       2016-07-22 15:59:46 +08:00
    @awanabe 楼主你缺少一个好用而且用的熟练的 python 分布式服务化框架。
    我猜楼主是要做一个基于 websocket 的网关或者推送服务吧,想要维持大量的客户端连接,所以需要多个进程来支撑,那么可以单独做 connector 系统,专门用来维护与客户端的连接,多开进程,自然就能承受更多的客户端连接了( gevent , tornado 都能很方便的实现)。

    当然系统又不可能只是维护连接就好了,例如可能业务层面上需要主动向特定客户推送一些数据,那么就涉及到别的业务系统向当前的 connector 系统发送数据,然后推送给指定客户端的需求,也就发展成了各个系统间通信交互或者说 RPC 调用的需求。
    如果楼主有用的 6 的 Python 分布式框架就能解决了。。。

    我最开始的回复其实就是想说,可以更发散一些,不能总是用做 web 系统的思维来做所有的业务,毕竟 Python 也是可以做分布式, RPC ,服务化的。有时候业务变得复杂了,或者是要求变高了,例如需要更高的承载,更高的吞吐量,用简单做 web 系统的方式可能就不能满足需求了。

    (不过,现在 Python 环境对分布式,服务系统这些东西讨论的还真的很少,也难得见到有好用的开源框架放出来,相反这方面 Java 环境倒是做的最好的,毕竟人家做各种大型业务系统成功案例多,自然就有东西放出来讨论了)

    楼主说的 GIL 问题,既然是多进程的系统,那就不该考虑线程的问题了。既要维护大量的连接,又要做比较复杂的业务间的交互,能够单进程当然是最好的,毕竟多线程之间(或者协程并发)的交互是最方便的,但是 Python 做不了这个事情,楼主倒是可以考虑 Java ,如果一定要选择 Python ,那就只有多进程,分布式的方案了。

    自己的见解,有不对的地方楼主还请指出来。自己用 python 做过可能跟楼主类似的东西,所以看到了帖子就多说了点。
    awanabe
        29
    awanabe  
    OP
       2016-07-22 16:50:57 +08:00
    @2225377fjs 理解, 还是灵活的调整吧, 你的意思就是尽量拆分服务, 每种服务都可以有自己的实现嘛。

    至于通知的话,现在可行的方案, 可以起一个 NodeJS 的 socketIO 服务器(集群)就可以规避掉这个问题。也相当于拆服务嘛。

    只是现在还没到拆分服务的阶段。单机有单机的玩法, 就先用单 worker 扛着呗, 需要扩容了就拆。
    rrfeng
        30
    rrfeng  
       2016-07-22 17:15:28 +08:00
    @awanabe
    嗯这个我是了解的。所以一开始我就说了和主题关系不大~
    cppgohan
        31
    cppgohan  
       2016-07-23 11:41:13 +08:00
    @glasslion "WSGI 本来就不支持持久化连接和服务端推送"

    对 WSGI 一知半解, 从文档看: https://www.python.org/dev/peps/pep-0333/

    > Applications and middleware are forbidden from using HTTP/1.1 "hop-by-hop" features or headers, any equivalent features in HTTP/1.0, or any headers that would affect the persistence of the client's connection to the web server. These features are the exclusive province of the actual web server, and a server or gateway should consider it a fatal error for an application to attempt sending them, and raise an error if they are supplied to start_response() . (For more specifics on "hop-by-hop" features and headers, please see the Other HTTP Features section below.)

    单看文档感觉使用 WSGI 在约束 HTTP Server 的情况下, 它是能够保证持久化连接的? 所以并不算 WSGI 的问题, 算是 uWSGI 在多 worker 情况下没法满足 wsgi 的约束?
    clino
        32
    clino  
       2016-07-23 22:00:08 +08:00
    @awanabe 我测试了一下果然有这个问题,我插,想想有没有什么办法解决
    clino
        33
    clino  
       2016-07-28 09:26:33 +08:00
    @awanabe 不对,我看 werkzeug 有针对处理的:
    https://github.com/pallets/werkzeug/blob/master/werkzeug/local.py#L20
    我用 flask 测过也没有问题,不同的 gevent 之间的这些 local 变量不会共享
    clino
        34
    clino  
       2016-07-28 09:32:22 +08:00
    @awanabe 明白了,原来那篇文章也没说错,他说的是 uwsgi --ugreen 有问题,但是如果 uwsgi --gevent 就可以,因为 werkzeug 本来有对 greenlet 做了处理, 不过 uwsgi 的 ugreen 我基本没用过了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2610 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 10:48 · PVG 18:48 · LAX 02:48 · JFK 05:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.