This time the chose topic is only a thought about how I think microservices should be. Yes, you do not care at all about what I think about that stuff! XD No worries. It´s only for discussion and fun. The truth is, the process has allowed me to think about this specific topic comparing several different ways to make microservices. Moreover, it has been funny and exciting!
What I really like!It´s Reactive Scala programming based, small, small microservices ready to be run in well balanced clusters. OK, it is out of scope. We'll focus on Java! (this time). I am still working on my proposal for HIgh Performance Microservices in Scala.
The General Trend: SpringIs inevitable. When a company begins to think in microservices thinks of Spring Boot. It´s robust, it´s well tested, it uses Maven or Gradle (I love Gradle..). Even some people think (believe me) that Spring Boot is special because it packages all in an executable jar file.... Well, all seems to be perfect. Anyway, after reviewing a number of microservices based on Spring boot I always get a strange sensation. I´ll try to explain it.
My point: Spring Boot brings Spring to the Microservice. But..what for?? Do we really need reflection AOP, Spring IOC, Spring Configuration, Spring context, etc, etc?? If we need all the IOC stuff in a microservice I think we are breaking the basic principle of MSs: be smaller as possible.
That's because I do not need IOC in a microservice. It´s simply counter-productive because of its size. I repeat as a mantra: The MS must be such smaller as possible. Just a few classes to meet the single responsability principle. I see continuously MSs of 40 or 50MB with 90 or more dependencies. Do you really think this is meeting the basic principles of MS architectures?
Spring is following a well known commercial policy. trying to cover all the aspects of development, oriented to cloud, etc. Pivotal and Cloud Foundry have worked hard in this way. Well. The SpringEverywhere way of thinking is not good. It's basically a nonsense limit to our resources and thinking as engineers,
But Spring is based on JEE specifications and there are many JEE implementations. Many of them are incredibily good. Are they real alternatives in an enterprise way of thinking? I mean, the true standards are not frameworks. Standards are Specifications, and we can follow specs, not frameworks.
Bad Things in the Spring Boot WayWhen I am considering I want to make a microservice Spring seems to be the "default" solution. But it is not the better one at all. We should not forget the specifications are focused on enterprise solutions. So, we should follow the specs and try to find the best adapted architecture for us.
- Spring is heavy
- Spring is based on reflection.
- Embedded Tomcat is not a NIO container. You can configure to act as one. Please, try and tell! Even the Spring Boot documentation includes how to change to Jetty or Undertow. Wise people and definitely recommendable!
- It´s definitely slow... As mentioned above, use Jetty or Undertow instead. Definitely it would be the preferred way.
My ObjectivesIt is only an example. I should create a set of microservices. Each one must meet certain features. REST based communications, nested composed calls, etc. Speed is the key. We must compose complex views from dozens of microservices. Limited resources regarding CPU and memory. Deployment and clustering are out of scope for now.
So, each MS is at the same time:
- A REST server
- A REST client
- A light intermediate process
- Externally configurable
- Logs for metrics
- Integrable to dashboards
- Performance must be testable too
- Exception handling
- And...THEY MUST BE ABSOLUTELY NON BLOCKING
If you try to create ten of these microservices with Spring you are screwed. Tomcat is really a barrier for asynchronoous operations. The size and weight of dependencies is terribly high. Spring Boot is expensive in terms of resources.
A ProposalAfter certain investigation and thinking of Reactive Programming I found what could be an acceptable composition of a model for the general prototype of needed microservices:
- Grizzly as NIO container. Configurable in code, implements Jersey 2.
- The MS is simply a Jersey application. Such easy like that.
- JerseyTest meets requirements for real test.
- ComposableFuture for composing promises.
- Performance Test based on JMeter.
- A special service allow us to retrieve the version, name and state of the service.
- Special lof for metrics.
- Reactive patterns with Observable.
- Exception handling for services.
- To create the executable jar file I propose the good old Mave Shade plugin. Impressive in efficiency and build time speed.
- Last but not least, Docker integration in a Maven profile. You can create a Docker image with the MS to be run, clustered, balanced, etc.
But for now, the project has basically 4 ways to implement operations:
Once the request is received a subsequent request is made. When received, the next one is made and so on. Threads are being blocked, times for each request add to the general amount, etc. It can seem a naive way of programming but you can believe me, my friend. I´ve seen dozens of implementations in this way.
As evident, you can easily infer that more calls are being nested (dependant requests in a flow) the more time is added to the final response. Moreover, a number of threads are blocked while expecting the request. This dramatically affects to scalability and performance when concurrency conditions are given.
AsynchronousWe can use Futures with the async() method for clients, suspended requests and responses, etc. All is much better but I´m afraid Jersey clients based on async() are not very scalable because of thread handling when receiving the response. Conclusion, we save time and thread contention in requests but fails when receiving responses.
ReactiveOh my friends. It´s another world. Think in a world with streams, composables and promises. It´s the asynchonous world where the information are based on streams. It´s not Scala, It´s Java!! Java 8 allow us to do this and is possible to use that with Rx Jersey extensions simply by using the rx() method in the Jersey client! The result is, we avoid thread contention in requests and responses, saving a lot of time in processes. Doubtlessly, it is the best option.
Reactive ObservableThe same as above for Observable reactive streams.
Please, take a look on the code and use if it can be useful for you. We get a relatively small file (around 10 MB), less dependencies (no more than 15). It is very scalable and lightweight in terms of resources in host. I think is specially adapted to scenarios where you need a number of microservices and you need speed with scarce resources.