Tornado长轮询和WebSocket

Http协议是一种请求响应式协议, 不允许服务端主动向客户端发送信息.

短轮询是一种简单的实现服务端推送消息的解决方案, 客户端以一定间隔自动向服务端发送刷新请求, 服务端返回要推送的消息作为响应.

短轮询存在严重缺陷:

  • 短轮询需要进行高频率的网络通信, 且收到大多数轮询请求时服务端没有消息需要推送.

  • 需要维护大量Http连接, 严重消耗资源

如果手写一个短轮询的话你会发现, 短轮询带来的问题不止这些.

长轮询#

长轮询是客户端向服务端发送一个刷新请求, 并保持连接打开. 服务端收到请求后不立即响应,等到需要推送消息时再返回. 然后, 客户端再次发送刷新请求并等待推送.

长轮询不再需要频繁发送刷新请求, 但是长期等待的Http连接可能断开, 需要考虑异常处理.

长轮询请求等待过程中服务端处理进程不能被阻塞, tornado的异步IO机制可以方便的使用长轮询.

import tornado.httpserver
from tornado.ioloop import IOLoop
import tornado.options
import json
from tornado.web import Application, RequestHandler, asynchronous


class ChatApp(Application):
    def __init__(self):
        handlers = [
            (r'/new-message', NewMsgHandler),
            (r'/update-message', UpdateMsgHandler)
        ]
        super(ChatApp, self).__init__(self, handlers=handlers)
        self.cache = []


class NewMsgHandler(RequestHandler):
    def __init__(self, app):
        self.app = app

    def post(self):
        msg = self.get_argument('msg')
        self.app.cache.append(msg)


class UpdateMsgHandler(RequestHandler):
    def __init__(self, app):
        self.app = app

    @asynchronous
    def post(self):
        if self.request.connection.stream.closed():
            return
        response_json = json.dumps(self.app.cache)
        self.write(response_json)
        self.finish()


def main():
    tornado.options.parse_command_line()
    app = ChatApp()
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

if __name__ == '__main__':
    main()

示例的完整代码,请去草民的仓库

这篇博客提供了一个更为强大的基于长轮询的聊天室, 而且草民非常喜欢他的代码风格.

tornado的长轮询聊天室例子分析

长连接断开的处理机制可以参考这篇文章

WebSocket#

WebSocket是HTML5协议中提出的客户-服务器通信协议, 它允许双方以类似TcpSocket的方式进行通信.

它基于标准Http协议实现, 但使用新的ws://URL格式.

Tornado在websocket模块中提供了一个WebSocketHandler类,

  • open方法在一个新的WebSocket连接打开时被调用,

  • on_message方法在连接接收到新的消息时被调用

  • on_close方法在客户端关闭时被调用

  • write_message(message, binary=False)方法可以通过WebSocket向对方发送数据

      - 若binary=False, message可以是string或者dict(会被自动编码为JSON), 
    
      - 若binary=True, message可以是任意byte string
    

继承WebSocketHandler并重写自己上述方法,实现基于WebSocket的应用.

来自tornado官方文档的示例:

class EchoHandler(WebSocketHandler):

	def allow_draft76(self):
    	return True

	def check_origin(self, origin):
    	return True

    def open(self):
        print "new client opened"

    def on_close(self):
        print "client closed"

    def on_message(self, message):
        self.write_message(message)

allow_draft76check_origin用于进行安全性校验, 只有它们都返回True时WebSocket才能正常连接.

tornado WebSocketHandler文档

Python WebSocket#

WebSocket虽然是为Web应用设计的, 为了减轻后端的开发压力可以采用WenSocket代替Tcp Socket与后端交互.

Websocket-client是Python Websocket支持包,可以使用pip安装:

pip install websocket-client

PyPi websocket-client

websocket-client提供了几个低级API:

  • 建立websocket连接

ws = create_connection("ws://echo.websocket.org/")

  • 发送消息

ws.send(msg)

  • 接收消息

result = ws.recv()

  • 关闭连接

ws.close()

websocket也提供了JS风格的API, 来自官方文档的示例:

import websocket
import thread
import time

def on_message(ws, message):
    print message

def on_error(ws, error):
    print error

def on_close(ws):
    print "### closed ###"

def on_open(ws):
    def run(*args):
        for i in range(3):
            time.sleep(1)
            ws.send("Hello %d" % i)
        time.sleep(1)
        ws.close()
        print "thread terminating..."
    thread.start_new_thread(run, ())


if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp(
        "ws://echo.websocket.org/",
        on_message = on_message,
        on_error = on_error,
        on_close = on_close
    )
    ws.on_open = on_open
    ws.run_forever()

更多信息请参见Github websocket-client

posted @   -Finley-  阅读(6795)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示
主题色彩