先说明我的需求, 其实也很简单, 我们的后台加一个提示功能, 有新订单到时在页面左下角进行提示, 并提供一个接口, 用于用户在app下单给api调

tornado自身就有websocket的实现,有websocket的handler,直接拿来使用就可以了

from tornado.websocket import WebSocketHandler
from tornado.web import RequestHandler
class WebsocketHandler(WebSocketHandler):
  '''websocket   /ws
  '''
  clients = []
  
  def open(self):
    self.clients.add(self)
  
  def on_message(self, message):
    pass
  
  def on_close(self):
    if self in self.clients:
      self.clients.remove(self)


class WsNotifyHandler(RequestHandler)
  def post(self):
    '''给api调的接口 /api/wsnotify
    '''
    # some_data = self.get_argument('param')
    for client in WebsocketHandler.clients:
      client.write_message('..{给客户端写数据}..')

这里实现起来也简单, 用一个数组(给每个页面都提示)来记录连上websocket的客户端, 在接口处一次循环, 给客户端数据就O了, 前段按照数据做不同处理.

结果布到线上就出问题了, 有时有的页面能收到提示, 有的不行, 有时页面都可以或都不行, 打断点发现clients这个数组中websocket客户端个数却是在变动的. tornado一般都会以运行多个进程来跑, 并使用nginx作为代理服务器, websocket连接属于不同的进程, 所以不一定可以给客户端写数据.

解决的方式也有几种:

  1. 进程间共享websocket连接, 这是不可取的, 维护这个共享资源也是一件头疼的事. websocket连接也不能序列化存储在redis中.
  2. 单独将websocket提取出来, 单独作为一个服务. 我们也只是一个小小的管理后台, 也没必要这么做.
  3. 因为我们这里这个websocket也功能比较简单, 不需要接受客户端给后端发数据, 所以, 可以在这里做文章, 在接口处, 对每一个websocket都建连接(tornado自带有websocket client的功能),并写数据, 然后再websocket中的on_message中获取, 再将数据写会给客户端. 虽然听起来很脑残, 可以我就是这么做的.
from tornado.websocket import WebSocketHandler
from tornado.gen import coroutine
from tornado.web import RequestHandler, connect_websocket
WEBSOCKETS = [
    'ws://127.0.0.1:8001/ws'
    'ws://127.0.0.1:8002/ws'
    'ws://127.0.0.1:8003/ws'
    'ws://127.0.0.1:8004/ws'
]
class WebsocketHandler(WebSocketHandler):
  '''websocket   /ws
  '''
  clients = []
  
  def open(self):
    self.clients.add(self)
  
  def on_message(self, message):
    for client in self.clients:
      client.write_message(message)
  
  def on_close(self):
    if self in self.clients:
      self.clients.remove(self)


class WsNotifyHandler(RequestHandler)
  @coroutine
  def post(self):
    '''给api调的接口 /api/wsnotify
    '''
    some_data = self.get_argument('param')
    
    for ws_url in WEBSOCKETS:
      ws_client = yield connect_websocket(ws_url)  ;; 建立连接
      ws_client.write_message(json.dumps(some_data))  ;; 写数据
      ws_client.close()

这样子就可以给每一个连接的客户端发送提示了