Probably the most popular topic at the Devoxx 2015 conference in Poland was microservices. You could learn about general principles of microservices, how to write, test, deploy, and monitor microservices.
In this article I will summarize what I learned, based on 7 different talks:
- Principles Of Microservices – Sam Newman (ThoughtWorks)
- “Bootiful” Microservices with Spring Cloud – Josh Long (Pivotal)
- Everybody lies – Tomasz Kowalczewski (Codewise)
- Hystrix – managing failures in distributed systems – Tomasz Nurkiewicz (4FinanceIT)
- Microservices – enough with theory, let’s code some – Tomasz Szymański (SoftwareMill), Marcin Grzejszczak (4finance)
- Building fault tolerant microservices – Christopher Batey (DataStax)
- Running Micro-Services with Spring Boot in Kubernetes – Ray Tsang (Google Cloud Platform)
Introduction to microservices
Microservices is a way to develop single application by dividing it into several independent, but inter-operating, systems. There are several benefits and problems in this architecture.
Main benefits include:
- Fault tolerance – in case of failure of one microservice, we can smoothly degrade functionalities of our application. For example, let’s say that comment service failed. Instead of bringing whole application down, we can show pages without comments. Core of our application is still running.
- Scalability – we can scale critical components of our application. In traditional application model, scalability is often obtained by creating several instances of whole monolithic application. In microservices you can duplicate only services, which are performance bottleneck.
- Loose coupling – benefits of loose coupling are generally known. In microservices we take loose coupling to the extreme. Each service could be created by different people, different technologies, frameworks, languages.
Unfortunately, there are some challenges. Instead of one application, we have now several dozen of services. Many simple solutions in traditional architecture are difficult in microservices:
- Testing – instead of simple unit tests, we know have to test interactions between services.
- Deploying – one application could be deployed manually. If we have several dozen application, manual deploy is too time consuming and error prone.
- Configuration – it would be very difficult to configure each service individually in property files.
- Logging – when there is an application error, it is not practical to connect to each service and grep all of the log files.
- Monitoring – we would like to know which service is down or which service has performance problems.
- Discoverability – it is not practical to keep IP addresses in services configurations. One service could be used by several others.
- Load balancing – in traditional approach we can have one load balancer in front of the application. In case of microservices sometimes it is needed to have load balancing between several services. Several services can exist in many instances.
Microservices should be modeled around business domain. In best case scenario they should implement single business functionality.
It is very important that services are very loosely coupled. It means that you should avoid sharing code between services. Services should also use independent databases. Shared database leads to tight coupling.
Most popular frameworks for creating microservices are:
Consumer driven contract
API for a microservice should be written by his consumer. Consumer should also write automated acceptance test for service.
For consumer driven contract development you could use for example Accurest.
There are several libraries for calling other services:
- Feign – could be used to create declarative rest client. You can use it with Spring Cloud project.
- micro-infra-spring – spring rest template extension.
- Swagger – api documentation frontend.
It is very important to use some fault protection mechanisms while calling other microservices. Without fault tolerance failure in one service could propagate and kill whole application.
- fail fast – waiting infinitely for response could exhaust all resources in calling service.
- thread pools – it’s better to use dedicated threads for calling other service. Calls could potentially hang. By using dedicated threads we can protect container http threads.
- circuit breakers – don’t beat a dead horse. When service is down or has performance problems you don’t want to make additional calls to it.
The most popular library for writing fault tolerant code is Hystrix.
Configuring many services can be made easier by using configuration server. Configuration server is centralized repository for configuration, but also can be used to version control services configuration.
There are couple solutions for config server:
Keeping IP addresses for each service can be cumbersome. Problem is even more difficult when you are dynamically adding instances of microservices. Service discovery is used to dynamically register microservices in centralized system. Other services can later used discovery to get IP addresses for registered services. Discovery can also be used for load balancing.
Here are some service discovery systems:
When there is an application error, it is not practical to grep log files from all of microservice instances. It is better to have centralized log database.
It is helpful to generate correlation id for all application requests and propagate it in the system. By using correlation id you can later investigate how request was processed in several microservices.
There are several tools for application logging:
- logstash – processing log files
- logstash forwarder – sending logs
- elasticsearch – log database
- kibana – log UI and visualization
Metrics and monitoring
Besides logs, other important aspect of monitoring application is metrics. Metrics can give you information about aggregated times of running critical tasks, rate of important events or information about used application and system resources. It can give you insight about which service failed and why. You could also extrapolate the data to predict maximum system load.
There are several tools to collect metrics data:
- Metrics – the most popular metrics library
- HdrHistogram – accurate metrics library
- Graphite – metrics database
- Grafana – metrics visualization
- Seyren – alerting dashboard for Graphite
- Icinga – monitoring system
- Hystrix dashboard
- Turbine – aggregates several hystrix streams into one dashboard
There are several technologies for service testing:
Deploying manually can be time consuming and error prone. It should be possible to deploy services independently, so you could change version of one microservice without touching others.
It is also a good idea to have coexisting endpoints when api for microservice changes. Older services could use old api, while newer versions are using newer api.
There are several tools for continuous integration and deployment:
To further abstract runtime environment, it can be a good idea to run services in container.
There are several levels of abstractions:
- Vagrant – can be used to abstract and automate virtual machine creation.
- Docker – can be used to create lightweight service containers.
- Kubernetes – is container orchestrator. It can be used to further abstract group of servers into a cloud. Kubernetes automatically deploys docker containers to server with required resources.
At the Devoxx there were many presentations talking about creating microservices. Creating application with microservice architecture is only half a battle. To have successfully running application you should have means to change configuration, monitor, check logs, test and deploy new versions of services. Fortunately there are many tools to choose from to create highly reliable systems.