Microservices are the cool new kids on the block. Whether you’re not yet sold on the idea of a distributed system or if you just can’t get enough of it, I’ve got you covered.
This post is a quick start guide to get you up and running with microservices in Java. The task isn’t necessarily easy for me, though. Microservices can model all kinds of real world structures. They’re a perfect example of Conway’s Law in execution.
That means that everything could go great when transitioning to a distributed system, provided that you and your organization have already decided that a microservices architecture is ideal. But things can also go haywire very easily, and you could end up maintaining multiple hosts, deploying multiple codebases, dealing with complicated logging, and supporting different executions.
We’ll focus on the bright side this time. As we move along, I’m going to reinforce some of the concepts related to Java microservices that you may or may not already be familiar with.
Today, we’re going to create a very simple boilerplate to help you get started. It’s simple enough to get you up and running quickly, but it should offer enough content to prove that it works. This initial setup will consist of a service discovery (more on that later) and a concierge service that’ll welcome us in.
Hit the Ground Running
First things first, we need some tools! Java is our programming language of choice today. IntelliJ, Eclipse, or your favorite IDE will be our wingman.
Since Java is our weapon of choice, we’ll use Spring as our base to get up and running. With Spring as our foundation, we can be pretty sure we’re in for a smooth flight. Briefly, though, if you don’t know what Spring is, you’re missing out.
Spring is a framework that streamlines a lot of the boilerplate that normally comes with setting up a Java project. You can also use other frameworks such as Restlet, Play, Spark, etc. It’s up to your comfort and convenience.
If you’re building microservices in Java, it’s very important to have the right toolset. As for myself, I’m going to be setting up a new Spring project using IntelliJ.
In the next screen, I’m going to be adding one of Netflix’s greatest creations (after Sense 8): Eureka.
Before that though, I think it’s time for a quick knowledge check. Microservices by themselves are no good if there isn’t a way to track which one is active and which one isn’t. Moreover, in complex systems, keeping a list of services available becomes increasingly important.
Picture high-availability systems with global replication. How would you keep track of the same service deployed at five different locations? How would you load balance requests between them?
That’s complicated—unless, of course, you have a service that discovers other services as well as load balances between them. Netflix faced the same issue, and the solution they came up with was Eureka. That’s what we’ll use today.
Here’s what that first setup screen looks like. A Spring project offers a number of dependencies that go from database management to sessions and security. The ones we’re interested in are mentioned below.
And that’s it! Let’s do some coding.
The first thing we have to do is create a service discovery class. Normally, this is one of the most complex aspects of handling microservices in Java. As I said above, there’s a lot that can go into a full-service microservices architecture.
So, let’s take a look at how much Spring can automate that for us.
And that’s it. That didn’t take long whatsoever. I gotta admit, I was very surprised when I first saw how easy it was. Let’s go over some of the items in there:
- @EnableEurekaServer marks your module as a Eureka Server, basically tying your microservices together.
- The @SpringBootApplication annotation attempts to use the Spring auto-configuration services, based on the dependencies you’ve added.
Once that’s done, we need to configure the actual settings for the server, which is pretty quick. When the application boots, it looks for an applications.properties file, which should be blank the first time you open it. The settings in there should be simple and straightforward.
For the time being, this is all we need:
Running the Project
There are a couple of ways to run the project. The first method is to open up a terminal session in your favorite OS. Then, navigate to the directory you’re using to store the project and run the following:
#Mac OS/Linux./mvmw spring-boot:run
For me (and this is probably your case as well), I just hit that run button on my IDE and waited for the magic happen. However you do it, the console should display a lot of information, beginning with the Spring details and the information of our base DiscoveryService. I’d like to talk about a few things showing up in the console output before we move on.
- Tomcat initialized and Starting service. Make sure this line is displaying the port you previously set. In our case, it’s port 6969. If not, you may need to go back to the applications.properties file and double-check the settings.
- Initiating Jersey application. What’s a Jersey application? Jersey is a library that will assist us with the REST API. It’s not something you need to manually include, as it’s already there as part of the Maven dependencies.
Once all of that finishes, if you visit your localhost with the port you set (in my case http://localhost:6969/), you’ll see the Spring dashboard. We’re onto something here!
This interface provides an overview of the overall health of the system and any other notifications we may need to know about. Below those (not seen in the screenshot) are additional metrics that are relevant to the server, such as memory, memory usage, uptime, replicas information, and more.
One key item to note here is that the instances panel is still empty, which means that we haven’t created an instance (a microservice) that connects to our discovery. Let’s address that quickly now.
A concierge service sounds fancy all right. Now, that we’ve created a discovery service, it’s time to create an actual service that connects to it.
Our little concierge service will be very simple. It’ll respond to a specific URL, which we’ll pass a name to. Despite being very simple, this example will give us an opportunity to cover a few aspects of microservices that are specific to the latest version of Spring.
For our microservice, it’s important to mention that it needs to be a different project or codebase. It may seem obvious, but that’s one of the questions I had myself when I started working with microservices.
That doesn’t mean that we can’t split our project in modules or that several services can’t run on the same host. They can. We’re just trying to walk before we run.
So, to create the concierge service, create a new project, make sure you include Eureka again, and write a simple class.
First, we should create a simple server-class. This isn’t complicated at all, and a few lines will get us going.
This isn’t anything we haven’t seen before. Our concierge module will also require its own settings. Remember when we set up the Eureka server earlier? This is a similar setup. Open the application.properties file and add a few settings:
#Microservice server settings spring.application.name=concierge-service spring.freemarker.enabled=false spring.thymeleaf.cache=false spring.thymeleaf.prefix=/templates/ eureka.client.serviceUrl.defaultZone=http://localhost:6969/eureka/ server.port=2222 #sandbox security settings NOT PRODUCTION READY spring.security.user.name=user spring.security.user.password=pass
One really cool thing about Spring version 2.0, which is reflected in the above code, is that security is enabled by default. So when you visit the microservice, it now includes a nice login page by default. That’s why I’ve explicitly added a set of credentials. Also, notice that we’re using a different port this time.
Next in line is a class that actually does something. Our concierge class will receive a name from the URL and display it.
I won’t go into too much detail into what’s happening here, but notice that Spring annotations will do much of the legwork for you, such as mapping a URL request to a method and indicating a REST response instead of a view.
So, it’s time for the moment of truth. I’ll be running the discovery service first and then the concierge service.
Boom! There’s our service, and it’s automatically tracked and visualized! Pardon my excitement; it’s amazing how easily that worked. Surely enough, if we visit the service at http://localhost:2222/greeting/john, we’ll get a response and a warm welcome.
That’s how you create your first microservice architecture in Java. It didn’t take long at all, but there sure are a lot of different steps.
As you go on with microservices, you will eventually start building complex systems. For such systems, it is important for you to test the application before deploying it. When it comes to microservices, you have four major steps in testing.
1. Unit Testing
Every microservice is comprised of multiple tasks and functions. Unit testing is when you test each of these functions and make sure there are working as expected.
2. Component Testing
In the microservice architecture, each microservice can be considered as a component. In component testing, you test the complete microservice along with all its functions and tasks.
3. Integration Testing
This test is to ensure that microservices communicate with each other properly. Integration test helps you understand how microservices collaborate to get the overall task done. Microservices communicate with each other either synchronously or asynchronously.
When microservices communicate synchronously, a service will call another service or send a request. It then waits for the request to complete and then resumes with its next task.
In asynchronous communication, a service will send a request to another service and resume with its next task with waiting for the other service to return. And the service will come back to the request when the other service responds to it.
4. End-to-End Testing
As the name suggests, this is to test the overall application when all its parts are put together. This is the last step of testing and it happens before deployment.
Now that you are on with microservices, it’s best to know how it is beneficial for you to make the best of it.
Benefits of Microservices
Easier and Faster Development
Distributed tasks of coding make the development faster because the teams can simultaneously work on different services. Divided services are easier to understand, build, manage, and deploy because they are taken care of independently.
This helps in continuous integration and continuous delivery. It also becomes easy for the teams to understand the functionality better.
One of the greatest advantages of using microservices is that you can use multiple stacks to build a single thing. An application can have different functionalities and you might not find one right tool for all of them. So, you can choose the tool that is best based on the task.
By doing this, you are improving the quality and efficiency of the application without affecting the integration of microservices.
There’s no single point of failure when you use microservices because the services are distributed. Even if one part of the application goes down, the rest of the application is still up and running. And if you have backup services, you can easily replace it with the service that went down with minimal efforts.
In many scenarios, you might want to scale just a particular service or a couple of services based on the demand. Microservices allow you to scale just what is needed rather than making changes to the complete software. This makes scalability easy and quick.
Microservices Best Practices
Like any other tool and approach, microservices are great only when they’re used properly. You can’t win a war just by having the most advanced weapon in the world. You need to know when and how to use it. Here are some of the best practices from planning to execution.
Do You Need Microservices?
The most important thing is to decide whether microservices are best for your use case. Microservices have pros and cons. You have to decide whether the pros are of use to you and if they are worth the cons.
First, analyze your need and see if you need microservices.
Planning Microservice Architecture
Plan your microservices architecture as per business needs. Just because you decide to use microservices doesn’t mean you have to use them everywhere.
Identify where microservices are needed. Don’t overdo or underdo microservices. You should also consider scalability while planning.
Avoid Using Monolithic Databases
Code isolation is one of the features of microservices as discussed earlier. If you adapt to microservices but still use a monolithic database, then you’re not getting the best out of the architecture. Use separate data stores for your microservices.
Design your microservices to be loosely coupled. This means that the dependency of one microservice on another should be minimal. You can use asynchronous communication, API calls, etc., to achieve this.
Single Responsibility Principle
According to this principle, a microservice should be designed to perform only one main function or provide one main service. And the microservice should do this efficiently. By using this principle, you will improve the quality of your application and also improve performance.
Focus on Security
It goes without saying that security is essential for every application. Learn about common vulnerabilities and security issues while designing and building applications.
Using microservices increases the attack surface. So take security measures by using approaches like DevSecOps, Penetration testing, Vulnerability assessment, etc.
Log microservices and your application. Logs are one of the richest sources of knowledge in applications. Logging not just helps you identify the point and reason for failures but also helps you monitor the performance of microservices and the application as a whole. Hence, you have to implement proper logging.
Microservices Monitoring With Scalyr
Eureka is great to track microservices. But you’d want to do more than just track them. By now you should know the importance of keeping all your services looked upon.
And logging is one of the best ways to monitor microservices. But will you check dozens of logs from each server? Will you set up your own Syslog server?
Of course not. What you should do is get your company and your brand new microservices approach the right tools to get your microservices going. Scalyr has you covered there. Your DevOps team will appreciate it.
Scalyr easily ingests logs from your application and visualizes them in a beautiful dashboard so you can easily know what you need to, without having to go through thousands of log entries. All you need is a Scalyr API key to get started. If this is what your company needs or something that’s of interest to you, you can get a trial here.
Microservices in Java: Wrapping up
This is just setting up. We haven’t gone through the migration from a monolith or server requirements. There’s so much left to cover, and I wish I could make this post go on forever.
We haven’t discussed database patterns! Do we split the database or the code first? Who knows? How big can Java microservices be?
There are so many questions still, but time’s up. You don’t need me for that. It’s on you now; it’s time to break that monolith. Good luck!