Feign: Declarative web-service client and Eureka: Naming server for load balancing

Invoking other microservices made easy with load balancing

Nikitha Gullapalli
6 min readMay 15, 2023

Github : https://github.com/nikitha2/Currency-Exchange-Conversion-Microservices-Backend.git

Linkedin: https://www.linkedin.com/in/nikitha-gullapalli

Feign makes it easy to invoke other microservices. In this article we will make a call from CurrencyConversionService to CurrencyExchangeService.

CurrencyExchangeService tells us the conversion rate. Example from USD to INR. Call to http://localhost:8000/currency-exchange/from/USD/to/INR returns the x for 1USD = x INR. It uses h2 database.

CurrencyConversionService gets from, to and quantity as inputs. It then makes a call to CurrencyExchangeService for the conversion rate, multiplies quantity and conversion rate and returned the totalCalculatedAmount.

I got this example from a course I took on Udemy. I enjoyed and learnt a lot from the course. If you are looking for a course to learn microservices, I would strongly suggest it. Course : Master Microservices with Spring Boot and Spring Cloud.

1. Prerequisites :

In this project we are using a services connected to spring-cloud and have spring-security integrated. You can find steps to connect spring-cloud to microservice here and spring-security integration here. However, these are not a requirement to make calls to other microservices using Feign.

Make sure to import CurrencyExchangeService and SpringCloudConfigServer101. Run it! You should be able to make a call http://localhost:8000/currency-exchange/from/USD/to/INR. When prompted for credentials enter db-username and db-password. If you have your own microservices you want to make a call to. That will work too.

2. pom.xml dependency

Add pom.xml dependency to use feign in your microservice application you want a make a call from. For me it was CurrencyConversionService.

<!-- dependency for Feign. Makes intra-service calls easy-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

