|
1 | 1 | .. _websocket_protocols: |
2 | 2 |
|
3 | | -WebSocket kernel wire protocols |
4 | | -=============================== |
5 | 3 |
|
6 | | -The Jupyter Server needs to pass messages between kernels and the Jupyter web application. Kernels use ZeroMQ sockets, and the web application uses a WebSocket. |
| 4 | +Kernel Messages |
| 5 | +=============== |
| 6 | + |
| 7 | +When a kernel is created or connected to via the `REST API |
| 8 | +<https://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/jupyter_server/master/jupyter_server/services/api/api.yaml>`__, |
| 9 | +the Jupyter server sets up a websocket to ZeroMQ bridge for communicating |
| 10 | +between the browser and the kernel. When the server connects to a kernel, the |
| 11 | +server sends a ``request_kernel_info`` messages to retrieve the kernel message |
| 12 | +spec version the kernel implements. The server automatically adapts messages |
| 13 | +from the kernel spec version the kernel implements to the kernel spec |
| 14 | +implemented by the current version of jupyter_client installed. |
| 15 | + |
| 16 | +Restarting or shutting down a kernel should be done with a REST request to the |
| 17 | +server, not through a kernel message, so that the kernel manager can do the |
| 18 | +appropriate logic around kernel shutdown, like asking the kernel to shut down |
| 19 | +first through a kernel message, then forcefully shutting down the kernel if |
| 20 | +there is no response. |
| 21 | + |
| 22 | +Kernel messages |
| 23 | +--------------- |
| 24 | + |
| 25 | +A websocket client connecting to a kernel through the Jupyter server websocket |
| 26 | +bridge will use messages according to the Jupyter kernel message spec, with the |
| 27 | +modifications noted below. |
| 28 | + |
| 29 | +Kernel Message Specification Changes |
| 30 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 31 | + |
| 32 | +**Message Channels** |
| 33 | + |
| 34 | +The notebook server multiplexes all kernel message channels into a single |
| 35 | +websocket channel and encodes the channel name in the websocket message wire |
| 36 | +format (see below). |
| 37 | + |
| 38 | +**Kernel Status** |
| 39 | + |
| 40 | +The Jupyter server sends several additional `kernel status <https://jupyter-client.readthedocs.io/en/stable/messaging.html#kernel-status>`__ messages in addition |
| 41 | +to the kernel status messages that are sent by the kernel itself: |
| 42 | + |
| 43 | +1. When a kernel is restarted, an ``execution_state: 'restarting'`` kernel status message is sent. |
| 44 | +2. When a kernel dies, an ``execution_state: 'dead'`` kernel status message is sent. |
| 45 | + |
| 46 | +These status messages will have a different message header ``session`` value |
| 47 | +than the message header session values in messages from the kernel. |
| 48 | + |
| 49 | +.. note:: |
| 50 | + |
| 51 | + As an implementation detail, the message header session value of these |
| 52 | + messages matches the `session_id` in the kernel connection URL. |
| 53 | + |
| 54 | +.. note:: |
| 55 | + |
| 56 | + In the classic notebook and JupyterLab client code, the websocket connection |
| 57 | + is closed when explicitly requesting a restart or shutdown, so the |
| 58 | + restarting and dead messages aren't received if it was requested by the |
| 59 | + user. In those cases, receiving a ``restarting`` or ``dead`` message from |
| 60 | + the notebook server means that the kernel had something happen to it, and |
| 61 | + the user should be explicitly notified. |
| 62 | + |
| 63 | +**IOpub Message Rate Limits** |
| 64 | + |
| 65 | +The notebook server inspects the messages coming from the kernel to the client |
| 66 | +to rate-limit iopub messages. These rate limits can be raised. |
| 67 | + |
| 68 | + |
| 69 | +Buffering |
| 70 | +~~~~~~~~~ |
| 71 | + |
| 72 | +If all websocket clients have disconnected from a kernel, the notebook server |
| 73 | +will temporarily buffer messages from the kernel to be delivered to the first |
| 74 | +websocket client that connects to the kernel. |
| 75 | + |
| 76 | + |
| 77 | + |
| 78 | +.. note:: |
| 79 | + |
| 80 | + In the classic notebook client and JupyterLab, requesting a kernel restart |
| 81 | + immediately closes all websocket connections to the kernel, so kernel |
| 82 | + buffering starts. When a new websocket connection is created connecting to |
| 83 | + the kernel, the notebook server transmits all of the messages buffered from |
| 84 | + the kernel. For the IPython kernel, this means the new websocket connection |
| 85 | + will start with receiving status busy, shutdown_reply, and status idle |
| 86 | + messages on the iopub channel from before the restart. |
| 87 | + |
| 88 | +.. note:: |
| 89 | + |
| 90 | + TODO |
| 91 | + |
| 92 | + Document the session URL parameter used in kernel connections. Is that |
| 93 | + created every time we request a kernel with a post request? Is it tied to |
| 94 | + just creating new sessions with the session rest api? |
| 95 | + |
| 96 | + |
| 97 | +Wire protocol |
| 98 | +------------- |
| 99 | + |
| 100 | +Jupyter Server translates messages between the ZeroMQ channels connected to a |
| 101 | +kernel and the websocket connection to the browser. The wire formats for these |
| 102 | +messages is as follows. |
7 | 103 |
|
8 | 104 | ZeroMQ wire protocol |
9 | | --------------------- |
| 105 | +~~~~~~~~~~~~~~~~~~~~ |
10 | 106 |
|
11 | 107 | The kernel wire protocol over ZeroMQ takes advantage of multipart messages, |
12 | 108 | allowing to decompose a message into parts and to send and receive them |
@@ -38,12 +134,12 @@ See also the `Jupyter Client documentation <https://jupyter-client.readthedocs.i |
38 | 134 | Note that a set of ZeroMQ sockets, one for each channel (shell, iopub, etc.), are multiplexed into one WebSocket. Thus, the channel name must be encoded in WebSocket messages. |
39 | 135 |
|
40 | 136 | WebSocket protocol negotiation |
41 | | ------------------------------- |
| 137 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
42 | 138 |
|
43 | 139 | When opening a WebSocket, the Jupyter web application can optionally provide a list of subprotocols it supports (see e.g. the `MDN documentation <https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#subprotocols>`_). If nothing is provided (empty list), then the Jupyter Server assumes the default protocol will be used. Otherwise, the Jupyter Server must select one of the provided subprotocols, or none of them. If none of them is selected, the Jupyter Server must reply with an empty string, which means that the default protocol will be used. |
44 | 140 |
|
45 | 141 | Default WebSocket protocol |
46 | | --------------------------- |
| 142 | +~~~~~~~~~~~~~~~~~~~~~~~~~~ |
47 | 143 |
|
48 | 144 | The Jupyter Server must support the default protocol, in which a kernel message is serialized over WebSocket as follows: |
49 | 145 |
|
@@ -101,7 +197,7 @@ Then retrieving the channel name, and updating with the buffers, if any: |
101 | 197 | } |
102 | 198 |
|
103 | 199 | ``v1.kernel.websocket.jupyter.org`` protocol |
104 | | --------------------------------------------- |
| 200 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
105 | 201 |
|
106 | 202 | The Jupyter Server can optionally support the ``v1.kernel.websocket.jupyter.org`` protocol, in which a kernel message is serialized over WebSocket as follows: |
107 | 203 |
|
|
0 commit comments