Part 1: Creating microservice using Spring Cloud, Eureka and Zuul

Part 1: Creating microservice using Spring Cloud, Eureka and Zuul

Spring framework provides a set of libraries for creating microservices in Java. They are a part of the Spring Cloud project. Today I’m going to show you how to create simple microservices using Spring Boot and following technologies:

  • Zuul – gateway service that provides dynamic routing, monitoring, resiliency, security, and more
  • Ribbon – client side load balancer
  • Feign – declarative REST client
  • Eureka – service registration and discovery
  • Sleuth – distributed tracing via logs
  • Zipkin – distributed tracing system with request visualization.

Sample application is available at https://github.com/piomin/sample-spring-microservices.git. Here’s picture with application architecture. Client calls endpoint available inside customer-service which stores basic customer data via Zuul gateway. This endpoint interacts with account-service to collect information about customer accounts served by endpoint in account-service. Each service registering itself on Eureka discovery service and sending its logs to Zipkin using spring-cloud-sleuth.

san1s57hfsas5v53ms53

This is an account-service controller. We use findByCustomer method for collecting customer accounts by its id.

@RestController
public class Api {
   private List<Account> accounts;
   protected Logger logger = Logger.getLogger(Api.class.getName());

   public Api() {
      accounts = new ArrayList<>();
      accounts.add(new Account(1, 1, "111111"));
      accounts.add(new Account(2, 2, "222222"));
      accounts.add(new Account(3, 3, "333333"));
      accounts.add(new Account(4, 4, "444444"));
      accounts.add(new Account(5, 1, "555555"));
      accounts.add(new Account(6, 2, "666666"));
      accounts.add(new Account(7, 2, "777777"));
   }

   @RequestMapping("/accounts/{number}")
   public Account findByNumber(@PathVariable("number") String number) {
      logger.info(String.format("Account.findByNumber(%s)", number));
      return accounts.stream().filter(it -> it.getNumber().equals(number)).findFirst().get();
   }

   @RequestMapping("/accounts/customer/{customer}")
   public List<Account> findByCustomer(@PathVariable("customer") Integer customerId) {
      logger.info(String.format("Account.findByCustomer(%s)", customerId));
      return accounts.stream().filter(it -> it.getCustomerId().intValue()==customerId.intValue()).collect(Collectors.toList());
   }

   @RequestMapping("/accounts")
   public List<Account> findAll() {
      logger.info("Account.findAll()");
      return accounts;
   }
}
 

This is customer-service controller. There is a findById method which interacts with account-service using Feign client.

@RestController
public class Api {

   @Autowired
   private AccountClient accountClient;

   protected Logger logger = Logger.getLogger(Api.class.getName());
   private List<Customer> customers;

   public Api() {
      customers = new ArrayList<>();
      customers.add(new Customer(1, "12345", "Adam Kowalski", CustomerType.INDIVIDUAL));
      customers.add(new Customer(2, "12346", "Anna Malinowska", CustomerType.INDIVIDUAL));
      customers.add(new Customer(3, "12347", "Paweł Michalski", CustomerType.INDIVIDUAL));
      customers.add(new Customer(4, "12348", "Karolina Lewandowska", CustomerType.INDIVIDUAL));
   }

   @RequestMapping("/customers/pesel/{pesel}")
   public Customer findByPesel(@PathVariable("pesel") String pesel) {
      logger.info(String.format("Customer.findByPesel(%s)", pesel));
      return customers.stream().filter(it -> it.getPesel().equals(pesel)).findFirst().get();
   }

   @RequestMapping("/customers")
   public List<Customer> findAll() {
      logger.info("Customer.findAll()");
      return customers;
   }

   @RequestMapping("/customers/{id}")
   public Customer findById(@PathVariable("id") Integer id) {
      logger.info(String.format("Customer.findById(%s)", id));
      Customer customer = customers.stream().filter(it -> it.getId().intValue()==id.intValue()).findFirst().get();
      List<Account> accounts = accountClient.getAccounts(id);
      customer.setAccounts(accounts);
      return customer;
   }
}
 

Here’s the interface of Feign client for communication with account-service.

@FeignClient("account-service")
public interface AccountClient {

   @RequestMapping(method = RequestMethod.GET, value = "/accounts/customer/{customerId}")
   List<Account> getAccounts(@PathVariable("customerId") Integer customerId);

}

To be able to use Feign clients we only have to enable it in our main class.

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class Application {

   public static void main(String[] args) {
      SpringApplication.run(Application.class, args);
   }
}

 

There is also important configuration inside application.yml in customer-service. The ribbon load balancer needs to be enabled. I also suggest setting lease renewal and expiration on the Eureka client to enable unregistration from discovery service when our service is shutting down.

