协程问题:Can only use Waiter.switch method from the Hub greenlet

最近项目上在使用gevent模块的时候频频报错:

 AssertionError:Can only use Waiter.switch method from the Hub greenlet

项目中用到了gevent模块的猴子补丁:monkey_patchall()

也就是所有的阻塞模块都支持协程了,但是出现这个报错说明有的协程没有走gevent模块,从源码层面分析,

    def switch(self, value=None):
        """Switch to the greenlet if one's available. Otherwise store the value."""
        greenlet = self.greenlet
        if greenlet is None:
            self.value = value
            self._exception = None
        else:
            assert getcurrent() is self.hub, "Can only use Waiter.switch method from the Hub greenlet"
            switch = greenlet.switch
            try:
                switch(value)  
            except:
                self.hub.handle_error(switch, *sys.exc_info())

协程在切换的时候,getcurrent()当前hub不对应,也就是项目中存在了两个gevent hub或者hub为None。

通常来说项目import gevent模块再打上猴子补丁是不会出现这种情况,经过排查发现,项目使用了一个flask_uwsgi_websocket模块,这个模块主要是实现uwsgi websocket协议通信功能, 里面的网络通信阻塞方法都通过gevent模块实现了协程异步,因此可以认为这个模块也是使用了gevent hub。

经过排查,发现在通过flask_uwsgi_websocket调用阻塞方法recv()和send()进行协程异步时,就会出现刚刚那个报错。既然flask_uwsgi_websocket用的也是gevent模块实现的协程,也就是用的同一个gevent hub,那么报错就不可能出现两个gevent hub,只可能是hub为None,hub为就只有一种情况:

flask_uwsgi_websocket的配置有问题,未正确开启协程。

如何解决?

首先查看看flask_uwsgi_websocket官方文档:
https://github.com/zeekay/flask-uwsgi-websocket

可以看到这么一栏:
在这里插入图片描述

要想使用flask_uwsgi_websocket支持websocket协议在uwsgi服务器上通信,又要使用gevent实现协程异步,需要同时开启–wsgi和–gevent两个参数:

–wsgi 声明该模块支持uwsgi通信规范

–gevent 指定项目运行支持的协程数量

因此,解决这个问题的办法就是在配置文件,如ini中添加如:

wsgi=true
gevent=10

或者在启动命令中如文档所说添加启动参数:

 --gevent 100 --wsgi

注:开启协程后会导致uwsgi flask应用无法响应http请求,只能响应websocket协议通信,此时需要开启–threads 或者配置threads=4开启线程即可

其他情况

还有一种情况下会触发这个报错,如:flask_uwsgi_websocket框架下,向外部模块如so注册回调,然后在so回调中执行包含greenlet协程的代码块,就会出现这个报错。

原因:flask_uwsgi_websocket框架下开启协程后只能单线程运行,因此greenlet hub,也只能存在一个对象,运行在主线程中。当从外部模块回调时,相当于另起一个子线程处理回调,此时执行协程代码块,会去寻找注册的greenlet hub,由于两者运行不在同一个线程,因此就无法找到协程代码块所注册的greenlet hub,也就出现了Can only use Waiter.switch method from the Hub greenlet报错

解决方式:对于回调产生的协程找不到greenlet hub问题,可以采用队列Queue的方式异步解耦,即回调处理协程代码块一开始就运行在主线程中,通过队列元素判断是否执行协程代码块。当有回调发生时,回调事件加入队列Queue中,此时回调线程执行完毕。主线程中的回调处理协程接收到队列元素开始执行协程代码块,由于在整个异步回调过程中,协程代码块始终是运行在主线程中,也就不会出现相应的报错

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值