Quick Guide to Microservices with Spring Boot 2, Eureka and Spring Cloud

Quick Guide to Microservices with Spring Boot 2, Eureka and Spring Cloud

There are many articles on my blog about microservices with Spring Boot and Spring Cloud. The main purpose of this article is to provide a brief summary of the most important components provided by these frameworks that help you in creating microservices and in fact explain to you what is Spring Cloud for microservices architecture.

The topics covered in this article are:

  • Using Spring Boot 2 in cloud-native development
  • Providing service discovery for all microservices with Spring Cloud Netflix Eureka
  • Distributed configuration with Spring Cloud Config
  • API Gateway pattern using a new project inside Spring Cloud: Spring Cloud Gateway
  • Correlating logs with Spring Cloud Sleuth

Before we proceed to the source code, let’s take a look on the following diagram. It illustrates the architecture of our sample system. We have three independent Spring Boot microservices, which register themself in service discovery, fetch properties from configuration service and communicate with each other. The whole system is hidden behind API gateway.

Currently, the newest version of Spring Cloud is Finchley.M9. This version of spring-cloud-dependencies should be declared as a BOM for dependency management.

<dependencyManagement>
   <dependencies>
      <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-dependencies</artifactId>
         <version>Finchley.M9</version>
         <type>pom</type>
        <scope>import</scope>
      </dependency>
   </dependencies>
</dependencyManagement>

Now, let’s consider the further steps to be taken in order to create a working microservices-based system using Spring Cloud. We will begin from Configuration Server.

The source code of sample applications presented in this article is available on GitHub in repository https://github.com/piomin/sample-spring-microservices-new.git.

Step 1. Building configuration server with Spring Cloud Config

To enable Spring Cloud Config feature for an application, first include spring-cloud-config-server to your project dependencies.

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-config-server</artifactId>
</dependency>

Then enable running embedded configuration server during application boot use @EnableConfigServer annotation.

@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {

   public static void main(String[] args) {
      new SpringApplicationBuilder(ConfigApplication.class).run(args);
   }

}

By default Spring Cloud Config Server stores the configuration data inside the Git repository. This is very good choice in production mode, but for the sample purpose file system backend will be enough. It is really easy to start with config server, because we can place all the properties in the classpath. Spring Cloud Config by default search for property sources inside the following locations: classpath:/, classpath:/config, file:./, file:./config.

We place all the property sources inside src/main/resources/config. The YAML filename will be the same as the name of service. For example, YAML file for discovery-service will be located here: src/main/resources/config/discovery-service.yml.

And last two important things. If you would like to start a config server with a file system backend you have to activate Spring Boot profile native. It may be achieved by setting parameter --spring.profiles.active=native during application boot. I have also changed the default config server port (8888) to 8061 by setting property server.port in bootstrap.yml file.

Step 2. Building service discovery with Spring Cloud Netflix Eureka

More to the point of the configuration server. Now, all other applications, including discovery-service, need to add spring-cloud-starter-config dependency in order to enable a config client. We also have to include dependency to spring-cloud-starter-netflix-eureka-server.

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

Then you should enable running an embedded discovery server during application boot by setting @EnableEurekaServer annotation on the main class.

@SpringBootApplication
@EnableEurekaServer
public class DiscoveryApplication {

   public static void main(String[] args) {
      new SpringApplicationBuilder(DiscoveryApplication.class).run(args);
   }

}

Application has to fetch property source from configuration server. The minimal configuration required on the client side is an application name and config server’s connection settings.

spring:
  application:
    name: discovery-service
  cloud:
    config:
      uri: http://localhost:8088

As I have already mentioned, the configuration file discovery-service.yml should be placed inside config-service module. However, it is required to say a few words about the configuration visible below. We have changed Eureka running port from default value (8761) to 8061. For the standalone Eureka instance we have to disable registration and fetching registry.

server:
  port: 8061

eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

Now, when you are starting your application with an embedded Eureka server you should see the following logs.

Once you have succesfully started application you may visit Eureka Dashboard available under address http://localhost:8061/.

Step 3. Building microservices using Spring Boot and Spring Cloud

Our microservice has to perform some operations during boot. It needs to fetch configuration from config-service, register itself in discovery-service, expose HTTP API and automatically generate API documentation. To enable all these mechanisms we need to include some dependencies in pom.xml. To enable a config client we should include starter spring-cloud-starter-config. Discovery client will be enabled for microservice after including spring-cloud-starter-netflix-eureka-client and annotating the main class with @EnableDiscoveryClient. To force Spring Boot application generating API documentation we should include springfox-swagger2 dependency and add annotation @EnableSwagger2.

