我正在尝试使用几个聊天创建一个Tornado应用程序。聊天应该基于HTML5 websocket。 Websockets很好地沟通,但我总是遇到每个消息被发布两次的问题。
该应用程序使用四个类来处理聊天:
Chat
包含到目前为止的所有书面邮件以及应通知所有waiters
的列表
ChatPool
用作新Websockets的查找 - 当没有具有所需scratch_id
的人时,它会创建新聊天或返回现有聊天实例。
ScratchHandler
是所有HTTP请求的入口点 - 它解析基本模板并返回客户端的所有详细信息。
ScratchWebSocket
向数据库查询用户信息,设置连接并通知聊天实例是否必须传播新消息。
如何防止多次发布消息? 如何使用龙卷风构建多聊天应用程序?
import uuid
import tornado.websocket
import tornado.web
import tornado.template
from site import models
from site.handler import auth_handler
class ChatPool(object):
# contains all chats
chats = {}
@classmethod
def get_or_create(cls, scratch_id):
if scratch_id in cls.chats:
return cls.chats[scratch_id]
else:
chat = Chat(scratch_id)
cls.chats[scratch_id] = chat
return chat
@classmethod
def remove_chat(cls, chat_id):
if chat_id not in cls.chats: return
del(cls.chats[chat_id])
class Chat(object):
def __init__(self, scratch_id):
self.scratch_id = scratch_id
self.messages = []
self.waiters = []
def add_websocket(self, websocket):
self.waiters.append(websocket)
def send_updates(self, messages, sending_websocket):
print "WAITERS", self.waiters
for waiter in self.waiters:
waiter.write_message(messages)
self.messages.append(messages)
class ScratchHandler(auth_handler.BaseHandler):
@tornado.web.authenticated
def get(self, scratch_id):
chat = ChatPool.get_or_create(scratch_id)
return self.render('scratch.html', messages=chat.messages,
scratch_id=scratch_id)
class ScratchWebSocket(tornado.websocket.WebSocketHandler):
def allow_draft76(self):
# for iOS 5.0 Safari
return True
def open(self, scratch_id):
self.scratch_id = scratch_id
scratch = models.Scratch.objects.get(scratch_id=scratch_id)
if not scratch:
self.set_status(404)
return
self.scratch_id = scratch.scratch_id
self.title = scratch.title
self.description = scratch.description
self.user = scratch.user
self.chat = ChatPool.get_or_create(scratch_id)
self.chat.add_websocket(self)
def on_close(self):
# this is buggy - only remove the websocket from the chat.
ChatPool.remove_chat(self.scratch_id)
def on_message(self, message):
print 'I got a message'
parsed = tornado.escape.json_decode(message)
chat = {
"id": str(uuid.uuid4()),
"body": parsed["body"],
"from": self.user,
}
chat["html"] = tornado.escape.to_basestring(self.render_string("chat-message.html", message=chat))
self.chat.send_updates(chat, self)
注意:来自@A的反馈后。 Jesse我从send_updates
更改了Chat
方法。不幸的是,它仍然会返回双倍值。
class Chat(object):
def __init__(self, scratch_id):
self.scratch_id = scratch_id
self.messages = []
self.waiters = []
def add_websocket(self, websocket):
self.waiters.append(websocket)
def send_updates(self, messages, sending_websocket):
for waiter in self.waiters:
if waiter == sending_websocket:
continue
waiter.write_message(messages)
self.messages.append(messages)
2.EDIT :我将我的代码与示例提供的演示进行了比较。在websocket示例中,通过WebSocketHandler
子类和类方法将新消息传播给服务员。在我的代码中,它是由一个分离的对象完成的:
来自演示:
class ChatSocketHandler(tornado.websocket.WebSocketHandler):
@classmethod
def send_updates(cls, chat):
logging.info("sending message to %d waiters", len(cls.waiters))
for waiter in cls.waiters:
try:
waiter.write_message(chat)
except:
logging.error("Error sending message", exc_info=True)
我的应用程序使用对象而没有WebSocketHandler的子类
class Chat(object):
def send_updates(self, messages, sending_websocket):
for waiter in self.waiters:
if waiter == sending_websocket:
continue
waiter.write_message(messages)
self.messages.append(messages)
答案 0 :(得分:9)
如果要创建基于Tornado的多聊天应用程序,我建议您使用某种消息队列来分发新消息。这样,您就可以在负载均衡器(如nginx)后面启动多个应用程序进程。否则,您将只能坚持一个进程,因此在缩放方面受到严重限制。
我更新了我的旧Tornado聊天示例以支持您要求的多房间聊天。看一下存储库:
<强> Tornado-Redis-Chat 强>
<强> Live Demo 强>
这个简单的Tornado应用程序使用Redis Pub / Sub功能和websockets将聊天消息分发给客户端。只需使用聊天室ID作为发布/订阅频道,即可轻松扩展多房间功能。
答案 1 :(得分:2)
on_message
将消息发送到所有连接的websockets,包括发送消息的websocket。这是问题:邮件是否回传给发件人?