OSGi – Open Services Gateway Initiative. Quote from the official site of the OSGi Alliance: “The OSGi technology is a set of specifications that define a dynamic component system for Java. These specifications enable a development model where applications are (dynamically) composed of many different (reusable) components.”(c)
As usual I will not give a detailed description of the technology as you can find overviews for beginners in internet as well as the most delicious part – official specifications here: OSGi specs. Instead I will give you the essence of OSGi from the architectural point of view. Let me use IoT Events Engine (IoTEE) software as a complex live example (I’ve introduced it to you in IoTEE introduction) instead of thinking up fake basic use cases each time.
In general each IoT solution can be seen as an Event Driven System (see EDA) as the core value of IoT is communication with remote endpoints, collecting data from them and reacting to events. IoTEE is designed as a service which processes events from different sources in real-time and executes business logic according to dynamically defined rules. So, globally the core pattern behind this is [Micro]services: instead of implementing a (huge, complex thus error prone) piece of software which manages connections to devices via different protocols, receives events, processes them it’s suggested to go alternative way and have 2 loosely coupled services – one for managing protocol-dependent communications with devices and another (protocol agnostic IoTEE) for processing events. This approach provides you deployment flexibility, makes your solution scalable, enhances reliability, allows to perform upgrades more easily.
Now let’s look at the IoTEE service in a more detail pretending like its architecture is still to be defined. Requirements were:
- Be able to process events from several heterogenous sources in a common way in parallel
- Provide possibility to add/remove pollers in run time
- Provide possibility to create and load/unload rules for events processing in run-time
- Provide possibility to change business logic linked to the rule in run time
- Provide common mechanism for (re)configuration of components in run time
- Provide means for passing events between sub-components
- Guarantee scalability of the service
- Support 2 modes of operation: standalone instance and cluster
- Allow flexibility in cluster configuration (make all instances identical; have different pollers running on different instances or mixed mode)
- Have admin Web portal for managing Rules, Actions, Pollers, monitoring logs
- Provide possibility to change implementation of the main components in run time:
- Events Pollers (pluggable components responsible for polling events from different event sources)
- Rules Engine (component implementing the rules logic and processing events with rules)
- Actions Executor (component encapsulating functionality for executing business logic on rules trigger)
- Frontend (IoTEE Web interface)
Seems that we can follow initial approach and also divide a single service into a set of micro services – let’s try this out. For the simplest case everything looks quite pretty:
Just 2 moments:
We need a central management portal according to requirements plus because when creating Rules in the R service we should know available Actions from the A service
Services are independent, so each should have its own (re)configuration mechanism, maybe centrally managed from M as an option
Not a problem, we’ll get the following:
It becomes more interesting, when we try to satisfy other requirements. Our imagination can draw picture like this:
Where we have logical clusters of services of each type, dynamically extendable, loosely coupled with each other, where services in the cluster are also independent & lightweight so that they can be easily added/removed via some orchestration framework to adjust capacity.
As usual reality makes some adjustments:
In general events sources may not support parallel reading of events from them, so some sync mechanism is needed for pollers of each type to avoid collisions.
R services are stateful by definition, this means you can’t just add one more and thus scale – you also need to synchronise their state.
You will be trying to use your servers’ resources most efficiently, so you’ll use several physical machines but you will definitely colocate your services there.
With these notes in mind we’re getting something like this as a minimum:
Here each service is deployed independently of the others most likely as a Docker container, services communicate via HTTP REST APIs. Services of each type form a cluster which spans all VMs/Servers (2 on the picture). For simplicity this is not shown. We’ll have to implement synchronisation mechanism for each service of type Pi, also for services of type R. P services are not able to just send events to all available R services, in this case we’ll get duplicate events processing – we either need a proxy for Rs or an events distribution algorithm for Ps. Plus we’ll require additional monitoring to determine service failures. Of course, it’s a matter of taste, but for me solution looks neither simple nor elegant. And don’t forget that services co-located on a single server still communicate via relatively slow HTTP. I’m not saying this can’t be implemented or will not work, I just doubt whether this is an optimal approach.
As an alternative let’s try IoTEE service design based on OSGi (recall we’re still pretending we haven’t done this yet). Ok, let’s dive into the world of OSGi
after advertisement in the next post. Stick around!