pyzmq.ZMQStream中的bug

 

 

问题的现象

 

之前写了一个SharedSocketStream的组件,该组件的目的是用同一个socket负责处理消息发送,防止过多的socket打开和关闭。SharedSocketStream内部会持有一个zmqstream.ZMQStream实例,所有的消息都由ZMQStream发送。

 

测试发现,当有大量的消息发送时,会出现消息突然发送不出去的现象。对于该问题,排查了数日,昨天算是找到了最根本的原因。总结地说是多线程并发引起数据不一致。

 

问题的原因

 

ZMQStream内部会持有一个ZMQSocket,IOLoop会监听该ZMQSocket的事件(read, write, error),至于哪些事件需要IOLoop监听是调用IOLoop.update_handler方法设置的,该方法接受一个events的参数,该参数是一个整数,这个整数的意义是:

 

25:监听error, read事件

29:监听error, read, write事件。

 

当IOLoop监听到ZMQSocket的某个事件,ZMQStream._handle_events方法会被回调(需要注意的是,这个调用发生在IOLoop的线程中)。如果event是read,那么_handle_recv会被调用,会从socket中收消息。如果event是write,那么_handle_send方法会被调用,会做真正的发送消息的操作。

_handle_events方法的最后会调用下面代码:

 

 

          """rebuild io state based on self.sending() and receiving()"""

          if self.socket is None:

              return

          state = self.io_loop.ERROR

          if self.receiving():

              state |= self.io_loop.READ

          if self.sending():

              state |= self.io_loop.WRITE

          if state != self._state:

              self._state = state

              self.io_loop.update_handler(self.socket, state)

 

 

该方法的逻辑是根据ZMQStream内部的状态更新IOLoop需要监听的事件(调用update_handler)。注意这里的逻辑:如果当前发送队列里没有数据,新的state会是25,也就是说,不会监听该ZMQSocket的写事件。

 

再来看看ZMQStream.send_multipart方法。我们在外部使用ZMQStream就是调用send_multipart方法发送数据。ZMQStream.send_multipart方法并不会使用socket发送数据,而是把数据放到内部队列中。然后调用下面的代码来更新IOLoop需要监听该socket的write事件:

 

 

          """Add io_state to poller."""

          if not self._state & state:

              self._state = self._state | state

              self.io_loop.update_handler(self.socket, self._state)

 

 

该方法先做了一个判断,这个判断的逻辑是:如果ZMQStream没有注册write事件,才会向IOLoop注册write事件。具体来说,如果self._state == 25(25 & 4 = 0,4就是那个state),才会将生成一个29(使用 self._state | state),将29注册到IOLoop上。

 

其实,根本的原因就在于,下面的两行代码会被两个线程同时调到:

 

 

1              self._state = self._state | state

2             self.io_loop.update_handler(self.socket, self._state)

 

 

其中一个线程是IOLoop的线程,我们可以称之为ioloop_thread, 另外一个线程是我们的自己发送消息的线程(该线程会调用ZMQStream.send_multipart,间接调用了这两行代码),我们可以称之为work_thread。下面是一个极端的情况会导致bug的出现:(我们将这两行代码进行了编号)

 

1. ioloop_thread走完了语句1,将self._state设置为25

2. work_thread走完了语句1,将self._state设置为29

3. work_thread走完了语句2,设置监听read, write, error事件。

4. ioloop_thread走完了语句2,设置监听read, error事件。

 

如果按照这样的顺序走完,状态会变成下面这样:

 

a) ZMQStream内部的_state是29,也就是说,ZMQStream自己认为IOLoop会监听socket read, write, error事件。

b) IOLoop被配置只监听socket的read, error事件。

 

由于write事件没有被监听,所以,最可恶的是这个判断if not self._state & state。本来,在调用ZMQStream.send_multipart方法时,是有机会重新配置IOLoop监听write事件的,但是由于这个判断,并且ZMQStream的内部_state是29,所以IOLoop._update_handle不会被调用。从而,IOLoop永远都只监听read, error事件,ZMQStream._handle_send方法也永远不会被调用,导致消息不会被真正发送。

 

解决办法

 

可以绕过这个问题,在调用ZMQStream.send_multipart之前,强制设置ZMQStream._state = 25

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
旋转手势是hammer.js库的一个功能,它允许用户通过触摸屏幕来旋转一个元素。然而,有时候在使用hammer.js的过程可能会遇到旋转功能的bug。 旋转bug可能有不同的表现形式,如旋转过程元素位置的跳动、旋转角度的不稳定或者旋转速度异常等等。这些问题与hammer.js库本身的设计或者配置有关,所以我们可以通过检查代码和配置项来解决旋转bug。 首先,可以确保hammer.js库的版本是最新的,因为更新版本通常会修复一些旧版本bug。然后,我们可以检查hammer.js的配置项,例如可触摸元素的选择器、旋转的阈值、旋转的方向等等。根据旋转bug的具体表现,可能需要调整这些配置项来获得更好的用户体验。 此外,我们还可以检查其他可能影响旋转功能的因素。例如,元素的CSS样式是否正确设置了旋转的心点、是否存在其他与旋转相冲突的事件监听器等等。通过排查这些因素,我们可以找到并解决造成旋转bug的根本原因。 如果以上方法无法解决旋转bug,还可以考虑使用其他库或者自行实现旋转功能。有一些其他的JavaScript库或者插件也提供了类似的旋转功能,可以避免hammer.js的bug。另外,如果具备前端开发的能力,可以尝试自行实现旋转功能,这样可以更好地掌控代码和调试bug。 总之,解决hammer.js旋转bug的方法包括检查代码和配置项、更新库版本、检查其他可能影响旋转的因素,并可以考虑使用其他库或自行实现旋转功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值