server:
  port: ${PORT:3333}

eureka:
  client:
    serviceUrl:
      defaultZone: ${vcap.services.discovery-service.credentials.uri:http://127.0.0.1:8761}/eureka/
  instance:
    leaseRenewalIntervalInSeconds: 1
    leaseExpirationDurationInSeconds: 2

ribbon:
  eureka:
    enabled: true
 

We’ve got our two microservices implemented and configured. But first we have to create and run discovery service based on Eureka server. This functionality is provided by our discovery-service. We only have to import one dependency spring-cloud-starter-eureka-server and enable it in the application main class using @EnableEurekaServer annotation. Here is configuration of Eureka server in application.yml file:

server:
  port: ${PORT:8761}

eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
  server:
    enableSelfPreservation: false
 

After running discovery-service we see its monitoring console available on 8761 port. And now let’s run our two microservices on default ports set in their application.yml configuration file and more two instances of them on another ports using -DPORT VM argument, for example account-service on port 2223, and customer-service on port 3334. Now we take a look at the Eureka monitoring console. We’ve got two instances of account-service running on 2222, 2223 ports and two instances of customer-service running on 3333, 3334 ports.

eureka

We have two instances of each microservice registered on the discovery server. But we need to hide our system complexity to the outside world. There should be only one IP address exposed on one port available for inbound clients. That’s why we need API gateway – Zuul. Zuul will forward our request to the specific microservice based on its proxy configuration. Such request will also be load balances by ribbon client. To enable Zuul gateway dependency spring-cloud-starter-zuul should be added inside pom.xml and annotation @EnableZuulProxy in the main class. This is Zuul configuration for ourservices set in application.yml.

server:
  port: 8765

zuul:
  prefix: /api
  routes:
    account:
      path: /account/**
      serviceId: account-service
    customer:
      path: /customer/**
      serviceId: customer-service
...
 

Like we see Zuul is configured to be available under its default port 8765 and it forwards requests from /api/account/ path to account-service and from /api/customer/ to customer-service. When URL http://localhost:8765/api/customer/customers/1 is call several times we’ll see its load balanced between two instances of each microservice. Also when we shut down one microservice instance we can take a look that it is unregistered from the Eureka server.

In the second part of the article I’ll present how to use Spring Cloud Sleuth, Zipkin and ELK. If you are interested in see Part 2: Creating microservices – monitoring with Spring Cloud Sleuth, ELK and Zipkin.

70 COMMENTS

comments user
Oscar Martinez

Thx for this article, I have a question related with how this servers phisically are distributed, if they are in the same ServerAPP or in differents servers or in different services, and what AppServer like GlassFish, Weblogic, WildFly are you using for this example.

thx a lot from Colombia.

    comments user
    Piotr Mińkowski

    Hi. Tomcat server is embedded by default in Spring Boot Web.

      comments user
      Thrinath

      Nice share.
      I have downloaded your code and ran the services as Spring Boot in STS Spring tool suite (Run As->Spring Boot App) with the following order:
      1. discovery-service
      2. account-service
      3. customer-service
      4. gateway-service
      5. zipkin-service
      Is the order correct ?
      I believe, these services are packaged as jar. And I ran these easily in eclipse/STS.
      I want to deploy these services on external tomcat server.
      Do I need to export all these services as war files and deploy them to tomcat server ?
      How to configure the multiple ports and application/servlet context name as you mentioned .yml files ?
      Please let me know the steps/configuration needed to deploy these services on external tomcat server ?

        comments user
        Piotr Mińkowski

        Well, those services are running on Spring Boot. Tomcat is embedded and is starting with application: java -jar Application. Port can be overriden with VM argument -DPORT. If you would like to deploy it on external Tomcat you have to remove Spring Boot and use stardard Spring Web app packaged as WAR. The only question is why?

        comments user
        Piotr Mińkowski

        Well, those services are running on Spring Boot. Tomcat is embedded and is starting with application: java -jar Application. Port can be overriden with VM argument -DPORT. If you would like to deploy it on external Tomcat you have to remove Spring Boot and use stardard Spring Web app packaged as WAR. The only question is why? These are microservices

      comments user
      Thrinath

      Thanks for your reply.
      The reason behind to deploy to external server is, all applications are at same place and easy to debug through tomcat log files and easy to restart the server.
      If I ran these services from command prompt, is it possible ?
      I don’t have knowledge on spring boot. Trying to understand it. Hope you understand my concerns.

        comments user
        Piotr Mińkowski

        Ok, I understand. But it is rather an anti-pattern for microservices to deploy all of them on one external server.

      comments user
      Thrinath

      So you mean to say, I should run all services from command prompt. Right ?
      If I run from command prompt, by any chance do I need to restart the server anytime ?

      comments user
      Thrinath

      Thanks for your quick responses.
      To execute all micro services (jar files) at once, which is the best method ?
      I mean Batch script is better or any other ?

        comments user
        Piotr Mińkowski

        I think it is ok

comments user
shafiqksm

Thank you for the tutorial. How to run the sample applicaiton

    comments user
    Piotr Mińkowski

    if you are using IDE like Eclipse or IntelliJ just run main class, for example pl.piomin.microservices.account.Application for account-service. You can also build your application with maven and that run using java -jar…

      comments user
      shafiqksm

      Thank you

comments user
satya

Reblogged this on HelpEzee.

comments user
Vidya

Hi Piotr,
Thanks for the tutorial. I tried creating two instances of account-service using the command: “java -jar target/account-service.jar -Dserver.port=2223” . But, am getting the below exception: “java.net.BindException: Address already in use”. hwy it is not taking 2223 port. Please help.

    comments user
    Piotr Mińkowski

    Hi,
    Use option -DPORT=2223. Take a look on application.yml. There are property you can see below. That’s why you need set -DPORT.

    server:
    port: ${PORT:2222}

      comments user
      Vidya

      Hi Piotr,
      I tried the way you have suggested. “java -jar target/account-service.jar -DPORT=2223”. Still am getting the BindException. I could see the below line in the log: Tomcat initialized with port(s): 2222 (http)

        comments user
        Piotr Mińkowski

        Try like that: java -jar -DPORT=2223 target/account-service.jar. Still the same?

comments user
Vidya

Thank you Piotr. It worked.

comments user
Vidya

Hi Piotr,

I have a doubt. Is it possible to balance the load among the different instances of a microservice. Say for example, can we limit the number of requests to be processed by an instance should be 50 and the rest has to be processed by another instance.
Do we require any configuration changes related to ribbon. Please help.

    comments user
    Piotr Mińkowski

    Hi,
    Yes, it is possible. If you have more than instance registered in discovery server it is automatically load balanced by Zuul and by your microservice if you use Feign client or @LoadBalanced with Srpign RestTemplate

comments user
Hùng Nlk

Hi Piotr,
What is the app you used to design diagram?

    comments user
    Piotr Mińkowski

    Google Drawings 🙂

comments user
Casey Drake

I am glad you came up with this post! I was looking for ways to build microservices using Zuul and this has helped me a lot. Expecting new posts from you on other microservice development aspects.

comments user
zhenya

HI Piotr! I have Up Eureka, Zuul, account-server. then tried to run http://localhost:8765/api/account on Postman,, return status is 404 not found , please Help!

comments user
zhenya

Hi Piotr! love your post! could you please share the steps that how to create the sample microservice listed above? thanks much

comments user
zhenya

Hi Piotr! I am wondering once each service was created in this new microservice framework, is it possible to change its name?

    comments user
    Piotr Mińkowski

    Hi,
    Of course it is possible. You may set the application name with property ${spring.application.name}

comments user
zhenya

Hi Piotr! I am wondering how to organize the customer-service, discovery-service, gateway-service, zipkin-service into Sample-spring-microservices project ? thanks much!!!!!!

comments user
Jyothi Gorripudi

Hi poor! Can we be able to apply fallback functionlaity from the zuul side..

comments user
bam bus

Hello Piotr.

I have configured eureka-server and its working correctly. For the client side I have one application with two controllers. In the first controller I have 7 services and in the second i have one. The problem for me is when I set Eureka Client in my client app in Eureka dashboard I can only see the instance for the whole App.

The question is how to tell eureka to discover every service that I have in the application?

    comments user
    Piotr Mińkowski

    Hello,
    It works properly. It doesn’t matter how many controllers have you defined in your application. You register the application in Eureka, no each controller separately. There is no such the option.

      comments user
      bam bus

      Okay. Thank you for the good answer and the quick response.

comments user
bam bus

Okay. Thank you for the good answer and the quick response.

comments user
bam bus

Can I register vertx app (packed in spring boot) on Eureka (should i use Feign client on services) ?
Can I use vertx instead of zuul api gw ?
I am using vertx to access the services but I am using hardcoded port and ip which are defined in the vertx api gw. I want to use the funcionality of Zuul api gw, to access services without specifing ip address and port in our Vertx api gw.

    comments user
    Piotr Mińkowski

    Well, I don’t that Vert.x has integration with Eureka. You have two choices in my opinion. You may use other discovery server like Consul (see my article about Vertx with microservices). You can also use Eureka REST API directly

comments user
Sandeep B

Hi,
Could you please tell me how to perform multipart file upload using feign client?

comments user
PM

i am trying to implement Zuul->Eureka->service to use https instead of http using selfsigned certs. Did someone did this? If so can you share the code? Thanks.

comments user
Ajit

how can i deploy it in websphere as an EAR

    comments user
    Piotr Mińkowski

    You can’t. It is based on Spring Boot

comments user
Jandos Iskakov

Hello Piotr! Thanks for nice article. I want to know if it is possible to have multiple instances of Zuul. I need in case if one instance fails. I have searched everywhere in internet and could not found information about Zuul cluster. It is crucial because if you have only one Zuul it is actually bottleneck and could be a problem if it shuts down.

    comments user
    Piotr Mińkowski

    Hello. You may have as many instances as you want. But why would you like to have them in the cluster?

      comments user
      Jandos Iskakov

      Hello. Imagine if one zuul server shuts down due to some technical reasons, then the whole system is stuck. You can have as many of microservices and eureka servers and it is obvious. Eureka servers can even replicate. But what about zuul, frontend only bound to one port zuul. So how can i achieve this. How to route to another zuul if one is not available.

        comments user
        Piotr Mińkowski

        Well, like I said you may have many instances of Zuul. Then you should provide failover on the client side. Cluster is not needed there

comments user
Jandos Iskakov

Ok, i assume it is a problem of frontend which zuul to hit then.

comments user
Rashmi

Hi, I am using eclipse IDE. I have 1 instance of the service running on the eureka server. Could you tell me how to enable a second instance of the same service? How will I be able to achieve this using my IDE?
Thanks.

    comments user
    Piotr Mińkowski

    Hi. You mean one instance registered in Eureka server? If you are using eclipse just edit run command of your application (Run configurations…) and add VM parameter -DPORT=… or -Dserver.port=…, and just run the second instance of your application.

      comments user
      Rashmi

      Yes, one instance of the service is already registered in eureka server. (service is running on port 8080). Now I want another instance of the same service to run on say 8081.
      I add -DPORT=8081 in the run configuration and tried to run the service second time, but it fails.

        comments user
        Piotr Mińkowski

        Why it fails?

          comments user
          Rashmi

          Hey, its working. Thanks!

comments user
ManiKanta Kandagatla

Hi piotr. I need the following scenario
E.g.:
Incoming requests: 1
No.of instances of customer service: 1
Incoming requests: 100
No.of instances of customer service: 10(on 10 different ports)
Incoming requests: > 10000
No.of instances of customer service: 100(on hundred ports)
Is there a way to configure spanning individual services(customer service) in zuul/eureka server? How does load balancing work on single ribbon if only single instance is launched ?

    comments user
    Piotr Mińkowski

    Hi,
    Spring Cloud does not provide autoscaling mechanisms out-of-the-box. You would have to use some cloud providers like Pivotal’s Cloud Foundry or HashiCorp’s Nomad, and deploy your microservice there. If only one instance of service is launched the 100% traffic goes to that instance 🙂

comments user
ManiKanta Kandagatla

Thanks for a quick response.
I have one more question, I am importing the git project into Spring tool suite but unable to resolve import errors. I have googled it and most of the answers are saying to use
org.springframework.boot
spring-boot-starter-parent
2.0.2.RELEASE

in pom.xml. But when i am doing it, the whole dependency tree is broken. Could you please tell me how to configure your code in STS. Right now i am building and running the complete codebase through maven commandline.

    comments user
    ManiKanta Kandagatla

    Sorry for the inconvenience. I have imported the projects as Maven projects and it solved the issues

comments user
Deepak

Hi Piotr, I am new to microservices Could you please help me with this question – what design pattern the microservices is based upon?

Regards

    comments user
    Piotr Mińkowski

    Hi Deepak,
    There are many patters. For example service discovery, distributed configuration, API gateway.

comments user
Sathish

Hi Piotr,
Can we configure multiple instance with different ports like 2221,2222,2223 for single service in application.yml apart from using -DPORT option?

    comments user
    Piotr Mińkowski

    Hi,
    Well you can create different spring profiles with different ports or set port to 0, what means that it would be automatically random during boot.

comments user
manasamittal94

Good post..Keep on sharing..

    comments user
    Piotr Mińkowski

    Thanks 🙂

comments user
shoyrasharma

What are the Top best Tools for Monitoring Microservices?
https://www.decipherzone.com/blog-detail/top-best-tools-monitoring-microservices
Monitoring systems become a critical thing for having a robust system running in production. But moving to microservices can break the monitoring strategy as traditional methods alone cannot connect performance and dependencies across the distributed architecture. It also leads to an increase in your mean time to resolution (MTTR).

Leave a Reply