Django/gevent socket.IO 与 redis pubsub,如何安排?
我有一个独立的Python脚本,它的功能很简单,就是从Twitter的实时数据接口获取数据。每当收到一条消息时,它会通过Redis的发布/订阅功能,把消息发布到一个叫“tweets”的频道。下面是这个脚本:
def main():
username = "username"
password = "password"
track_list = ["apple", "microsoft", "google"]
with tweetstream.FilterStream(username, password, track=track_list) as stream:
for tweet in stream:
text = tweet["text"]
user = tweet["user"]["screen_name"]
message = {"text": text, "user": user}
db.publish("tweets", message)
if __name__ == '__main__':
try:
print "Started..."
main()
except KeyboardInterrupt:
print '\nGoodbye!'
我的服务器端使用的是django-socketio来实现socket.io的功能(这个库是基于gevent-socketio的)。这个库提供了一些方便的装饰器和一个叫broadcast_channel的方法。因为我是在Django框架下做的,所以我把这些代码放在views.py里,只是为了方便导入。以下是我的views.py代码:
def index(request):
return render_to_response("twitter_app/index.html", {
}, context_instance=RequestContext(request))
def _listen(socket):
db = redis.Redis(host="localhost", port=6379, db=0)
client = db.pubsub()
client.subscribe("tweets")
tweets = client.listen()
while True:
tweet = tweets.next()
tweet_data = ast.literal_eval(tweet["data"])
message = {"text": tweet_data["text"], "user": tweet_data["user"], "type": "tweet"}
socket.broadcast_channel(message)
@on_subscribe(channel="livestream")
def subscribe(request, socket, context, channel):
g = Greenlet.spawn(_listen, socket)
客户端的socket.io JavaScript代码则是连接到“livestream”频道,并捕获任何发送到这个频道的消息:
var socket = new io.Socket();
socket.connect();
socket.on('connect', function() {
socket.subscribe("livestream");
});
socket.on('message', function(data) {
console.log(data);
});
这个代码明显的问题是,每当有新用户或者新的浏览器窗口打开这个页面时,都会生成一个新的_listen方法,这样每个用户都会订阅到推文,导致每个客户端收到重复的消息。我的问题是,应该把_listen方法放在哪里,才能确保它只创建一次,而不管有多少个客户端?另外,要记住broadcast_channel方法是一个socket实例的方法。
1 个回答
3
问题在于我本来应该用socket.send来发送信息,但我却用了socket.broadcast_channel。