|
| 1 | +# Architecture Docs |
| 2 | + |
| 3 | +## Qpid Proton-J Integration |
| 4 | + |
| 5 | +Qpid Proton-J publishes events and messages via its event-driven process called [`Reactor`][Reactor]. `azure-core-amqp` |
| 6 | +hooks into Qpid Proton-J Reactor via `Handlers`. [`BaseHandler`][BaseHandler] contains all of the events that can be |
| 7 | +listened to. `Handlers` can be associated with classes implementing the `Extendable` interface via |
| 8 | +`BaseHandler.setHandler(Extendable, Handler)`. Proton-J `Connection`, `Session`, and `Link` (`Sender` and `Receiver` |
| 9 | +are specialised instances of `Link`) are all `Extendable`. In `azure-core-amqp`, we create and own an instance of |
| 10 | +`Reactor`. When an AMQP connection, session, or link is created from that `Reactor`, we instantiate and correlate the |
| 11 | +corresponding [`Handler`][Handlers]. This way, we have granular control over what events are listened to for each |
| 12 | +handler, rather than listening to all events available on [`BaseHandler`][BaseHandler]. |
| 13 | + |
| 14 | +The UML diagram below shows this relationship; the interfaces shown in green are Proton-J classes. Each Proton-J |
| 15 | +instance (e.g., `Connection`, `Session`, `Sender`, `Receiver`) is associated with one corresponding `*Handler`. Each |
| 16 | +`azure-core-amqp` `AmqpConnection` is associated with one [`Reactor`][Reactor]. When that instance closes, the AMQP |
| 17 | +connection is also closed. |
| 18 | + |
| 19 | +Each [ReactorConnection][ReactorConnection] has one Proton-J [`Reactor`][Reactor] instance. Each [`Reactor`][Reactor] |
| 20 | +has one [`ReactorDispatcher`][ReactorDispatcher] and one [`ReactorExecutor`][ReactorExecutor]. When a `Reactor` is |
| 21 | +created, a [`SelectableChannel`][SelectableChannel] is associated with this `Reactor`. |
| 22 | +[`ReactorDispatcher`][ReactorDispatcher] is responsible for queueing work on this channel and the work is run in FIFO |
| 23 | +basis. [`ReactorExecutor`][ReactorExecutor] holds onto the `Reactor` instance and continuously processes to work |
| 24 | +SelectableChannel. |
| 25 | + |
| 26 | +![azure-core-amqp integration with Proton-J][AzureCoreAmpqArchitecture] |
| 27 | + |
| 28 | +## Prefetch and AMQP Link Credits |
| 29 | + |
| 30 | +In Project Reactor, prefetch is the initial number of items to request upstream. Afterwards, 75% of the initial prefetch |
| 31 | +is used for subsequent `request(long)`. |
| 32 | + |
| 33 | +In Event Hubs, prefetch is the number of AMQP link credits to put on the link when it is first created. After those |
| 34 | +initial link credits have been consumed, we have different ways of calculating how many credits are added to the link. |
| 35 | + |
| 36 | +The diagram below illustrates how it happens. Things to note: |
| 37 | + |
| 38 | +* Large `EventData` use multiple AMQP link credits because |
| 39 | +* There is no backpressure for [`EventHubConsumerAsyncClient.receiveFromPartition()`][EventHubConsumerAsyncClient]. |
| 40 | +* [`EventHubConsumerAsyncClient.receiveFromPartition()`] returns `EventData` on `Scheduler.single("<name>")`. |
| 41 | + * Since events are not published on another Scheduler, they flow downstream using the Scheduler that |
| 42 | + [`ReceiveLinkHandler.onDelivery`][ReceiveLinkHandler] executed on. |
| 43 | + * All Proton-J events run on the single scheduler because it is not thread-safe. |
| 44 | +* [`EventProcessorClient`][EventProcessorClient] uses back-pressure due to `concatMap` and `publishOn` within its |
| 45 | + [PartitionPumpManager.startPartitionPump][PartitionPumpManager]. |
| 46 | + |
| 47 | +![Flow of credits when receiving deliveries][ReceiveFlowDiagram] |
| 48 | + |
| 49 | +<!-- Links --> |
| 50 | +[BaseHandler]: https://github.com/apache/qpid-proton-j/blob/main/proton-j/src/main/java/org/apache/qpid/proton/engine/BaseHandler.java |
| 51 | +[EventHubConsumerAsyncClient]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventHubConsumerAsyncClient.java#L334 |
| 52 | +[EventProcessorClient]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventProcessorClient.java |
| 53 | +[AzureCoreAmpqArchitecture]: ./architecture-uml.jpeg |
| 54 | +[Handlers]: https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler |
| 55 | +[ReceiveFlowDiagram]: ./receive-flow.jpeg |
| 56 | +[PartitionPumpManager]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/PartitionPumpManager.java#L228 |
| 57 | +[Reactor]: https://github.com/apache/qpid-proton-j/blob/main/proton-j/src/main/java/org/apache/qpid/proton/reactor/Reactor.java |
| 58 | +[SelectableChannel]: https://qpid.apache.org/releases/qpid-proton-j-0.33.5/api/org/apache/qpid/proton/reactor/Selectable.html#setChannel-java.nio.channels.SelectableChannel- |
| 59 | +[ReceiveLinkHandler]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java#L97 |
| 60 | +[ReactorConnection]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java |
| 61 | +[ReactorDispatcher]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorDispatcher.java |
| 62 | +[ReactorExecutor]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorExecutor.java |
0 commit comments