“its not the big fish which eats the small fish, its the fast fish that eats the slow fish”
In traditional programming sense, we are all familiar with monolithic applications, meaning one big chunk of war or ear that contains all your business logic and connects to one data storage, usually a relational database. The problem with monolithic apps is should one part of the application fails, everything fails. It’s either we do a full rollback if something goes wrong or everything goes live. Also, if you needed to make one small changes there is a tedious process of testing the whole application again to ensure nothing has been broken. Lastly, scaling is also an issue, if you wanted to scale your application, you would have to scale the whole thing, for example creating a second instance of your server with everything in it, but what if you only had a heavy load on sales module of your e-commerce site? you would be wasting memory/cpu/cost to scale the whole thing, when you just wanted to scale your sales module.
Microservices hopes to address these issues, seperating your applications into bite-sized chunks. Your one big ecommerce site can be divided into modules like sales module, login module, user management module, and so on. They would all be sitting in different instances (or it can be grouped in one instance), and they would just be talking to each other via services. In layman’s terms, its like you have multiple small applications.
Microservices in a way is similar to SOA (service oriented architecture), but more improved version, it does not focus too much on hooking up multiple monoliths. Instead, it focuses on creating standalones applications for each services.
3 pillars of microservices:
Isolation– prerequisite for resiliency, requires asynchronous communication
Time – concurrency
Space – distribution and mobility
1.) Isolate all the things – Failure isolation or bulkheading, term originally used in container ships where they would have isolation for each part of the ship, that in the event it had leaks and water was able to enter the ship it is able to isolate that area and the ship would still be able to sail to its destination.
2.) Act Autonomously – parts act autonomously, meaning they are able to process things on their own without having to wait for other parts to respond. This why we need to use asynchronous calls, so that threads are not held up by another service.
3.) Do one thing, and do it well – This is Unix philosophy, each service, method, should do one thing, and do it well. Write programs to make them work together. This adapts SRP model or single responsibility principle.
4.) Own Your state, exclusively – Microservices that share database with other database is still a monolith application! it is only disguised as a microservice application! Each microservice should have its own storage data, it could be any of the following:
- TimeSeries DB (influxdb and opentsdb)
- Event log (Kafka, Kineses, Cassandra)
Polyglot Persistence – decentralized data management, not allowing one service calling storage of another service, but only its API through Event sourcing, or Command Sourcing.
- Command Sourcing – means you are playing all the commands used, which means all the side effects as well, bringing the service up to terms of state
- Event Sourcing – Avoids using Object relational impedance mismatch, usually occurs from Object relational mapping, usually the beset persistence model for microservices, due to async messaging
Database – contains the latest values (or just snapshots, or blank states), all the events are stored in an event log, the truth is the logs
5.) Embrace asynchronous message-passing – Communication between microservices are done by message passing, this means embracing eventual consistencies of your states in your application. This is a trade-off for highly scalable application. In contrast to just having one database, knowing at one point in time the exact state of your data, having microservices with multiple data storage means you have eventual consistencies across your application.
REST – default microservice communication protocol, but it is synchronous!
– very unfitting
– use it sparingly, at the expense of coupling your services
6.) Stay Mobile, but addressable – since our services are now scalable, how does one service make a call to another? if for example one service is down, or has scaled, how does the other services still call your moving service.
- Load-Balancing between instances of a stateless service
- Active-Passive state replication between stateful service
- Use a service registry that services can use to lookup informationKnown Challenges:
- coordination/Integration – more and more libraries are embracing reactive streams : akka Streams, RxJava,
Spark Streaming, Cassandra Drivers
- security – Two way authentication
– Hash Message Authentication Code
- data consistency – Minimizing Data Coupling and embracing eventual consistency
- API Management – say for example you have a client that needs to communicate with 10 different services before completing a transaction. To simplify this you need to implement an API Gateway.