CS 530 - Advanced Software Engineering

Microservices Architecture

Reference: Sommerville, Engineering Software Products, Chapter 6

 

Software services

A software service is a software component that can be accessed from remote computers over the Internet. Given an input, a service produces a corresponding output, without side effects. The service is accessed through its published interface and all details of the service implementation are hidden. Services do not maintain any internal state. State information is either stored in a database or is maintained by the service requestor. When a service request is made, the state information may be included as part of the request and the updated state information is returned as part of the service result. As there is no local state, services can be dynamically reallocated from one virtual server to another and replicated across several servers.

After various experiments in the 1990s with service-oriented computing, the idea of 'big' Web Services emerged in the early 2000s. These were based on XML-based protocols and standards such as SOAP for service interaction and WSDL for interface description. Most software services don't need the generality that's inherent in the design of web service protocols. Consequently, modern service-oriented systems, use simpler, 'lighter weight' service-interaction protocols that have lower overheads and, consequently, faster execution.

Microservices are small-scale, stateless, services that have a single responsibility. They are combined to create applications. They are completely independent with their own database and UI management code. Software products that use micro services have a microservices architecture. If you need to create cloud-based software products that are adaptable, scaleable and resilient then I recommend that design them around a microservices architecture.

A microservice example: system authentication

Each of these features could be implemented as a separate service that uses a central shared database to hold authentication information. However, these features are too large to be microservices. To identify the microservices that might be used in the authentication system, you need to break down the coarse-grain features into more detailed functions.

Microservices

Characteristics of microservices

Microservices communicate by exchanging messages. A message that is sent between services includes some administrative information, a service request and the data required to deliver the requested service. Services return a response to service request messages. An authentication service may send a message to a login service that includes the name input by the user. The response may be a token associated with a valid user name or might be an error saying that there is no registered user.

Microservice characteristics

Microservices architecture

A microservices architecture is an architectural style – a tried and tested way of implementing a logical software architecture. This architectural style addresses two problems with monolithic applications The whole system has to be rebuilt, re-tested and re-deployed when any change is made. This can be a slow process as changes to one part of the system can adversely affect other components. As the demand on the system increases, the whole system has to be scaled, even if the demand is localized to a small number of system components that implement the most popular system functions.

There are many benefits of using microservices architecture. Microservices are self-contained and run in separate processes. In cloud-based systems, each microservice may be deployed in its own container. This means a microservice can be stopped and restarted without affecting other parts of the system. If the demand on a service increases, service replicas can be quickly created and deployed. These do not require a more powerful server so 'scaling-out' is, typically, much cheaper than 'scaling up'.

Decomposition guidelines

Services communicate by exchanging messages that include information about the originator of the message, as well as the data that is the input to or output from the request. When you are designing a microservices architecture, you have to establish a standard for communications that all microservices should follow. Some of the key decisions that you have to make include the following. should service interaction be synchronous or asynchronous? should services communicate directly or via message broker middleware? what protocol should be used for messages exchanged between services?

In a synchronous interaction, service A issues a request to service B. Service A then suspends processing while B is processing the request. It waits until service B has returned the required information before continuing execution. In an asynchronous interaction, service A issues the request that is queued for processing by service B. A then continues processing without waiting for B to finish its computations. Sometime later, service B completes the earlier request from service A and queues the result to be retrieved by A. Service A, therefore, has to check its queue periodically to see if a result is available.

Direct service communication requires that interacting services know each other's address. The services interact by sending requests directly to these addresses. Indirect communication involves naming the service that is required and sending that request to a message broker (sometimes called a message bus). The message broker is then responsible for finding the service that can fulfill the service request.

Microservice data design. You should isolate data within each system service with as little data sharing as possible. If data sharing is unavoidable, you should design microservices so that most sharing is 'read-only', with a minimal number of services responsible for data updates. If services are replicated in your system, you must include a mechanism that can keep the database copies used by replica services consistent.

Inconsistency management. An ACID (Atomicity, Consistency, Isolation, and Durability) transaction bundles a set of data updates into a single unit so that either all updates are completed or none of them are. ACID transactions are impractical in a microservices architecture. The databases used by different microservices or microservice replicas need not be completely consistent all of the time. Dependent data inconsistency: the actions or failures of one service can cause the data managed by another service to become inconsistent. Replica inconsistency: there are several replicas of the same service that are executing concurrently. These all have their own database copy and each updates its own copy of the service data. You need a way of making these databases 'eventually consistent' so that all replicas are working on the same data.

Eventual consistency is a situation where the system guarantees that the databases will eventually become consistent. You can implement eventual consistency by maintaining a transaction log. When a database change is made, this is recorded on a 'pending updates' log. Other service instances look at this log, update their own database and indicate that they have made the change.

Most user sessions involve a series of interactions in which operations have to be carried out in a specific order. This type of service coordination is called a workflow. An authentication workflow for UID/password authentication shows the steps involved in authenticating a user. In this example, the user is allowed 3 login attempts before the system indicates that the login has failed.

Failure types in a microservices system

A timeout is a counter that this associated with the service requests and starts running when the request is made. Once the counter reaches some predefined value, such as 10 seconds, the calling service assumes that the service request has failed and acts accordingly. The problem with the timeout approach is that every service call to a 'failed service' is delayed by the timeout value so the whole system slows down. Instead of using timeouts explicitly when a service call is made, one could use a circuit breaker. Like an electrical circuit breaker, this immediately denies access to a failed service without the delays associated with timeouts.

RESTful services

The REST (REpresentational State Transfer) architectural style is based on the idea of transferring representations of digital resources from a server to a client. You can think of a resource as any chunk of data such as credit card details, an individual's medical record, a magazine or newspaper, a library catalogue, and so on. Resources are accessed via their unique URI and RESTful services operate on these resources. This is the fundamental approach used in the web where the resource is a page to be displayed in the user's browser. An HTML representation is generated by the server in response to an HTTP GET request and is transferred to the client for display by a browser or a special-purpose app.

RESTful service principles

RESTful service operations

Service deployment

After a system has been developed and delivered, it has to be deployed on servers, monitored for problems and updated as new versions become available. When a system is composed of tens or even hundreds of microservices, deployment of the system is more complex than for monolithic systems. The service development teams decide which programming language, database, libraries and other support software should be used to implement their service. Consequently, there is no 'standard' deployment configuration for all services. It is now normal practice for microservice development teams to be responsible for deployment and service management as well as software development and to use continuous deployment. Continuous deployment means that as soon as a change to a service has been made and validated, the modified service is redeployed.

Continuous deployment depends on automation so that as soon as a change is committed, a series of automated activities is triggered to test the software. If the software 'passes' these tests, it then enters another automation pipeline that packages and deploys the software. The deployment of a new service version starts with the programmer committing the code changes to a code management system such as Git. This triggers a set of automated tests that run using the modified service. If all service tests run successfully, a new version of the system that incorporates the changed service is created. Another set of automated system tests are then executed. If these run successfully, the service is ready for deployment.

Useful links