The Substrates API makes it painless to create observability Circuits, where Events from one type of Instrument, resulting from client calls, can be consumed and reinterpreted as an operation on another type of Instrument. This benefits instrumentation agents and self-adaptive applications that use such Events to change call paths and adjust self-regulating mechanisms.
In this post, we walk through one of the showcases in the project’s code repository that demonstrates how the complexity of hooking up components in a Circuit is greatly simplified. In the example, there are two basic types of Instruments. The first type of Instrument, considered a sensor, allows a client to publish an int value. The second Instrument type, considered an observer, publishes a boolean value, a flag, which could be an indicator of sorts within a control panel. An Outlet will be the glue that consumes an Event from the sensor Instrument and reinterprets it using a predicate before forwarding it to the observer Instrument.
Circuit
The first step is to create a Circuit; both Conduits and the Instruments they manage will share this Circuit in their Event publishing and processing. Like Thread pools for service request scaling, Circuits are blocking buildings for pipeline scaling.
1 2 3 4 5 6 |
final var circuit = cortex.circuit ( "Outlets" ); |
With the Circuit created, the first Conduit, representing a sensory level, can be created. Generally, a Conduit is created with a specific Instrument type, such as a Counter, Timer, Probe, Valve, etc. Here a built-in type, Publisher, is used for conciseness.
1 2 3 4 5 6 7 8 9 |
final var sensors = circuit.conduit ( "Sensors", cortex.publishers ( Integer.class ) ); |
The second Conduit is created similarly but with the Event emittance class type changing from Integer to Boolean.
1 2 3 4 5 6 7 8 9 |
final var observers = circuit.conduit ( "Observers", cortex.publishers ( Boolean.class ) ); |
Subscriber
Outlet
With both Conduits created, a Subscriber can now subscribe to the first Conduit, the sensors, that will create a corresponding Instrument with the Conduit managing the observers whenever a sensor is created. The Subscriber also registers an Outlet with each sensor Instrument created. The Outlet is the relay within the circuitry in that it takes the output from a named sensor. It transforms it into an input to the same named observer, going from a raw sensor int input to a derived observer boolean output.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
sensors .source () .subscribe ( ( subject, registrar ) -> { final var observer = observers.instrument ( subject .reference () .name () ); registrar.register ( event -> observer.emit ( event.emission () > 0 ) ); } ); |
Instrument
Event
All that is left now is to feed data into the input layer of the Circuit by creating a named Instrument from the sensors named Conduit and emitting values. The following code will result in four Events following through the pipeline of the Circuit.
1 2 3 4 5 6 7 8 9 |
final var sensor = sensors.instrument ( "sensor" ); sensor.emit ( 1 ); sensor.emit ( 0 ); |
Registering an Outlet with both Conduits that prints the Subject and emittance of an Event results in the following output.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Event: 1 Subject: Circuit[Outlets]#282675f0-823d-42db-9665-8d0521a99ce9/Conduit[Sensors]#f3633089-31fb-4504-ab99-08dc1c479841/Instrument[sensor]#21a9b95f-b337-423b-bee3-5edaee250028 Object: 1 Event: 2 Subject: Circuit[Outlets]#282675f0-823d-42db-9665-8d0521a99ce9/Conduit[Sensors]#f3633089-31fb-4504-ab99-08dc1c479841/Instrument[sensor]#21a9b95f-b337-423b-bee3-5edaee250028 Object: 0 Event: 3 Subject: Circuit[Outlets]#282675f0-823d-42db-9665-8d0521a99ce9/Conduit[Observers]#207f0beb-4f6c-4d48-8c02-cc15d5c0f93a/Instrument[sensor]#50894869-f07b-420f-ada8-ff96d799c8ef Object: true Event: 4 Subject: Circuit[Outlets]#282675f0-823d-42db-9665-8d0521a99ce9/Conduit[Observers]#207f0beb-4f6c-4d48-8c02-cc15d5c0f93a/Instrument[sensor]#50894869-f07b-420f-ada8-ff96d799c8ef Object: false |