Service Discovery & Communication
-
Generate a new stand-alone Spring application using Spring Initialzr. (Can find the setup folder)
-
Fill up the necessary fields (Group, Artifact and Dependencies) as shown below.
Gruop: com.eteration
Artifact: discovery-server
Project: maven
Dependicies: Spring Web, Eureka Server
-
Then click the generate button(ctrl+enter) project will be downloaded as a compressed file, extract it to a suitable directory
-
Launch Eclipse EE and click the import project.
-
The generated project as an Existing Maven Project as shown below.
- Click the browse and select the extracted file(config-server).
- If everything is done correctly, one should have a similar folder structure as displayed below.
-
Let's use customer-service, product-service, order-service example, config-server(We can find the setup folder).
-
Import the projects in Eclipse.
-
After importing the downloaded project to Eclipse, make the following modifications in DiscoveryServerApplication.java to enable the discovery server by using the annotation @EnableEurekaServer at class level. Finally like this.
package com.eteration.discoveryserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class DiscoveryServerApplication {
public static void main(String[] args) {
SpringApplication.run(DiscoveryServerApplication.class, args);
}
}
- Now we have a discovery-server, we need to register other services as discovery-client, to do this add the following dependency to each pom.xml files of all corresponding services (customer, product and order):
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
-
After this addition one should “refresh” all Maven Projects so that the dependency manager will download the dependency added. Right-click on any project, and from the menu hover, select all on Maven and from there click on Update Project.
-
After dependencies are downloaded, add @EnableDiscoveryClient annotation to CustomerServiceApplication.java, ProductServiceApplication.java, OrderServiceApplication.java at class level as below:
package com.eteration.customerservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class CustomerServiceApplication {
public static void main(String[] args) {
SpringApplication.run(CustomerServiceApplication.class, args);
}
}
- If you run discovery-server with default properties it will complain. Because by default it will try to register itself. To deal with this situation, create an application.yaml under src/main/resources as shown below:
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
server:
enableSelfPreservation: false
Start Feign Client Implementation
- Add the following dependency to pom.xml file of order-service to begin Feign Client implementation:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- After refreshing the project as explained before, add @EnableFeignClients annotations to OrderServiceApplication.java at class level. These annotations do what they say they do. Like this:
package com.eteration.orderservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
-
Now it is time to implement Feign Clients in order-service, to do this follow the instructions below and in case of difficulty see the implemented versions below:
-
Create the client package: com.example.demo.orderservice.client
- Create a class : CustomerDTO.java
- Modify this class as below:
package com.eteration.orderservice.client;
public class CustomerDTO {
private int id;
private String name;
private String address;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
- Create an interface: CustomerServiceClient.java
- Modify this class as below:
package com.eteration.orderservice.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("customer-service")
public interface CustomerServiceClient {
@GetMapping("/customers/{customerId}")
public CustomerDTO readCustomerById(@PathVariable("customerId")long id);
}
- Create a class: ProductDTO.java
- Modify this class as below:
package com.eteration.orderservice.client;
public class ProductDTO {
private long id;
private String name;
private int price;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
- Create a inteface: ProductServiceClient.java
- Modify this class as below:
package com.eteration.orderservice.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("product-service")
public interface ProductServiceClient {
@GetMapping("/products/{productId}")
public ProductDTO readProduct(@PathVariable("productId") long id);
}
- Modify OrderService.java, OrderServiceApplication.java and as shown below:
package com.eteration.orderservice.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import com.eteration.orderservice.client.CustomerDTO;
import com.eteration.orderservice.client.CustomerServiceClient;
import com.eteration.orderservice.client.ProductDTO;
import com.eteration.orderservice.client.ProductServiceClient;
import com.eteration.orderservice.controller.OrderController;
import com.eteration.orderservice.dto.OrderRequestDTO;
import com.eteration.orderservice.dto.OrderResponseDTO;
@Service
public class OrderService {
@Autowired
private ProductServiceClient productServiceClient;
@Autowired
private CustomerServiceClient customerServiceClient;
private static Logger logger = LoggerFactory.getLogger(OrderService.class);
public OrderResponseDTO createOrder(@RequestBody OrderRequestDTO orderRequest)
{
logger.info(orderRequest.toString());
OrderResponseDTO orderResponseDTO = new OrderResponseDTO();
ProductDTO product = productServiceClient.readProduct(orderRequest.getProductId());
CustomerDTO customer = customerServiceClient.readCustomerById(orderRequest.getCustomerId());
orderResponseDTO.setOrderId(orderRequest.getCustomerId() +"_"+orderRequest.getProductId() );
orderResponseDTO.setTotalAmount(product.getPrice()*orderRequest.getCount());
orderResponseDTO.setMessage("Customer information: "+customer.getName() +",Customer address: "+customer.getAddress());
return orderResponseDTO;
}
}
- OrderServiceApplication.java
package com.eteration.orderservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
- OrderRequestDTO.java
package com.eteration.orderservice.dto;
public class OrderRequestDTO {
private long customerId;
private long productId;
private int count;
@Override
public String toString() {
return "OrderRequestDTO [CustomerId: " + customerId + ", ProductId: " + productId + ", Order count: " + count + "]";
}
public long getCustomerId() {
return customerId;
}
public void setCustomerId(long customerId) {
this.customerId = customerId;
}
public long getProductId() {
return productId;
}
public void setProductId(long productId) {
this.productId = productId;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
- So let's add logger to product-service.
- ProductDTO.java add to toString() method using right-click -> source -> generate toString()..
package com.eteration.productservice.dto;
public class ProductDTO {
private String productId;
private String name;
private double price;
@Override
public String toString() {
return "ProductDTO [productId=" + productId + ", name=" + name + ", price=" + price + "]";
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
- ProductService.java add to logger.
package com.eteration.productservice.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import com.eteration.productservice.controller.ProductServiceController;
import com.eteration.productservice.dto.ProductDTO;
@Service
public class ProductService {
@Value("${kdv}")
private int kdv;
private static Logger logger = LoggerFactory.getLogger(ProductServiceController.class);
public ProductDTO readProduct(@PathVariable("productId") String productId) {
ProductDTO product = new ProductDTO();
product.setProductId(productId);
product.setName("Product" + productId);
product.setPrice(Math.random() * 100 * kdv);
logger.info(product.toString());
return product;
}
}
- So let's add logger to customer-service.
- CustomerDTO.java add to toString() method using right-click -> source -> generate toString()..
package com.eteration.customerservice.dto;
public class CustomerDTO {
private String id;
private String name;
private String surname;
private String address;
private String cardNumber;
@Override
public String toString() {
return "CustomerDTO [id=" + id + ", name=" + name + ", surname=" + surname + ", address=" + address
+ ", cardNumber=" + cardNumber + "]";
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCardNumber() {
return cardNumber;
}
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
}
- CustomerService.java add to logger.
package com.eteration.customerservice.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.eteration.customerservice.dto.CustomerDTO;
import com.eteration.customerservice.controller.CustomerController;
@Service
public class CustomerService {
private static Logger logger = LoggerFactory.getLogger(CustomerController.class);
public CustomerDTO readCustomer(String id) {
CustomerDTO customer = new CustomerDTO();
customer.setId(id);
customer.setAddress("İstanbul/Bahcelievler");
customer.setName("Murat");
customer.setSurname("Karakas");
customer.setCardNumber("1234-4545-4545-2325");
logger.info(customer.toString());
return customer;
}
}
- Now start running all services as a Java Application with the following order and go to the port where discovery-server is started on a browser to check results:
- config-server
- discovery-server
- product-service / customer-service
- order-service
- Check the Result http://localhost:8761
- Open a tool that is able to make HTTP requests Postman after running OrderServiceApplication.java, start constructing the response body as below and
- make the request:
{
"customerId": 4,
"productId": 2,
"count":"3"
}
- excpected output like this:
{
"orderId": "4_2",
"message": "Customer information: Murat,Customer address: İstanbul/Bahcelievler",
"totalAmount": 1479.0
}
- So let's check eclipse log in customer-service
- And also check using eclipse in product-service