Here is the full list of dependencies defined for my sample microservice.

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger2</artifactId>
   <version>2.8.0</version>
</dependency>

And here is the main class of application that enables Discovery Client and Swagger2 for the microservice.

@SpringBootApplication
@EnableDiscoveryClient
@EnableSwagger2
public class EmployeeApplication {

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

   @Bean
   public Docket swaggerApi() {
      return new Docket(DocumentationType.SWAGGER_2)
         .select()
         .apis(RequestHandlerSelectors.basePackage("pl.piomin.services.employee.controller"))
         .paths(PathSelectors.any())
         .build()
         .apiInfo(new ApiInfoBuilder().version("1.0").title("Employee API").description("Documentation Employee API v1.0").build());
}

   ...

}

Application has to fetch configuration from a remote server, so we should only provide a bootstrap.yml file with service name and server URL. In fact, this is the example of Config First Bootstrap approach, when an application first connects to a config server and takes a discovery server address from a remote property source. There is also Discovery First Bootstrap, where a config server address is fetched from a discovery server.

spring:
  application:
    name: employee-service
  cloud:
    config:
      uri: http://localhost:8088

There are not many configuration settings. Here’s the application’s configuration file stored on a remote server. It stores only HTTP running port and Eureka URL. However, I also placed file employee-service-instance2.yml on remote config server. It sets different HTTP ports for application, so you can easily run two instances of the same service locally based on remote properties. Now, you may run the second instance of employee-service on port 9090 after passing argument spring.profiles.active=instance2 during an application startup. With default settings you will start the microservice on port 8090.

server:
  port: 9090

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8061/eureka/

Here’s the code with implementation of REST controller class. It provides an implementation for adding a new employee and searching for employees using different filters.

@RestController
public class EmployeeController {

   private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeController.class);

   @Autowired
   EmployeeRepository repository;

   @PostMapping
   public Employee add(@RequestBody Employee employee) {
      LOGGER.info("Employee add: {}", employee);
      return repository.add(employee);
   }

   @GetMapping("/{id}")
   public Employee findById(@PathVariable("id") Long id) {
      LOGGER.info("Employee find: id={}", id);
      return repository.findById(id);
   }

   @GetMapping
   public List findAll() {
      LOGGER.info("Employee find");
      return repository.findAll();
   }

   @GetMapping("/department/{departmentId}")
   public List findByDepartment(@PathVariable("departmentId") Long departmentId) {
      LOGGER.info("Employee find: departmentId={}", departmentId);
      return repository.findByDepartment(departmentId);
   }

   @GetMapping("/organization/{organizationId}")
   public List findByOrganization(@PathVariable("organizationId") Long organizationId) {
      LOGGER.info("Employee find: organizationId={}", organizationId);
      return repository.findByOrganization(organizationId);
   }

}

Step 4. Communication between microservices with Spring Cloud Open Feign

Our first microservice has been created and started. Now, we will add other Spring Boot microservices that communicate with each other. The following diagram illustrates the communication flow between three sample microservices: organization-service, department-service and employee-service. Microservice organization-service collect list of departments with (GET /organization/{organizationId}/with-employees) or without employees (GET /organization/{organizationId}) from department-service, and list of employees without dividing them into different departments directly from employee-service. Microservice department-service is able to collect a list of employees assigned to the particular department.

In the scenario described above both organization-service and department-service have to localize other Spring Boot microservices and communicate with them. That’s why we need to include additional dependency for those modules: spring-cloud-starter-openfeign. Spring Cloud Open Feign is a declarative REST client that uses Ribbon client-side load balancer in order to communicate with other microservice.

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

The alternative solution to Open Feign is Spring RestTemplate with @LoadBalanced. However, Feign provides a more elegant way of defining clients, so I prefer it instead of RestTemplate. After including the required dependency we should also enable Feign clients using @EnableFeignClients annotation.

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableSwagger2
public class OrganizationApplication {

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

...

}

Now, we need to define client’s interfaces. Because organization-service communicates with two other Spring Boot microservices we should create two interfaces, one per single microservice. Every client’s interface should be annotated with @FeignClient. One field inside annotation is required – name. This name should be the same as the name of target service registered in service discovery. Here’s the interface of the client that calls endpoint GET /organization/{organizationId} exposed by employee-service.

