My first experience with Hashicorp Consul was when I was tasked to take over some minor maintenance of a simple Springboot app to which my predecessor had added this platform. Consul has everything to set up and run a distributed and horizontally scaled application environment (read: microservices). But since we had only one service and one instance of it, deploying Consul was completely unnecessary and only there as a learning exercise. Good old microservices; you’d think they should be past their peak of inflated expectations by now, but I still regularly find them between the must-have requirements for projects in companies that do not strike me as the kind of hyper-scaler for which microservices were invented and intended. But I could be wrong.
The first step and also the biggest challenge towards a microservices architecture is one of cleverly carving up your domain. How do you break it down into logical, manageable and future-proof individual services? They don’t tell you that in books, not even in the otherwise thorough and illuminating Spring Microservices in Action (Manning). It’s a technical text about all manner of support tooling and abstractions that you need to program without worry. Imagine I want to validate a street address against its postal code for any European country. I may call a service that delegates this request to another endpoint based on the country code. I don’t want to manage IP-addresses and access credentials for those remote services in my client code. It also has to be stable enough so I don’t have to code my own retry logic if the call fails. In order to faciliate this demanding wish list, mature microservices environments supply at least the following:
- Resilience strategies: Outages within single services should not bring down the entire system with them. You have your circuit breakers that shut down all access if a single service is too slow and keep pinging it in the background to see if it has regained its breath. The bulkhead pattern ensures that instances of the same services run in distinct pools, similar to the watertight compartments formed by a ship’s bulkheads to prevent a single rupture in the hull to sink the entire ship. Try not to think of the Titanic…
- Service discovery means you have a single port of call that all service instances register themselves with. Clients look up services through that same port and receive a connection to an available instance through a load balancer.
- Service routing would take care of routing a request like the one with the postcode to the correct endpoint. It can also handle central concerns like authentication (who are you?) and authorization (what can you see/edit?)
- Last but not least: you whole deployment pipeline should be automated, scripted and under version control. It should be possible to set up a new environment without manual (error-prone) steps. That means virtualization and containerization are almost indispensible.
It’s no coincidence four popular frameworks to make all this possible come from Netflix: there’s Eureka for discovery, Hystrix for resilience, Ribbon for load balancing and Zuul for routing. They all work happily together with Springboot and the Cloud plugins. It’s not trivial matter, but the integration is heavily annotation-based and takes little coding to make it work. There’s so much intricate logic the software takes off your hands you’d be mad to implement it all by yourself.
It seems a lot to take in, because it is a lot. Do you need all this to do microservices? Do I really have to read another 500-page tome? I’m afraid you do. Microservices jsut aren’t simple and without these essential tools they are a recipe for disaster. And in case you are sure you can do without the Netflix or similar stack because you only have four services that use the same Oracle DB, well then you’re not really dealing with microservices and maybe you shouldn’t be calling it that.