You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Improved the I/O architecture documentation. (#13558)
Small cosmetic fixes.
Expanded the Sink.write() section with one more example.
Added SelectorManager diagram.
Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
Copy file name to clipboardExpand all lines: documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/ContentDocs.java
Copy file name to clipboardExpand all lines: documentation/jetty/modules/programming-guide/pages/arch/io.adoc
+49-9Lines changed: 49 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,9 +21,41 @@ The Jetty libraries (both client and server) use Java NIO to handle I/O, so that
21
21
The main class of The Jetty I/O library is link:{javadoc-url}/org/eclipse/jetty/io/SelectorManager.html[`SelectorManager`].
22
22
23
23
`SelectorManager` manages internally a configurable number of link:{javadoc-url}/org/eclipse/jetty/io/ManagedSelector.html[`ManagedSelector`]s.
24
-
Each `ManagedSelector` wraps an instance of `java.nio.channels.Selector` that in turn manages a number of `java.nio.channels.SocketChannel` instances.
24
+
Each `ManagedSelector` wraps an instance of `java.nio.channels.Selector` that in turn manages a number of `java.nio.channels.SelectionKey` instances, that each wrap a `java.nio.channels.SelectableChannel` instance such as `java.nio.channels.SocketChannel`.
25
25
26
-
NOTE: TODO: add image
26
+
[plantuml]
27
+
----
28
+
object SM as "SelectorManager"
29
+
object MS1 as "ManagedSelector" {
30
+
java.nio.channels.Selector
31
+
}
32
+
object MS2 as "ManagedSelector" {
33
+
java.nio.channels.Selector
34
+
}
35
+
object SC1A as "SelectionKey" {
36
+
SelectableChannel
37
+
}
38
+
object SC1B as "SelectionKey" {
39
+
SelectableChannel
40
+
}
41
+
object SC2A as "SelectionKey" {
42
+
SelectableChannel
43
+
}
44
+
object SC2B as "SelectionKey" {
45
+
SelectableChannel
46
+
}
47
+
object SC2C as "SelectionKey" {
48
+
SelectableChannel
49
+
}
50
+
51
+
SM o-- MS1
52
+
SM o-- MS2
53
+
MS1 o-- SC1A
54
+
MS1 o-- SC1B
55
+
MS2 o-- SC2A
56
+
MS2 o-- SC2B
57
+
MS2 o-- SC2C
58
+
----
27
59
28
60
`SocketChannel` instances are typically created by the Jetty implementation, on client-side when connecting to a server and on server-side when accepting connections from clients.
29
61
In both cases the `SocketChannel` instance is passed to `SelectorManager` (which passes it to `ManagedSelector` and eventually to `java.nio.channels.Selector`) to be registered for use within Jetty.
@@ -90,7 +122,7 @@ In the Jetty I/O library, you can call `EndPoint.fillInterested(Callback)` to de
90
122
At the Java NIO level, a `SocketChannel` or `DatagramChannel` is always writable, unless it becomes congested.
91
123
In order to be notified when a channel uncongests and it is therefore writable again, the `SelectionKey.OP_WRITE` flag must be set.
92
124
93
-
In the Jetty I/O library, you can call `EndPoint.write(Callback, ByteBuffer...)` to write the ``ByteBuffer``s and the `Callback` parameter is the object that is notified when the whole write is finished (i.e. _all_ ``ByteBuffer``s have been fully written, even if they are delayed by congestion/uncongestion).
125
+
In the Jetty I/O library, you can call `EndPoint.write(Callback, ByteBuffer\...)` to write the ``ByteBuffer``s and the `Callback` parameter is the object that is notified when the whole write is finished (i.e. _all_ ``ByteBuffer``s have been fully written, even if they are delayed by congestion/uncongestion).
94
126
95
127
The `EndPoint` APIs abstract out the Java NIO details by providing non-blocking APIs based on `Callback` objects for I/O operations.
96
128
The `EndPoint` APIs are typically called by `Connection` implementations, see <<connection,this section>>.
@@ -176,7 +208,7 @@ Submitting the invocation of the callback to an `Executor` to be invoked in a di
176
208
177
209
This side effect of asynchronous programming leading to `StackOverflowError` is so common that the Jetty libraries have a generic solution for it: a specialized `Callback` implementation named `org.eclipse.jetty.util.IteratingCallback` that turns recursion into iteration, therefore avoiding the `StackOverflowError`.
178
210
179
-
`IteratingCallback` is a `Callback` implementation that should be passed to non-blocking APIs such as `EndPoint.write(Callback, ByteBuffer...)` when they are performed in a loop.
211
+
`IteratingCallback` is a `Callback` implementation that should be passed to non-blocking APIs such as `EndPoint.write(Callback, ByteBuffer\...)` when they are performed in a loop.
180
212
181
213
`IteratingCallback` works by starting the loop with `IteratingCallback.iterate()`.
182
214
In turn, this calls `IteratingCallback.process()`, an abstract method that must be implemented with the code that should be executed for each loop.
When `onFillable()` is called, for example the first time that bytes are available from the network, the iteration is started.
200
-
Starting the iteration calls `process()`, where a buffer is allocated and filled with bytes read from the network via `EndPoint.fill(ByteBuffer)`; the buffer is subsequently written back via `EndPoint.write(Callback, ByteBuffer...)` -- note that the callback passed to `EndPoint.write()` is `this`, i.e. the `IteratingCallback` itself; finally `Action.SCHEDULED` is returned, returning from the `process()` method.
232
+
Starting the iteration calls `process()`, where a buffer is allocated and filled with bytes read from the network via `EndPoint.fill(ByteBuffer)`; the buffer is subsequently written back via `EndPoint.write(Callback, ByteBuffer\...)` -- note that the callback passed to `EndPoint.write()` is `this`, i.e. the `IteratingCallback` itself; finally `Action.SCHEDULED` is returned, returning from the `process()` method.
201
233
202
-
At this point, the call to `EndPoint.write(Callback, ByteBuffer...)` may have completed synchronously; `IteratingCallback` would know that and call `process()` again; within `process()`, the buffer has already been allocated so it will be reused, saving further allocations; the buffer will be filled and possibly written again; `Action.SCHEDULED` is returned again, returning again from the `process()` method.
234
+
At this point, the call to `EndPoint.write(Callback, ByteBuffer\...)` may have completed synchronously; `IteratingCallback` would know that and call `process()` again; within `process()`, the buffer has already been allocated so it will be reused, saving further allocations; the buffer will be filled and possibly written again; `Action.SCHEDULED` is returned again, returning again from the `process()` method.
203
235
204
-
At this point, the call to `EndPoint.write(Callback, ByteBuffer...)` may have not completed synchronously, so `IteratingCallback` will not call `process()` again; the processing thread is free to return to the Jetty I/O system where it may be put back into the thread pool.
236
+
At this point, the call to `EndPoint.write(Callback, ByteBuffer\...)` may have not completed synchronously, so `IteratingCallback` will not call `process()` again; the processing thread is free to return to the Jetty I/O system where it may be put back into the thread pool.
205
237
If this was the only active network connection, the system would now be idle, with no threads blocked, waiting that the `write()` completes. This thread-less wait is one of the most important features that make non-blocking asynchronous servers more scalable: they use less resources.
206
238
207
239
Eventually, the Jetty I/O system will notify that the `write()` completed; this notifies the `IteratingCallback` that can now resume the loop and call `process()` again.
When you need to perform an unknown number of writes, you may use an `IteratingCallback`, explained in <<echo,this section>>, to avoid ``StackOverFlowError``s.
362
394
363
-
For example, to copy from a `Content.Source` to a `Content.Sink` you could use the convenience method `Content.copy(Content.Source, Content.Sink, Callback)`.
364
-
For illustrative purposes, below you can find the implementation of `copy(Content.Source, Content.Sink, Callback)` that uses an `IteratingCallback`:
395
+
For example, to download a large content in smaller chunks, you can do this:
If you want to copy from a `Content.Source` to a `Content.Sink` you could use the convenience method `Content.copy(Content.Source, Content.Sink, Callback)`.
403
+
404
+
For illustrative purposes, below you can find a more elaborate example with an implementation of `copy(Content.Source, Content.Sink, Callback)` that uses `IteratingCallback`:
0 commit comments