by Michelle Szucs
Advantages, key considerations, and best practices for adopting a microservices architecture
Microservices are modular processes with small scopes; tens or hundreds of microservices can work together to fulfill the functionality of a traditional application. While traditional applications tend to be delimited by the business functions they fulfill, microservices are scoped by smaller, commonly repeated programmatic functions. These services communicate with each other using common protocols, treating each other as black boxes that are accessed via APIs.
The goal of microservice architectures is generally to make the system easier to maintain and to reduce dependence on specific back-end implementations. This implementation flexibility allows both the language implementing the service and the infrastructure on which it is running to change. Due to the black-box nature of microservices, as long as the API implementations remain consistent, back-end implementation does not affect how services communicate with each other.
This flexibility can be fully exploited because the decoupling of application components allows for service-by-service updates. Rather than requiring large releases to incorporate new features, features can be added to each service at will, expanding the API to account for new functionality. Other services can be updated individually to take advantage of these new tools. These smaller, individual codebases fit neatly into modern development practices, providing modularized units of code that can be individually managed and supported.
Comparing Microservices to Service-Oriented Architectures
In many ways, microservices are the modern evolution of service-oriented architectures (SOAs). A service in a SOA application usually has a large scope and encapsulates a full business function that is bundled into a single application. Microservice models abstract this concept further, extending decoupling past application and datastore access.
Network communication between components is a core tenant of both SOA applications and microservices. However, microservice architectures see significantly higher messaging loads, because a single component of a SOA application will usually be broken down into numerous microservices that need to exchange information and make calls to each other.
A key distinguishing factor between SOA applications and microservices is interdependence. The components of SOA applications are interdependent, generally deployed as monolithic applications (that may interact with other SOA applications). Microservices should be independently deployable and scalable, with load balancers to distribute requests between instances of any particular service.
Primary Drivers of Microservice Adoption
Microservices have become popular for a number of reasons, mostly centered on the relative ease of developing and maintaining modular code. Breaking an application into many independent microservices allows the services to be implemented, maintained, and updated separately, which provides advantages in a number of areas.
The implementation independence allows for polyglot application environments, with the language used to implement any particular service adjusted based on considerations such a performance, maintainability, and available libraries and tooling. Specific hardware may also benefit a particular service; an analytics service, for instance, will benefit from running on Oracle's SPARC M7 or SPARC S7 processor to take advantage of the Data Analytics Accelerator units. As needs change, these back-end implementation choices can be updated without affecting other services, as long as the APIs are implemented consistently.
Independent deployment also means that services can scale separately. While scaling monolithic applications requires partial rewrites to manage bottlenecks or creating additional instances of the entire application, microservice architectures grow in a much more finely tuned manner. When a service starts to reach capacity, additional instances can be created. A load balancer should be added to distribute requests between available instances and manage request distribution in case an instance goes down.
In general, microservice architectures point to a more modern method of software development. Building small development teams is easier when application units are of a contained, manageable size. Less coordination between teams is needed for relatively major code changes as long as APIs remain intact. If desired, a team can opt for a continuous integration and continuous deployment model more easily than when supporting a very large application, where releases tend to be highly controlled and carefully orchestrated. Smaller codebases are generally easier to maintain than larger codebases, and when outages do happen, they may affect only a subset of services and, therefore, only a subset of functionality.
Key Considerations When Developing and Deploying Microservices
Several central challenges must be addressed when beginning to adopt microservice architectures. Perhaps one of the biggest hurdles is dealing with the complexity of these architectures. The communication patterns will often grow exponentially as new services are added, making systems increasingly difficult to manage over time. The decentralized nature of development can contribute to this pitfall if no resources are assigned to overseeing the overall system performance. Stepping back for high-level looks at the system can avoid needless complexity and code repetition as the services evolve. Message patterns should be analyzed for inefficiencies, and API requests should be limited to the instances in which they are necessary. Avoiding undersized services can also reduce complexity.
Multiple specialized services running on a single physical system can create competition for physical resources, such as disk and network bandwidth, or for data resources, such as acquiring locks on objects. Maintaining independent datastores and allocating resources at a hardware level (rather than letting services vie for resources on a system that dynamically assigns them) can assist with identifying when services need to scale beyond their existing capacity.
Keeping many services available can pose a problem for balancing resources. Many availability solutions involve the use of other microservices to monitor and deploy replacement services in the case of failure; however, these solutions add to the complexity of the overall system. Hardware solutions, including basic redundancy, for availability should be used whenever possible. In some cases, systems may be best served by designing them to gracefully handle partial failures of components.
Best Practices for Microservices in Existing Environments
Many steps can be taken with existing Oracle applications and application environments to move toward microservice models.
APIs and interfaces require tightly coupled load balancers. Software load balancers that are designed to interface seamlessly with Oracle WebLogic Server—such as Oracle Traffic Director (which comes with some of the options available for Oracle WebLogic Server, Enterprise Edition)—are needed to handle request distribution between service instances.
Containerizing microservices in lightweight virtualization technologies such as Oracle Solaris Zones, which can be packaged into Oracle Solaris Unified Archives for easy portability, allows for lightweight virtualization that does not dedicate a large fraction of system resources to the hypervisor. Because microservices can be treated as black boxes that respond to API requests, virtual machines and containers should be locked down as much as possible. In Oracle Solaris, read-only zones can be used to prevent the application or users from being able to alter the virtual machine.
When possible, microservices should have their own datastores. This additional layer of separation allows for increased decoupling; microservices that rely on the same datastore would have some back-end implementation constraints. Additionally, performance improves when there is no contention for locks on objects between multiple services.
Most microservice architectures, especially early on, will rely on hardware redundancy and tested software solutions, such as Oracle WebLogic Cluster and Oracle Solaris Cluster, to keep services available. These technologies allow services to stay available just as they support full applications.
One of the most important but commonly overlooked aspects of microservice architectures is that performance tuning is perhaps more important than with a monolithic application. Because each microservice should be fulfilling a specialized purpose, it will likely be easier to optimize Java Virtual Machine and Oracle WebLogic Server settings for a microservice than for a large application that performs a number of highly unrelated tasks. Microservices on the same physical hardware should be chosen to balance each other; two network-hungry services will compete for resources and might fare better on separate physical nodes.
Planning for the Future: Making Microservices the Standard
Many existing applications will not evolve quickly into full microservice models, but there are a couple things to keep in mind whether you are evolving an existing application or starting from scratch.
To break an existing application into microservices, consider the current architecture of the application. Common design patterns can be exploited to create the first logical group of services. Many applications use model-view-controller, publisher-subscriber, or listener patterns, which produce a few categories of services that can be further subdivided.
Alternatively, recall that microservices will ideally not share datastores in order to allow services to be asynchronous. If there is already separation between data, consider reinforcing these divisions by building microservices around them.
When possible, protocols should be implemented as RESTful APIs. HTTP is preferred over heavier formats such as JSON and XML, because lightweight protocols will keep network chatter to a minimum. Microservices should also be idempotent. In the context of a RESTful API, this constraint means that making multiple identical API calls will not have any effects beyond those of making a single call. This constraint makes outputs more predictable, which is beneficial when service complexity rises, and can usually be observed by implementing communication with HTTP.
In addition to application architectural decisions, the platform should be carefully considered to reduce application overhead. Consider investing in new cloud-oriented software, such as pluggable databases from Oracle Database 12c and Oracle WebLogic Server Multitenant. These technologies may allow for better scalability and automated deployment of microservices, including simpler transitions between public and private clouds.
Finally, most microservice work tends toward cloud-native applications in the long-term. Cloud-native applications handle availability at a software level, by automatically detecting and failing over to other servers any services that are down. This model sometimes invokes the phrase, "cattle, not pets," referring to the disposable nature of services as long as another instance can be brought up to take a failed service's place. Most enterprise computing of today treats applications like favorite pets that must be sustained through a variety of hardware and software availability mechanisms. The "cattle" model of software development treats services as disposable and easily replaced. Cloud-native applications require careful planning early in the development process; while a cloud-native design is often a goal, it is recognized as difficult to achieve and unrealistic in most enterprises in the immediate future.
A Note on "Nanoservices"
A common pitfall when working with microservices is to inadvertently design and implement what are derisively referred to as "nanoservices." Nanoservice is the name given to a service so small in scope that its functionality would be better served by being absorbed into other services. An overabundance of nanoservices will lead to noisy neighbor problems and needless complexity. Avoiding nanoservices will often result in a slight duplication of code in some places; a fine balance must be found between minimizing network load and avoiding redundant development efforts.
Whether a particular service is a nanoservice is entirely dependent on the infrastructure as well as the overall business function being met. A service may be a nanoservice in one context and an appropriately sized microservice in another. A number of metrics can be implied to determine whether a service's scope is appropriate. Development resources (and whether the service strains or eases those resources) should be considered, along with the service's potential consumption of physical resources.
A simple service that calculated the sum of two numbers would be a nanoservice; the difficulty is striking a balance between this trivial example and the monolithic applications of today. While hundreds if not thousands of services are typical in complex use cases, many companies are starting to find that their architectures tend toward nanoservices as they try to scale out their operations. Inefficiencies are magnified at scale, meaning that architectures should be revisited as deployments grow.
While microservices are a quickly growing area of interest due to the profound positive impact they can have on the development process, many enterprise use cases will be slow to move to a true microservice model due to inherent complexity. While the long-term benefits of well-architected microservice deployments are quite high, conversion can require significant refactoring. For many enterprises, a slow movement toward microservice models may prove the most beneficial. By breaking up the large components of an application, immediate gains in implementation flexibility and codebase manageability can be seen. This work can be done with a number of existing technologies to promote modern development and support practices, while longer-term planning is necessary for more radical change. By blending tested software and hardware components with new approaches to developing and maintaining applications, the benefits of microservices can be realized in familiar environments, reducing risk while providing substantial organizational returns on investment.
About the Author
Michelle Szucs is a product manager on the Oracle Optimized Solutions team in the Systems division of Oracle. She is the solution manager for Oracle Optimized Solution for Secure Oracle WebLogic Server.