Spring Cloud Microservices — Part 3— Using Feign for Simplifying REST Calls
Introduction
To see the complete list of Spring Cloud Microservices tutorial series, you can check this link.
In a microservices environment services quite often need to call other services such as Service A calls Service B, Service B calls Service C and so on. So, you need to define several HTTP clients (e.g. Apache HttpClient, OkHttp etc.) to call other services. It might get quite time consuming and error prone if you have plenty of services. Feign is used to simplify HTTP calls and lets you define your clients just by defining interfaces.
Below are the steps to initialize a Feign client with Spring:
1- Adding Dependencies
Add the following dependencies to project’s pom.xml:
<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-openfeign</artifactId>
</dependency>
Notice that we also include the dependency spring-cloud-starter-netflix-eureka-client
to enable service discovery for our service. It is because Feign supports communicating with a discovery server (e.g. Eureka) which will let you call services by service name without knowing the actual service URL.
2- Configuring the Application
Add @EnableFeignClients
and @EnableEurekaClient
annotations to one of the @Configuration
classes to enable Feign and service discovery for the application. See the example below:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderServiceApplication { public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
3- Integrating Feign Client
1- Let’s define a sample interface in our client application that will retrieve user orders from Order Service:
import com.oardic.userservice.model.Order;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;import java.util.List;@FeignClient("order-service")
public interface OrderServiceFeignProxy { @GetMapping("/orders/user/{userId}")
List<Order> getUserOrders(@PathVariable long userId);
}
In this example, @FeignClient
is a Spring Boot annotation that makes it possible to use Feign HTTP client. As stated in this link, Value “order-service”
is used to generate a load balancer client and it will communicate with the discovery server if service discovery is enabled (i.e. Eureka). So, “order-service”
is name of the service on the discovery server. If the service discovery is not enabled in your service, you need to set url
attribute in @FeignClient
annotation to be able to access the target service via URL.
Next we need to provide interface method(s) just like defining a REST controller. The code in the above example will take the user ID as a path variable and make GET requests to Order Service to retrieve user orders. The concrete method implementation in Order Service looks like below:
@GetMapping("/user/{userId}")
public List<Order> getUserOrders(@PathVariable long userId) {
log.info("getUserOrders request received for user ID {}", userId);
return orders.values().stream()
.filter(order -> order.getUserId() == userId)
.collect(Collectors.toList());
}
As you see, we can use exactly the same method signature to define Feign client. You can change the method or parameter names, what matters here is matching the API path and sending the correct types of values. The rest is handled by Feign client.
2- Inject the client to any @Component
class and that’s it. In our sample project, User Service uses Feign to call Order Service and retrieves the orders of the user. See the implementation in UserController class below:
@RestController
@RequestMapping("/users")
public class UserController { @Autowired
private OrderServiceFeignProxy orderServiceProxy; private final Map<Long, User> users = ...; @GetMapping("/{userId}")
public User getUser(@PathVariable long userId) {
User user = users.get(userId);
if (user != null) {
user.setOrders(orderServiceProxy.getUserOrders(userId));
}
return user;
}
}
You can check out this link for more information about Feign.
Conclusion
OpenFeign is quite helpful to simplify service-to-service calls, since you don’t need to implement REST clients to call other services. Also, it easily integrates with the discovery server, so we don’t need to know about the IP/port information of the target services.
In the next tutorial, we will discuss about distributed tracing with Sleuth and Zipkin to track down the service requests.
Next Tutorial: Distributed Tracing with Sleuth and Zipkin
Source Code
You can download the complete source code of this tutorial series from this link.
References
https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/