Space Based Microservices — @ShayHassidim
Martin Fowler and James Lewis’s microservices paper has been influencing many development teams and projects around the world. As such, this software architecture style has gained prominence among web-scale startups and enterprises.
In this post, we’ll look at how microservices related to SBA and GigaSpaces XAP. We will review the different principles microservices lays out and describe how these fit into the SBA model.
What Is SBA?
Space-based architecture (SBA) is a software architecture pattern for achieving linear scalability of stateful, high-performance applications using the tuple space paradigm. It follows many of the principles of representational state transfer (REST), service-oriented architecture (SOA), and event-driven architecture (EDA), as well as elements of grid computing. With a SBA, applications are built out of a set of self-sufficient units, known as processing-units (PU) or elastic processing-units (EPU). These units are independent of each other, so the application can scale by adding more units (called also “instances”).
The XAP PU is the unit of scalability and failover. The PU supports Java , .Net, and C++. XAP allows you to deploy multiple independent PUs into the same grid or deploy a composition of PUs that have some inter-dependency. In such a case , XAP will orchestrate the PUs provisioning to have the right deploy, healing, and scaling order.
Separation of Concerns
XAP PU promotes developing and deploying applications composed of autonomous, self-contained units — very similar to microservice architecture. The PU concept provides functional separation of concern, where the deployed PUs are mostly independent, each having its own lifecycle scaling behavior, while exposing well known public interface via remote methods or a web service.
As microservices, the PUs are fine-grained units of execution. They are designed to do one thing very well. For example, a PU may encapsulate a web service (Web PU) that exposes a well-defined functionality with a well-known entry point, packaged as one unit of execution. It may also have a space instance (in-memory data grid node); this will make it a stateful PU, where it can preserve its state. A stateful PU is usually deployed with a backup instance, which makes it highly-available to survive failures without any data loss.
A PU as a microservice should adhere to being an atomic business entity, which must implement everything to achieve the desired business functionality. For optimized cross microservices communication, a map-reduce approach should be used.
An application deployed on a cloud platform, when handling a cloud instance failure, produces higher operational costs and system design complexities. This often leads to a general lack of confidence in cloud platforms, and eventual disagreement among the technical team when considering the application for cloud deployment. To deal with these symptoms, a more modular system that is loosely coupled and can be changed or maintained in parts is needed, to prevent downtime during failures.
Microservices deployment on a cloud environment is very popular. As the cloud is a very dynamic, sometimes unpredictable environment, deploying self-sufficient components as microservices becomes an important ingredient for a healthy system. XAP inherently delivers this functionality as each PU includes all the third-party components and resources it needs to be self-sufficient. XAP, together with its Cloudify blueprint, can be deployed on any cloud environment, leveraging the cloud ability to provision compute resources on-demand. This allows XAP to deliver continuous availability with zero downtime for the deployed PU where Cloudify handles the full automation of the PU lifecycle, leading to true consistency, durability, and scalability.
Microservices communicate with each other through language and platform-agnostic application programming interfaces (APIs). With XAP, microservices may use the following options:
• Proactive communication: Microservices sharing their state by writing it into the space. This data is available for other microservices by proactively reading it. For faster data sharing, microservices can run a client side cache that will store frequently used data.
• Reactive communication: Microservices registering for specific data they are interested in. When the desired data is written into the space by microservices, the registered microservices receive notification with the interested data.
In both cases the microservices are loosely coupled with each other, avoiding synchronous and blocking-calls whenever possible.
Microservices configuration with XAP was inspired from popular SW concepts and libraries such as Jini, Rio, Spring, Hibernate, Tomcat, etc. For Java users, a microservice will usually describe itself via a spring-based configuration file called pu.xml. It will compose Beans definition that will include the microservice internal artefacts and their dependency. The Beans will follow the regular life-cycle scheme spring provides with a few additional events provided with XAP such microservice (PU) creation and moving into backup or primary mode.
XAP provides users the freedom to deploy different types of PUs. These are usually divided into stateless and stateful PUs. Stateless PUs use another service (database, space, file) to hold their state. This could be a web PU or a remote space service that is acting as a front-end to the users. Stateless PUs can hold a local cache or local view that hold a copy of a state where its golden copy is preserved within another stateful PU. A stateless PU is usually deployed using a cluster topology without backups, as there is no state to maintain.
Stateful PUs would be any PU that embeds a Space component, usually deployed with primary-backup cluster topology. It may hold user-defined business logic such as event handlers or other collocated logic called explicitly or implicitly.
Since most microservices implementations might have a front-end web service end-point, the Stateless PU running web container within the XAP grid such as Jetty or Tomcat can be very useful.
There are multiple data processing options with XAP, all of which support microservices very well. You may have reactive-based processing or proactive. Each may be deployed as a microservice. Reactive-based processing are implemented using the different event handlers — polling, notify, and archive container. These are usually collocated with each microservice instance (stateful PU) for maximum performance and minimal network utilization, which avoids expensive serialization.
Proactive processing usually performs map-reduce across the entire data grid nodes. This would invoke business logic across primary data grid nodes, where each returns an intermediate result that is reduced and aggregated and passed to the caller.
XAP partitioning (routing) implicitly uses the space class ID (key) by default to distribute the space objects across the different data grid partitions. Users can specify any other space class field as the routing field. This allows users to collocate different objects to reside within the same partition, avoiding extra remote calls when accessing multiple related space objects and the usage of distributed transactions.
Partitioning is fundamental to any stateful system and it’s the primary reason why XAP provides scalability when it comes to data access and data processing. You can always spread the load across additional/fewer machines. You can add more machines to run the grid manually or dynamically (EPU).
Evolving Service Contracts
Microservices promotes evolving service contracts. This concept allows the microservice interface to be modified without any downtime. The PU supports this via hot deploy process. Compile and build your microservices with the newly added interfaces and run the hot deploy tool. This will use a waterfall approach where backup instances will be updated, switched to primary mode, and later the new backups that were previously primary will be upgraded as well. When this process completes, all the instances of a deployed PU will be refreshed running the latest business logic.
Global HTTP Session
Microservices is about breaking the application into manageable self-sufficient services. With large scale web applications, users often get to the point where their monolithic application has broken down into multiple logical components as the effort to test the entire web application with every change is prohibitive.
For Java application (J2E), this means multiple different ear or war files that need to be deployed where all need to share the same HTTP session. This means the HTTP session should be replicated across all these different deployed components. Usually these are running on different JVMs and different machines — sometimes deployed across different web containers from different vendors. Many times the HTTP session has not been designed to support replication as it is not serializable or it is bound to some external resource such as a database. Making it distributed, shared across multiple parties without modifying the application is a major burden. XAP Global HTTP Session is addressing all these issues — it can handle non-serializable content preserved within the HTTP session.
Operating system-level virtualization or “container technology” such as Docker, Rocket, and LXD offer portability of code across multiple environments. Developers are able to move code written on their development machines seamlessly across virtual machines, private cloud, and public cloud. Each running container provides everything from an operating system to the code responsible for executing a task. XAP, as a JVM-based technology, offers its own container, called GSC, to act as the runtime environment of the deployed PU. XAP GSC does not include the operating system, but the PU lifecycle management infrastructure. XAP supports multi-tenancy, allowing multiple PUs to be provisioned within the same GSC sharing the same JVM resources, or complete isolation having a dedicated GSC per PU instance.
Microservices applications should cover all the service functionality, security, performance, error handling, and consumption-driven testing for every current and future consumer. This must be included as part of the build pipeline for automated regression testing. XAP PU can have unit tests developed that will mimic any data grid cluster topology leveraging the same pu.xml used when deploying the PU into production environment.
The IPUC for Java users has been designed allow users running their PU within their IDE and also to be used with Unit Tests. This allows users to simulate a complete PU (specific PU instance or a complete cluster) to be provisioned within the single JVM. When testing microservices-based applications, this makes the Unit testing very simple.
The XAP Maven Plugin provides similar functionality as it allows users to build, deploy, and run a PU.
With microservices, an API gateway is used to orchestrate the cross-functional microservices that may reduce round trips for web applications.
API gateways are primarily used to Insulate the clients from how the application is partitioned into microservices and to Insulate the clients from the problem of determining the locations of service instances. It can also reduce the number of requests/roundtrips. For example, the API gateway enables clients to retrieve data from multiple services with a single roundtrip. Fewer requests also means less overhead and improves the user experience.
With XAP, an API gateway will be implemented as a web PU exposing Rest API deployed into XAP. You may use Jetty or Tomcat as the web container that will be running within the XAP Grid. The web PU can scale dynamically to increase/decrease its capacity in runtime without any downtime.
Microservices compels developers to choose best-of-breed languages, frameworks, and tools to write parts of applications. XAP provides developers the ability to deploy .Net , Java , Groovy, Scala, and C++ PUs into the same grid and share data between them without any special conversion. This allow each microservice to be written in a language that is best suited for the task.
XAP includes multiple components that will provide deployed stateless or stateful microservices ability to tolerate failure: These are event handlers, exception handlers, transactions, active-election, and split-brain solution.
Microservices should support Reentrancy. This means it can be interrupted in the middle of its execution and then safely called again (“reentered”) before its previous invocations complete execution. With XAP, use transaction semantic with the business logic that interacts with the data grid. Since XAP supports a spring transaction framework, the effort executing transactional logic is very minimal. Simply annotate the relevant method or class with the standard @Transactional, inject the XAP transaction manager, and you’re done.
Services need to be architected assuming that change is inevitable. Have a strategy to manage the forward-compatible service changes and allow your consumers to upgrade gracefully. The hot deploy utility will allow you to refresh existing running PU that includes your business logic with a new version without un-deploying the PU. For a stateful PU this is critical as it allows you to update any collocated business logic (i.e. polling/notify container) without any downtime to the data grid.
Building a Gateway in Every Service
To avoid tangling and over-complicated multi-hierarchy inter-communication between the front-end web service layer and the different microservices, the space should be used as a common shared memory and communication fabric that the different microservices should be utilizing.
You may collocate the space with the microservice that performs the most intensive interaction with the space and let the other microservices use it via remote communication. This is a very common approach. Another approach would be to have a dedicated microservice that will run the space only, and satellite microservices that may have local cache / local view maintaining a copy of the shared data.
Microservices promote a polyglot strategy for code and databases. With XAP, you can have a dedicated space for each microservice — usually having the heavy lifting business logic performed by the microservice collocated with the space itself. These different microservices can leverage a different database for persistence or share the same database. This makes the database administration effort simpler.
With XAP, persistence is configured usually using a write-behind approach (aka Mirror Service). This write-behind activity would allow the application to operate if the underlying database is slow or even if the database failed or is under maintenance activity. Once the database is online, all the operations that were logged will be propagated to the database.
Thread pool and connection pool management are required with any distributed web scale architecture. XAP provides (client and server-side configuration) LRMI protocol that allows you to set boundaries for amount of resources a client or server side components should use. Since microservices can be both a client or a server-side component, such resource control settings are very useful.
Microservices promote the usage of asynchronous calls. With SBA these should be implemented via the various asynchronous data access API provided: async Notifications, async Task execution, async remote method invocation, async write, async read, async take, and async clear. These allow your application to leverage non-blocking architecture when interacting with the space.
Microservices should support elasticity. This means the capacity of the microservice to handle incoming requests or data to store should grow or shrink without any downtime. This can happen automatically based on pre-defined SLA or manually. XAP supports this capability both for stateful and stateless PU.
With microservices, the culture and design principles should embrace failure and faults, similar to anti-fragile systems. Developers and operators can develop and deploy self-healing applications. XAP delivers high availability by keeping every item stored within the data grid within at least two different machines. This redundancy will be kept as long as there are available resources on the network. Upon failure of a data grid node, a new PU instance running the missing data grid node will be provisioned, keeping the capacity of the deployed stateful PU intact.
Semantic monitoring can provide an early warning system of something going wrong that triggers development teams to follow up and investigate. XAP includes advanced monitoring and alerting tools that may introspect any component within the system. This includes the grid and its hosting machines, the PU, and its stateful (space) or stateless artefacts. You may configure custom dashboards that will allow you to correlate different metrics. These may be both operational and business-relevant metrics. This provides a simple resolution path when problems are identified. You may also push metrics available into external monitoring tools such as CA APM to enterprise-level monitoring.
Microservice architectures emphasize transaction-less coordination between services, with explicit recognition that consistency may only be eventual consistency and problems are dealt with by compensating operations. With XAP, the recommendation is to leverage transactions as XAP has been built to support strict consistency. You can use eventual consistency with XAP (asynchronous replication, optimistic locking protocol), but this may impact accuracy and may lead to data loss.
Developing and deploying microservices with XAP should adhere to several straightforward concepts. Many of these are classic concepts that do not require special changes to the regular development and deployment cycle you may have. Sticking to these concepts will deliver top quality, a great end-user experience, and an agile, cost-effective, robust, scalable, and simple-to-maintain application.