@FeignClient(name = "employee-service")
public interface EmployeeClient {

   @GetMapping("/organization/{organizationId}")
   List findByOrganization(@PathVariable("organizationId") Long organizationId);

}

The second client’s interface available inside organization-service calls two endpoints from department-service. First of them GET /organization/{organizationId} returns organization only with the list of available departments, while the second GET /organization/{organizationId}/with-employees return the same set of data including the list employees assigned to every department.

@FeignClient(name = "department-service")
public interface DepartmentClient {

   @GetMapping("/organization/{organizationId}")
   public List findByOrganization(@PathVariable("organizationId") Long organizationId);

   @GetMapping("/organization/{organizationId}/with-employees")
   public List findByOrganizationWithEmployees(@PathVariable("organizationId") Long organizationId);

}

Finally, we have to inject Feign client’s beans to the REST controller. Now, we may call the methods defined inside DepartmentClient and EmployeeClient, which is equivalent to calling REST endpoints.

@RestController
public class OrganizationController {

   private static final Logger LOGGER = LoggerFactory.getLogger(OrganizationController.class);

   @Autowired
   OrganizationRepository repository;
   @Autowired
   DepartmentClient departmentClient;
   @Autowired
   EmployeeClient employeeClient;

...

   @GetMapping("/{id}")
   public Organization findById(@PathVariable("id") Long id) {
      LOGGER.info("Organization find: id={}", id);
      return repository.findById(id);
   }

   @GetMapping("/{id}/with-departments")
   public Organization findByIdWithDepartments(@PathVariable("id") Long id) {
      LOGGER.info("Organization find: id={}", id);
      Organization organization = repository.findById(id);
      organization.setDepartments(departmentClient.findByOrganization(organization.getId()));
      return organization;
   }

   @GetMapping("/{id}/with-departments-and-employees")
   public Organization findByIdWithDepartmentsAndEmployees(@PathVariable("id") Long id) {
      LOGGER.info("Organization find: id={}", id);
      Organization organization = repository.findById(id);
      organization.setDepartments(departmentClient.findByOrganizationWithEmployees(organization.getId()));
      return organization;
   }

   @GetMapping("/{id}/with-employees")
   public Organization findByIdWithEmployees(@PathVariable("id") Long id) {
      LOGGER.info("Organization find: id={}", id);
      Organization organization = repository.findById(id);
      organization.setEmployees(employeeClient.findByOrganization(organization.getId()));
      return organization;
   }

}

Step 5. Building API gateway using Spring Cloud Gateway

Spring Cloud Gateway is a relatively new Spring Cloud project. It is built on top of Spring Framework 5, Project Reactor and Spring Boot 2.0. It requires the Netty runtime provided by Spring Boot and Spring Webflux. This is a really nice alternative to Spring Cloud Netflix Zuul, which has been the only one Spring Cloud project providing API gateway for microservices until now.

API gateway is implemented inside module gateway-service. First, we should include starter spring-cloud-starter-gateway to the project dependencies.

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

We also need to have a discovery client enabled, because gateway-service integrates with Eureka in order to be able to perform routing to the downstream services. Gateway will also expose API specification of all the endpoints exposed by our sample microservices. That’s why we enabled Swagger2 also on the gateway.

@SpringBootApplication
@EnableDiscoveryClient
@EnableSwagger2
public class GatewayApplication {

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

}

Spring Cloud Gateway provides three basic components used for configuration: routes, predicates and filters. Route is the basic building block of the gateway. It contains a destination URI and list of defined predicates and filters. Predicate is responsible for matching on anything from the incoming HTTP request, such as headers or parameters. Filter may modify request and response before and after sending it to downstream services. All these components may be set using configuration properties. We will create and place on the configuration server file gateway-service.yml with the routes defined for our sample microservices.