3. Add annotation @EnableFeignClients to your ServiceApplication class

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients
public class CurrencyConversionServiceApplication {

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

4. Add a Proxy interface to make calls to another service and FeignClientConfiguration

We can make calls to currency-exchange-service via FeignProxy. This is an interface and has an annotation @FeignClient.

@name: name of the proxy. I prefer giving the name of the service being called. Here it is currency-exchange-service. Can be found in CurrencyExchangeService application.properties file.

spring.application.name = currency-exchange-service

@url: currency-exchange-service base url

@ configuration: I have spring-security for currency-exchange-service. So had to pass username and password along with the request. If you do not have spring-security integrated. You can ignore this.

Else, create a FeignClientConfiguration class and pass in username and password for currency-exchange-service login.

Copy-paste the necessary endpoint call from currency-exchange-service controller and remove the body. Everything else is same as in the currency-exchange-service controller.

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import com.springboot.microservices.currencyconversionservice.model.CurrencyConversionEntity;

@Component
@FeignClient(name = "currency-exchange-service", url = "localhost:8000", configuration = FeignClientConfiguration.class)
public interface CustomerExchangeProxy {

@GetMapping("/currency-exchange/from/{fromCurrency}/to/{toCurrency}")
public CurrencyConversionEntity findByFromCurrencyAndToCurrency(@PathVariable String fromCurrency,
@PathVariable String toCurrency);
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.springboot.microservices.currencyconversionservice.configuration.DbConfiguration;
import feign.auth.BasicAuthRequestInterceptor;

@Configuration
public class FeignClientConfiguration {

@Autowired
DbConfiguration dbConfiguration;

@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor(dbConfiguration.getUsername(), dbConfiguration.getPassword());
}
}

5. Call proxy from DAO layer

Create an object of CustomerExchangeProxy and make the call. This will make the required service call and return back the response. Make sure you have an POJO class to populate the response in. Here it is CurrencyConversionEntity.

@Autowired
CustomerExchangeProxy customerExchangeProxy;
public CurrencyConversionEntity findByFromAndToAndQuantityWithFeign(String fromCurrency, String toCurrency,
BigDecimal quantity) {
HashMap<String, String> uriVariable = new HashMap<>();
uriVariable.put("fromCurrency", fromCurrency);
uriVariable.put("toCurrency", toCurrency);

CurrencyConversionEntity responseEntity = customerExchangeProxy.findByFromCurrencyAndToCurrency(fromCurrency, toCurrency);

return new CurrencyConversionEntity(responseEntity.getId(), responseEntity.getFrom(),
responseEntity.getTo(), responseEntity.getConversionMultiple(), quantity,
quantity.multiply(responseEntity.getConversionMultiple()), responseEntity.getEnvironment());
}

6. Run applications cloud-service, currency-exchange and currency-conversion

Run applications cloud-service, currency-exchange and currency-conversion.Now make a call to http://localhost:8100/currency-conversion-feign/from/USD/to/INR/quantity/20. You should get a 200 response as shown below. totalCalculatedAmount = quantity x conversionMultiple.

Conclusion

Calls to other microservices can be made without Feign. Feign just makes it easier and reduces the boiler-plate code making our code more readable when we have a lot of call.

To make a call without Feign use RestTemplate(). Example below shows how we can make a call to currency-exchange using RestTemplate().

http://localhost:8100/currency-conversion/from/USD/to/INR/quantity/20 will call findByFromAndToAndQuantityWithRestTemplate() and give the same response as feign.

public CurrencyConversionEntity findByFromAndToAndQuantityWithRestTemplate(String fromCurrency, String toCurrency,
BigDecimal quantity) {

HashMap<String, String> uriVariable = new HashMap<>();
uriVariable.put("fromCurrency", fromCurrency);
uriVariable.put("toCurrency", toCurrency);

String uri = "http://localhost:8000/currency-exchange/from/{fromCurrency}/to/{toCurrency}";
ResponseEntity<CurrencyConversionEntity> responseEntity = new RestTemplate().exchange(uri, HttpMethod.GET,
new HttpEntity<Object>(createHeaders(dbConfiguration.getUsername(), dbConfiguration.getPassword())),
CurrencyConversionEntity.class, uriVariable);

// Can be used if no authentication needed. In other words, if spring-security is not used
/** ResponseEntity<CurrencyConversionEntity> responseEntity = new RestTemplate().getForEntity(
"http://localhost:8000/currency-exchange/from/{fromCurrency}/to/{toCurrency}",
CurrencyConversionEntity.class,
uriVariable);
**/

CurrencyConversionEntity responseEntityBody = responseEntity.getBody();
return new CurrencyConversionEntity(responseEntityBody.getId(), responseEntityBody.getFrom(),
responseEntityBody.getTo(), responseEntityBody.getConversionMultiple(), quantity,
quantity.multiply(responseEntityBody.getConversionMultiple()), responseEntityBody.getEnvironment());
}

Eureka

When traffic to certail part of the application is high. Example for a shopping app if cart service is being used more. We need several instances of the services in different ports. How do we keep track of these instances and when a new request comes in for the service, how to determine which instance of the service to give the task to?

This is where naming services with load balancing is helpful. Each service registers itself to eureka client. This way when we make a proxy call to another service via feign. It gets the data from the instance of the service avalible (In our case when currency-converstion service makes a call to currency exchange service. We make the call via Eureka. Eureka determines if it should call port 8000 or 8001 of currency-exchange)

To achieve this follow the step below

Eureka server:

  1. Create a Eureka server using spring initializer. Add dependencies as shown below and import the maven project into eclipse.

2. Enable server by adding @EnableEurekaServer in Application class.

3. App application name, port and url in application.properties

spring.application.name= naming-server-eureka

#http://localhost:8761
server.port=8761

eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone = http://localhost:8761

4. Run Application class as java application. It should start without errors. Now goto http://localhost:8761 on the browser and you should be eureka server and it will show all the application currently up. As of now we dont have any services setup with eureka so it wont show any servives are up.

Eureka client:

  1. In the services simply add eureka client dependency in pom.
<!-- Eureka naming server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

and naming server url in application.properties file

#------------Eureka naming server------------
eureka.client.serviceUrl.defaultZone= http://localhost:8761/eureka

2. Now start the application and local eureka url http://localhost:8761/eureka in browser. You should see that your services are up.

Feign and Eureka work together. So too much configuration is not needed from our end.

--

--