Веб-сокеты Cherrypy + Autobahn на одном порту

Можно ли запустить (смонтировать в вишневом дереве) класс websocket autobahnn для работы на том же порту, но с другим URL-адресом?

Например:

  • http://localhost:8080/web для статического содержимого сервера (html + javascript)
  • ws://localhost:8080/websocketA для сервера связи WS через класс WSA
  • ws://localhost:8080/websocketB для сервера связи WS через класс WSB

Это моя конфигурация автобана и запуск:

self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)

factory = WebSocketServerFactory("ws://0.0.0.0:8081", debug = False)
factory.protocol = WSA.SocketClient

coro = self.loop.create_server(factory, "0.0.0.0", 8081)
server = self.loop.run_until_complete(coro)

self.loop.run_forever()

Это моя вишневая конфигурация и запуск:

cherrypy.config.update({
    'server.socket_host' : '0.0.0.0',
    'server.socket_port' : 80,
})

cherrypy.tree.mount(WebApi.Web(), '/web', {
   '/': {
        "tools.staticdir.on": True,
        "tools.staticdir.root": os.path.dirname(os.path.abspath(__file__)),
        "tools.staticdir.dir": "Web",
        "tools.staticdir.index": "index.html"
    }
})

cherrypy.engine.start()

На данный момент сервер WebSocket работает на порту 8081, но я хотел бы запустить его на том же порту, что и сеть (8080). Если это возможно..


person Baboon    schedule 17.03.2015    source источник
comment
Я сделал что-то подобное с wss: и https:, я запустил haproxy, который обрабатывал подключение к веб-браузеру, затем бэкэнд haproxy был направлен либо на мой http-сервер, либо на мой маршрутизатор веб-сокетов (автобан). Итак, они были на разных портах на бэкенде, но они были на одном и том же порту на интерфейсе (haproxy выяснил, какой из них какой).   -  person Greg    schedule 19.03.2015


Ответы (2)


Буквально отвечая на ваш вопрос, вы не можете сделать это с CherryPy и Autobahn. Обычная обработка запросов CherryPy является синхронной, и, кроме того, это многопоточный сервер. Другими словами, невозможно выделить поток для соединения WebSocket. Возможность CherryPy монтировать отдельное приложение WSGI здесь не имеет смысла, потому что WSGI по своей сути является синхронным протоколом. А WebSockets по своей сути асинхронны. Но это не значит, что вы не можете сделать это немного по-другому.

CherryPy и ws4py

К счастью, благодаря продуманному дизайну CherryPy не ограничивается WSGI и допускает расширение. Этот факт используется в хорошей библиотеке участником CherryPy Сильвеном Хеллегуарком, ws4py. Он имеет интеграцию с CherryPy.

#!/usr/bin/env python3


import cherrypy
from ws4py.server.cherrypyserver import WebSocketPlugin, WebSocketTool
from ws4py.websocket import WebSocket


class Ws:

  @cherrypy.expose
  def a(self):
    '''WebSocket upgrade method.
    Method must exist for ``WebSocketTool`` to work, 404 returned otherwise.
    '''

  @cherrypy.expose
  def b(self):
    pass


class HandlerA(WebSocket):

  def received_message(self, message):
    self.send('"A" is my reply')


class HandlerB(WebSocket):

  def received_message(self, message):
    self.send('"B" is my reply')


class App:

  @cherrypy.expose
  def index(self):
    return '''<!DOCTYPE html>
      <html>
      <body>
        <table cellspacing='10'>
          <tr>
            <td id='a'></td>
            <td id='b'></td>
          </tr>
        </table>

        <script type='application/javascript'>
          var wsA       = new WebSocket('ws://127.0.0.1:8080/websocket/a');
          wsA.onmessage = function(event)
          {
            document.getElementById('a').innerHTML += event.data + '<br/>';
          };

          var wsB       = new WebSocket('ws://127.0.0.1:8080/websocket/b');
          wsB.onmessage = function(event)
          {
            document.getElementById('b').innerHTML += event.data + '<br/>';
          };

          setInterval(function()
          {
            wsA.send('foo');
            wsB.send('bar');
          }, 1000);
          </script>
      </body>
      </html>
    '''


if __name__ == '__main__':
  cherrypy.config.update({
    'server.socket_host' : '127.0.0.1',
    'server.socket_port' : 8080,
    'server.thread_pool' : 8
  })

  cherrypy.tools.websocket = WebSocketTool()
  WebSocketPlugin(cherrypy.engine).subscribe()

  cherrypy.tree.mount(Ws(), '/websocket', {
    '/a' : {
      'tools.websocket.on'          : True,
      'tools.websocket.handler_cls' : HandlerA
    },
    '/b' : {
      'tools.websocket.on'          : True,
      'tools.websocket.handler_cls' : HandlerB
    } 
  })

  cherrypy.tree.mount(App(), '/')

  cherrypy.engine.signals.subscribe()
  cherrypy.engine.start()
  cherrypy.engine.block()

CherryPy, nginx и автобан

Начиная с версии 1.3 nginx поддерживает WebSockets. Таким образом, вы можете легко мультиплексировать различные серверные части.

server {
  listen  80;

  server_name localhost;

  location /web {
    proxy_pass         http://127.0.0.1:8080;
    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
  }

  location /websocket {
    proxy_pass         http://127.0.0.1:8081;
    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
  }

}
person saaj    schedule 20.03.2015
comment
Большое спасибо за ответ. Я провел несколько тестов с Cherrypy/ws4py, и у меня все получилось :) Так что ваш ответ на 100% правильный. - person Baboon; 21.03.2015

Попробуйте использовать cherrypy.tree.graft для монтирования WSA на другой конечной точке (называемой "имя_сценария" в документах cherrypy).

См. здесь пример подключения приложений WSGI к другой конечной точке в виде статических файлов: http://rhodesmill.org/brandon/2011/wsgi-under-cherrypy/

Дополнительные документы здесь: http://cherrypy.readthedocs.org/en/latest/advanced.html#host-a-foreign-wsgi-application-in-cherrypy

person Isa Hassen    schedule 18.03.2015