Python 3.x 用于WebSocket通信的Jupyter
我正在开发一个需要WebSockets API的应用程序,并将Jupyter(以前的IPython)笔记本作为一个相对次要的功能进行集成。既然Jupyter已经使用WebSockets进行通信,那么要将它集成为一个通用库来为自己之外的其他WebSockets API提供服务有多困难?或者我最好使用另一个库,比如Python 3.x 用于WebSocket通信的Jupyter,python-3.x,websocket,ipython,ipython-notebook,jupyter,Python 3.x,Websocket,Ipython,Ipython Notebook,Jupyter,我正在开发一个需要WebSockets API的应用程序,并将Jupyter(以前的IPython)笔记本作为一个相对次要的功能进行集成。既然Jupyter已经使用WebSockets进行通信,那么要将它集成为一个通用库来为自己之外的其他WebSockets API提供服务有多困难?或者我最好使用另一个库,比如aiohttp?我正在寻找关于这方面最佳实践的任何建议和提示。谢谢 您可以将WebSocket从主应用程序代理到Jupyter 无论您使用什么技术来服务WebSocket,代理循环都非常相
aiohttp
?我正在寻找关于这方面最佳实践的任何建议和提示。谢谢 您可以将WebSocket从主应用程序代理到Jupyter
无论您使用什么技术来服务WebSocket,代理循环都非常相似(等待消息,将消息向前推)。然而,它将依赖于web服务器,因为Python没有类似于WSGI的WebSocket标准
我做了一个。必须在自己的进程中运行Jupyter,因为至少在编写代码时,将Jupyter直接嵌入到应用程序是不可行的。但我不确定最新版本是否改变了这一点。Jupyter自己也在使用龙卷风
"""UWSGI websocket proxy."""
from urllib.parse import urlparse, urlunparse
import logging
import time
import uwsgi
from pyramid import httpexceptions
from ws4py import WS_VERSION
from ws4py.client import WebSocketBaseClient
#: HTTP headers we need to proxy to upstream websocket server when the Connect: upgrade is performed
CAPTURE_CONNECT_HEADERS = ["sec-websocket-extensions", "sec-websocket-key", "origin"]
logger = logging.getLogger(__name__)
class ProxyClient(WebSocketBaseClient):
"""Proxy between upstream WebSocket server and downstream UWSGI."""
@property
def handshake_headers(self):
"""
List of headers appropriate for the upgrade
handshake.
"""
headers = [
('Host', self.host),
('Connection', 'Upgrade'),
('Upgrade', 'WebSocket'),
('Sec-WebSocket-Key', self.key.decode('utf-8')),
# Origin is proxyed from the downstream server, don't set it twice
# ('Origin', self.url),
('Sec-WebSocket-Version', str(max(WS_VERSION)))
]
if self.protocols:
headers.append(('Sec-WebSocket-Protocol', ','.join(self.protocols)))
if self.extra_headers:
headers.extend(self.extra_headers)
logger.info("Handshake headers: %s", headers)
return headers
def received_message(self, m):
"""Push upstream messages to downstream."""
# TODO: No support for binary messages
m = str(m)
logger.debug("Incoming upstream WS: %s", m)
uwsgi.websocket_send(m)
logger.debug("Send ok")
def handshake_ok(self):
"""
Called when the upgrade handshake has completed
successfully.
Starts the client's thread.
"""
self.run()
def terminate(self):
super(ProxyClient, self).terminate()
def run(self):
"""Combine async uwsgi message loop with ws4py message loop.
TODO: This could do some serious optimizations and behave asynchronously correct instead of just sleep().
"""
self.sock.setblocking(False)
try:
while not self.terminated:
logger.debug("Doing nothing")
time.sleep(0.050)
logger.debug("Asking for downstream msg")
msg = uwsgi.websocket_recv_nb()
if msg:
logger.debug("Incoming downstream WS: %s", msg)
self.send(msg)
s = self.stream
self.opened()
logger.debug("Asking for upstream msg")
try:
bytes = self.sock.recv(self.reading_buffer_size)
if bytes:
self.process(bytes)
except BlockingIOError:
pass
except Exception as e:
logger.exception(e)
finally:
logger.info("Terminating WS proxy loop")
self.terminate()
def serve_websocket(request, port):
"""Start UWSGI websocket loop and proxy."""
env = request.environ
# Send HTTP response 101 Switch Protocol downstream
uwsgi.websocket_handshake(env['HTTP_SEC_WEBSOCKET_KEY'], env.get('HTTP_ORIGIN', ''))
# Map the websocket URL to the upstream localhost:4000x Notebook instance
parts = urlparse(request.url)
parts = parts._replace(scheme="ws", netloc="localhost:{}".format(port))
url = urlunparse(parts)
# Proxy initial connection headers
headers = [(header, value) for header, value in request.headers.items() if header.lower() in CAPTURE_CONNECT_HEADERS]
logger.info("Connecting to upstream websockets: %s, headers: %s", url, headers)
ws = ProxyClient(url, headers=headers)
ws.connect()
# TODO: Will complain loudly about already send headers - how to abort?
return httpexceptions.HTTPOk()
嗨,Mikko,谢谢你的回答。事实上,经过一番挖掘,我意识到我的问题并不是我真正想问的。因此,我对在外部应用程序中重用iPython的
comm
对象与前端通信的经验非常好奇。话虽如此,您的答案仍然非常有用,并提供了在过程中出现的其他问题的答案,因此我将接受它并创建一个新问题。