Is your spring boot microservice production ready?
Spring boot is one of the most popular frameworks to build microservices. One of the major reasons behind it is the robustness and out-of-the-box feature that make your application production ready by default. Still, in the ever-evolving software engineering space that craves extendability, debuggability, and scalability, we need to take care of certain aspects of our service to make it production ready from all corners.
Versioning REST endpoint — Extendability
Any REST endpoint that you expose is most certainly going to evolve over time and you would like to provide your clients with the ability to make a transition from an old contract to new at their own pace. Not to mention the need to fall back to the old endpoint in case of issues. Versioning is essential to make this evolution smooth. You could follow any of the below ways of versioning your REST APIs.
URI path
- /api/v1/users
- /api/v2/users
Query Params
- /api/users?version=1
- /api/users?version=2
HTTP header
Content negotiation — Let the client specify the version and content type in the Accept header
- curl -H “Accept: application/vnd.usr.v1+json” http://localhost:8080/api/users
Exposing health check and metrics endpoints
In any architecture(old or new) you need to know the health of your application. Spring boot with its actuator module provides the endpoints to check if the application is up and ready. In the world of scalability and replication just having health endpoints at each services level isn’t enough, you have to aggregate the metric. Prometheus and Grafana are great stacks to take care of this requirement. On top of that, you will need Micrometer to expose data for Prometheus to consume.
Reference to explore more — https://www.atlantbh.com/custom_metrics_micrometer_prometheus_spring_boot_actuator/
Default TimeZone
Managing time and timezone is a huge headache. Client, Server, and Database, all 3 can and mostly would reside in different timezones(infra-wise speaking). If the server and database both capture and save data in UTC, the conversion to the client’s relative time zone will be uniform i.e, the client should always expect the time returned from the server to be in UTC and make appropriate conversion based on the end user’s current timezone.
TimeZone.setDefault(TimeZone.getTimeZone(“UTC”));
Circuit Breaker and Retry pattern for remote calls
In a microservices architecture you could have numerous remote calls(REST, SOAP, socket.IO, etc) and it’s best practice to wrap these calls in a circuit breaker and retry pattern. Retry logic should kick in to help to try to reconnect to a service that encountered a rare failure because of a network blip. Should the retry even fail for a certain number of times, then the circuit breaker pattern takes over and prevents further call to save on thread and network resource(cloud compute dude! everything is money)
https://microservices.io/patterns/reliability/circuit-breaker.html
Logging Pattern Update
By default, you will see the below information in your spring boot application log.
- Date and Time
- Log level
- Process Id
- Thread
- Class
- Message
I would recommend getting rid of the process Id and adding below additional information to make the most out of each log entry.
- Request/Trace id — Unique identifier for each request — Improves traceability
- Endpoint, request/response body, params, and status code — Improves debuggability
- Host — Improves log segregation based on host
There could be much more common information that you can add to the log by updating the pattern which would be specific to the nature of the application. For example, user Id to trace requests for each user, whether they are logged in or anonymous, etc.
Sample Improved Log Pattern
2022–08–07 08:05:19,999 INFO trace-id=3e229769–436c-4245-b21f-35d17f545167 host=LAPTOP-3G314OP3 [http-nio-8080-exec-1] com.met.jumbo.service.impl.LoggingService: REQUEST method = [GET] path = [/api/weather/ZIPCODE/560035/2022–07–22]2022–08–07 08:05:20,241 INFO trace-id=3e229769–436c-4245-b21f-35d17f545167 host=LAPTOP-3G314OP3 [http-nio-8080-exec-1] com.met.jumbo.service.impl.LoggingService: RESPONSE method = [GET] ResponseHeaders = [{Vary=Origin}] responseBody = [Error(code=JW-1001, messages=[Weather not found])]
Log File Rolling
Now that we are logging more information the size of our log file will naturally be bigger and therefore cause problems if not handled.
- Rolling based on the size of the log file
- Rolling based on date and time
Reference — https://www.baeldung.com/java-logging-rolling-file-appenders
Externalize your configuration
Move the configuration to a separate code base in version control and have it deployed to a config server that your application talks to. This is to decouple your application business logic deployment from the configuration. If a change to only configuration is needed you can deploy to the config server and refresh your application instances without the need to restart them.
Store secrets/passwords in Vault
It’s secure and best practice to store and use secrets/passwords in an external secure system like Vault. This helps in shielding the production-related credentials from members having access to the code base.
Conclusion
There are many more points to cover from an overall microservices architecture point of view but the points we covered in the article are meant to relate to changes you need to make in your spring boot application that is part of the microservice architecture. If you feel there are more points to cover feel free to drop a comment