But first, we should enable integration with the discovery server for the routes by setting property spring.cloud.gateway.discovery.locator.enabled to true. Then we may proceed to defining the route rules. We use the Path Route Predicate Factory for matching the incoming requests, and the RewritePath GatewayFilter Factory for modifying the requested path to adapt it to the format exposed by downstream services. The uri parameter specifies the name of target service registered in the discovery server. Let’s take a look at the following route definition. For example, in order to make organization-service available on gateway under path /organization/**, we should define predicate Path=/organization/**, and then strip prefix /organization from the path, because the target service is exposed under path /**. The address of target service is fetched for Eureka basing uri value lb://organization-service.

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
      - id: employee-service
        uri: lb://employee-service
        predicates:
        - Path=/employee/**
        filters:
        - RewritePath=/employee/(?.*), /$\{path}
      - id: department-service
        uri: lb://department-service
        predicates:
        - Path=/department/**
        filters:
        - RewritePath=/department/(?.*), /$\{path}
      - id: organization-service
        uri: lb://organization-service
        predicates:
        - Path=/organization/**
        filters:
        - RewritePath=/organization/(?.*), /$\{path}

Step 6. Enabling API specification on gateway using Swagger2

All Spring Boot microservices that is annotated with @EnableSwagger2 exposes Swagger API documentation under path /v2/api-docs. However, we would like to have that documentation located in a single place – on API gateway. To achieve it we need to provide a bean implementing SwaggerResourcesProvider interface inside gateway-service module. That bean is responsible for defining list storing locations of Swagger resources, which should be displayed by the application. Here’s the implementation of SwaggerResourcesProvider that takes the required locations from service discovery basing on the Spring Cloud Gateway configuration properties.

Unfortunately, SpringFox Swagger still does not provide support for Spring WebFlux. It means that if you include SpringFox Swagger dependencies to the project application will fail to start… I hope the support for WebFlux will be available soon, but now we have to use Spring Cloud Netflix Zuul as a gateway, if we would like to run embedded Swagger2 on it.

I created module proxy-service that is an alternative API gateway based on Netflix Zuul to gateway-service based on Spring Cloud Gateway. Here’s a bean with SwaggerResourcesProvider implementation available inside proxy-service. It uses ZuulProperties bean to dynamically load route definition into the bean.

@Configuration
public class ProxyApi {

   @Autowired
   ZuulProperties properties;

   @Primary
   @Bean
   public SwaggerResourcesProvider swaggerResourcesProvider() {
      return () -> {
         List resources = new ArrayList();
         properties.getRoutes().values().stream()
            .forEach(route -> resources.add(createResource(route.getServiceId(), route.getId(), "2.0")));
         return resources;
      };
   }

   private SwaggerResource createResource(String name, String location, String version) {
      SwaggerResource swaggerResource = new SwaggerResource();
      swaggerResource.setName(name);
      swaggerResource.setLocation("/" + location + "/v2/api-docs");
      swaggerResource.setSwaggerVersion(version);
      return swaggerResource;
   }

}

Here’s Swagger UI for our sample microservices system available under address http://localhost:8060/swagger-ui.html.

Step 7. Running applications

Let’s take a look on the architecture of our system visible on the following diagram. We will discuss it from the organization-service point of view. After starting organization-service connects to config-service available under address localhost:8088 (1). Based on remote configuration settings it is able to register itself in Eureka (2). When the endpoint of organization-service is invoked by external client via gateway (3) available under address localhost:8060, the request is forwarded to instance of organization-service basing on entries from service discovery (4). Then organization-service lookup for address of department-service in Eureka (5), and call its endpoint (6). Finally department-service calls endpoint from employee-service. The request was balanced between two available instances of employee-service by Ribbon (7).

Let’s take a look on the Eureka Dashboard available under address http://localhost:8061. There are four instances of microservices registered there: a single instance of organization-service and department-service, and two instances of employee-service.

Now, let’s call endpoint http://localhost:8060/organization/1/with-departments-and-employees.

Step 8. Correlating logs between independent microservices using Spring Cloud Sleuth

Correlating logs between different microservice using Spring Cloud Sleuth is very easy. In fact, the only thing you have to do is to add starter spring-cloud-starter-sleuth to the dependencies of every single microservice and gateway.

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

For clarification we will change default log format a little to: %d{yyyy-MM-dd HH:mm:ss} ${LOG_LEVEL_PATTERN:-%5p} %m%n. Here are the logs generated by our three sample microservices. There are four entries inside braces [] generated by Spring Cloud Stream. The most important for us is the second entry, which indicates on traceId, that is set once per incoming HTTP request on the edge of the system.

52 COMMENTS

Albert

This is a great tutorial, thanks!
If possible, could you add Auth Server (OAuth2) in? That would make this cover all … 🙂

Sebas

Hi, nice tutorial! Thanks!
I think you have a typo in step 2:

“Then you should enable running embedded discovery server during application boot by setting @EnableConfigServer annotation on the main class.”

It should say “… by setting @EnableEurekaServer …”, shouldn’t it?

    Piotr Mińkowski

    Thanks 🙂 Of course – I changed it

sluk3r

hi, I am new to Spring-cloud, could tell me to how run the application, please?

    Piotr Mińkowski

    You can run the main class using your IDE or just build the whole project using Maven and then run it like a standard java app with java -jar ….

    Chandru

    Hi, you were able to run the project, please let me know the steps

Stomer

awesome, that great!

Stomer

If possible, could you show cases configuration to auto scale service to many instances

Sridhar

We are using Spring Cloud Zuul in our production environment. We are planning to upgrade to Spring Boot 2.0 and Spring Cloud Finchley.RELEASE when Spring Cloud Finchley is officially released(GA Release).

Can we consider replacing Spring Cloud Zuul with Spring Cloud Gateway. Since Spring Cloud Gateway is based on a non-blocking api I feel it should give better performance thatn Spring cloud Zuul.

    Piotr Mińkowski

    I think it’s a good idea 🙂

Vivek

Hi very nice explanation. It hellp me to start writing microservices in easy go. I did not understand the proxy service role yet. can you please explain with the flow of the above example?
Thanks in Advance.

    Piotr Mińkowski

    Hi,
    Thanks 🙂 Proxy service is just an API Gateway, which guarantee that all the microservices are visible outside the system under single address and port. It proxies the request to the downstream services by context path

      Jeff

      You response would apply to the gateway-service, right? If I understand correctly, the proxy-service is a workaround to act as a gateway for Swagger UI since it doesn’t support WebFlux.

        Piotr Mińkowski

        Exactly

Cyrano

very good doc. thank you very much. it really help me to have a better insight on implementing microservices with spring-boot.

    Piotr Mińkowski

    Thanks 🙂

Savani

Hi Author – Thanks for very nice post, but I ‘m getting error mentioned here : https://stackoverflow.com/questions/51863731/caused-by-java-lang-illegalstateexception-you-need-to-configure-a-uri-for-the.
Could you please help me with this?

    Ivo

    Hello Savani

    I’m not the author but I’ll try to help you.
    Have you placed

    spring:
    profiles:
    active: native

    As the answer to the topic have said?

Suzan

How do you run this application with the Dockerfile?
Do I need to run profile “native” on Docker too?

    Piotr Mińkowski

    For config server? Yes

Vitor Saad

Congratulations on this project.
But I did not understand where Zuul fits.
The gateway project seems to be the single point of entry .. so what is zuul’s function?

    Piotr Mińkowski

    I used Zuul, because Spring Cloud Gateway didn’t support SpringFox Swagger. Maybe they have already fixed it in springfox library.

raghu

Thanks,
i have one suggestion , to change spring.cloud.gateway.discovery.locator.enabled to spring.cloud.gateway.enabled= true.

    piotr.minkowski

    That’s not the same.

Travis

Thank you for this guide.

I was wondering why use Feign for inter-service communication? Couldn’t the Organization service contact the Department service through the Gateway service?

    Piotr Mińkowski

    Why not? Feign is integrated with discovery so why you prefer communication through proxy?

swapnil skate

It may be achieved by setting parameter –spring.profiles.active=native during application boot

I did not understand this. Does this mean I have to create applications-native.properties file and put it inside config-service project and run the app. Since I am getting error when I rune with spring-boot:run -Dspring.profiles.active=native but still it gives me an error.

Can you please help me on this.

    Piotr Mińkowski

    It is not VM arg. You should set –spring.profiles.active=native

      swapnil skate

      Sorry to ask it again. But do I need to set
      spring:
      profiles.active: native

      in bootstrap.yml file?

      Can you help me to understand how I can run config service.

      Thanks

henok solomon

Thanks a lot for this post , clean and clear , I am currently introducing a micro-service paradigm to an existing exceedingly huge mobile payment platform currently entertaining > 10 million users , originally build using SOA approach. I got scared at first b/c of the volume of reading i been doing , this post summarized all the info i gathered so far . In-fact i began configuring the different module just to see how each component behave in my scenario. Hope to have a good r/n for the future with you .

Amit Patil

Hi Author,

Thank you for this full of informative post which overs couple of good spring cloud components explanation..

ThaoPT

Thanks !!!

Aji Sreekumar

Hi Poitr,
Thank you for the detailed explanation and I got a very good understanding on the configurations and its integration. I did a learning based on the source code(github.com/piomin/sample-spring-microservices-new) also. Would like to highlight an observation in the proxy-service module. Since the service is configured in the config-service and registered as a eureka client, It seems ProxyApplication class would require @EnableDiscoveryClient annotation. Not sure whether I have missed some alternatives, however for me it worked only with the given annotation. Kindly advise.
Kind regards,
Aji

    Piotr Mińkowski

    Hi Aji,
    Maybe you could update to the newest version of Spring Boot and Spring Cloud and it will be worked. Currently I have published online course available here https://www.youtube.com/watch?v=laI2yxthk3c. In the description you also have a link to the latest repo where I was using newest version of Spring Boot and cloud, and I didn’t have such problems.

Aji Sreekumar

Hi Piotr,
I am using Spring Boot version 2.2.6.RELEASE and Spring Cloud- Hoxton.RELEASE; Just checked the mentioned online course, the repository shared ,does not showing the ‘proxy-service’ module which does the swagger integration using Zuul. I am afraid whether I am referring the right repository (https://github.com/piomin/course-spring-microservices).

Kind regards,
Aji

    Piotr Mińkowski

    Hi. Sorry, I didn’t figure out you are asking about Zuul. It is possible since Spring Cloud Netflix Zuul has been moved to maintenance mode 1.5 year ago. But if you are looking for the example of exposing Swagger on gateway you ma be interested in this article https://piotrminkowski.com/2020/02/20/microservices-api-documentation-with-springdoc-openapi/. Since Zuul will be removed from Spring Cloud in the next release, you may think about migration to Spring Cloud Gateway. In my article you may also find how to use Springdoc for Swagger instead of SpringFox. Springdoc supports openapi 3 and reactive APIs.

boost

Hello Piotr,

First of all, thank you for this amazing tutorial !
Altough, I’m having an issue with Spring Gateway.

I set up a route using the following parameters :
” id: employee-service
uri: lb://employee-service
predicates:
– Path=/employee/** ”
However, I can only access my employee-service using the “/employee-service/endpoint” URI, the Path predicate isn’t working. Do you have any idea of what would cause that? 🙂

    Piotr Mińkowski

    Thanks 🙂 You should try to access it using just /employee path.

      boost

      I’ve already tried that as well.
      Setting the same path, filters, … with pure Java code works though.
      But I feel like it’s a less good practice to do it this way. :/

        Piotr Mińkowski

        You can remove the route declarations as well, and then you could access it using /employee-service path. For more details about Spring Cloud Gateway you may watch my online course: https://youtu.be/XIkSWHX38Tg

Harshit

I am facing issue in routing when I start services in my local machine.

My laptop is connected to my office VPN network and when I start eureka server,conifg, 3 client services, 1 spring cloud gateway in local environment all are registered with Eureka with IP:PORT.

I can see registered services on eureka portal like this : 192.168.225.153:config-service:8081, 192.168.225.153:employee-service:8082, 192.168.225.153:gateway-service:8085

With this configuration my spring cloud gateway not able to connect to downstream services (other 2 client service) on 192.168.225.153 IP.

Call to http://localhost:8085/employee/1 is not working. However when I directly make call to employee service using http://localhost:8082/1, it is working.

There is some issue between how gateway service is locating and connecting to downstream services. (e.g. employee, department)

If I disconnect from internet and restart all the services, Eureka registered services with machine name/host name.

I can see registered services on eureka portal like this : :config-service:8081, :employee-service:8082, :gateway-service:8085

Call to http://localhost:8085/employee/1 is now working. In this case, gateway is able to locate and connect with downstream services.

Any idea why services are not located by gateway when I am connected to my office VPN?

    piotr.minkowski

    Well, as you see they are registered under address 192.168.225.153, not `localhost`.

harshit

Thanks a lot Piotor for your detail explanation. I need your help to review one case. My department service is not able to locate employee service and ended up with below exception.

2020-07-24 17:20:57 ERROR [department-service,,,] Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.RetryableException: connect timed out executing GET http://employee-service/department/1%5D with root cause
java.net.SocketTimeoutException: connect timed out

I am running in my local Mac book.

I have bit modified bootstrap.yml to work with docker.

employee
spring:
application:
name: employee-service
cloud:
config:
fail-fast: true
discovery:
enabled: true
service-id: config-service

department
spring:
application:
name: department-service
cloud:
config:
fail-fast: true
discovery:
enabled: true
service-id: config-service

I have searched over net and found that discovery.enabled should be false. Is that the case?

fei zhao

I am a coder in China. when learned you microservice implemented by spring cloud, I learned a lot. The source code exists very well. and you write blogs to illustrate it. Thanks a lot.

    piotr.minkowski

    You’re welcome

Exit mobile version