基于Django & Channels & WebSocket & Twisted的物联网远程控制】写到,下位机每隔一段时间连接上位机传送数据和进行任务下发,现在领导改需求了,要求使用长链接,使得上位机的任务可以随时下发,方便用户。小米的智能家电采取的应该也都是长链接的方式,由于小米家电本身是市电供应,家里也都是wifi上网,所以除了会多消耗服务器资源外,这种即时的控制还是蛮不错的。但是真正用于野外的物联网终端,还是要平衡好电量、流量的问题才行。

之前也提到,web网页用户操作,web后台生成并发布到redis订阅,Twisted 采用redis客户端监听,并在下位机连接的时候下发任务,这里我用的相当于是一个全局变量存下来哪些设备有任务,如果要改成长链接实时控制,这种方法就不太合适了。

上位机是一个单独的service,redis监听是一个单独的service,这就要求两个service能够通信——redis客户service告诉上位机服务serivce,有新任务啦,赶紧下发!

解决这个问题过程中,也加深了对Twisted的理解,重要的有三点,参考[1]

  • service不接收也不发送数据,只负责启动和停止factory
  • factory也不接收和发送数据,负责生成和销毁protocol实例,protocol实例处理链接和收发数据
  • factory是长生命周期的,protocol实例是用完就销毁的

有了以上三点,事情就简单多了。

  • 创建redis client factory的时候,传入上位机服务factory
  • 让上位机服务factory 维护一个protocol实例的集合,protocol实例创建时添加,销毁是删除
  • 通过 redis_facotry.up_service_factory.protocol_set 获取上位机服务protocol实例,执行相应任务(长链接protocol实例不会被销毁)

直接看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

class FisheryProtocol(protocol.Protocol):

def connectionMade(self):
self.factory.protocol_set.add(self)

def connectionLost(self):
self.factory.protocol_set.pop(self)


class RedisProtocol(redis.SubscriberProtocol):

def messageReceived(self, pattern, channel, message):
self.factory.fishery_factory.protocol_set # get a specified protocol from protocol_set

class RedisFactory(redis.SubscriberFactory):
# ... other code

def __init__(self, fishery_factory):
super().__init__()
self.fishery_factory = fishery_factory