Spring Cloud — OpenFeign ile Rest Client

Ahmet Cokgungordu
4 min readOct 9, 2019

Bu yazımda Spring Boot uygulamaları için Spring Cloud OpenFeign kullanarak bir Rest Client yapacağız.

Feign, Feign anotasyonları ve JAX-RS anotasyonları ile kolaylıkla kullanılabilir ve web servis istemcileri yazmayı sağlar. Bunun yanında Spring Web’de kullanılanla aynı HttpMessageConverters’ı kullanır ve Spring MVC anotasyonlarını destekler.

Feign’i kullanmanın en güzel yanı servis çağırmak için herhangi bir kod yazmamıza gerek olmamasıdır.

Bağımlılıklar

Bir Spring Boot web projesi oluşturarak spring-cloud-starter-openfeign bağımlılığını pom.xml dosyamıza ekliyoruz.

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

Bunun için öncelikle spring-cloud-dependencies bağımlılıklarını eklememiz gerekir.

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

spring-cloud-starter-openfeign ve spring-cloud-dependencies bağımlılıklarının en son versiyonlarını Maven Central’da bulabilirsiniz.

Feign Client

Feign Client yapmak için, main sınıfımıza @EnableFeignClients eklememiz gerekiyor.

@SpringBootApplication
@EnableFeignClients
public class ExampleApplication {

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

@EnableFeignClients anotasyonu ile, Feign Client olarak tanımlanmış Interface’lerin bulunmasını etkinleştirmiş oluyoruz.

@FeignClient anotasyonunu kullanarak bir Feign Client tanımı yapıyoruz.

@FeignClient(value = “ticket-service”, url = “https://ticketservice.com/")
public interface TicketServiceClient {

@RequestMapping(method = RequestMethod.GET, value = “/tickets”)
List<Ticket> getTickets();

@RequestMapping(method = RequestMethod.GET, value = “/tickets/{ticketId}”, produces = “application/json”)
Ticket getTicketById(@PathVariable(“ticketId”) Long ticketId);
}

Bu örnekte, TicketService API’lerinden data çekmek için bir Client yapılandırdık.

@FeignClient anotasyonundaki “value” değeri zorunludur. URL adı ise isteğe bağlı bir değerdir. Bu örnekte API ana URL’ini belirttik.

Ayrıca, bu Interface bir Feign Client olduğu için, ulaşmak istenilen API’leri deklare etmek için Spring Web anotasyonlarını kullanabiliriz.

Yapılandırma

Her Feign Client özelleştirilebilir componentlerden oluşmaktadır.

Spring Cloud, FeignClientsConfiguration sınıfını kullarak tanımlanmış her bir Client için yeni bir varsayılan ayar seti oluşturur.

Bu sınıf aşağıdaki Beanleri içermektedir.

Decoder: ResponseEntityDecoder’i kullanarak Response verisini çözmek için kullanılır.
Encoder: SpringEncoder’i kullanarak RequestBody kodlanır.
Logger: Varsayılan log mekanizması olarak Slf4jLogger kullanılır.
Contract: SpringMvcContract ile anotasyonlar işlenir.
Feign-Builder: Componentleri oluşturmak için HystrixFeign.Builder kullanılır.
Client: LoadBalancerFeignClient veya varsayılan olarak Feign Client kullanılır.

Bean ile Yapılandırmalar

Yukarıdaki Bean’lerden birini veya daha fazlasını özelleştirmek istiyorsak, FeignClient anotasyonunu eklediğimiz yere @Configuration ekleyerek override yapabiliriz.

@FeignClient(value = “ticket-service”,
url = “https://ticketservice.com/”,
configuration = MyClientConfiguration.class)

@Configuration
public class MyClientConfiguration {

@Bean
public OkHttpClient client() {
return new OkHttpClient();
}
}

Bu örnekte, Feign’e HTTP/2'yi desteklemesi için varsayılan kullanım yerine OkHttpClient’ı kullanmasını belirtiyoruz.

Feign, request ile daha fazla headers gönderen ApacheHttpClient dahil olmak üzere farklı kullanım durumları için birden fazla istemciyi destekler.

Bu clientları kullanmak için, aşağıdaki gerekli bağımlılıkları pom.xml dosyamıza eklememiz gereklidir.

<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>

<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>

feign-okhttp ve feign-httpclient bağımlılıklarının en son versiyonlarını Maven Central’da bulabilirsiniz.

Properties Dosyasını Kullanarak Yapılandırmalar

@Configuration sınıfını kullanmak yerine, aşağıdaki application.yml örneğinde gösterildiği gibi Feign Client yapılandırması yapabiliriz.

feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic

Bu yapılandırma ile uygulamada tanımlanan her Client bağlantısı için zaman aşımı süresini 5 saniyeye çekmiş oluyoruz ve log seviyesini basic olarak ayarlıyoruz.

Bu şekilde tüm @FeignClient’lar için nesneleri yapılandırmış olduk. İsteğe bağlı olarak herhangi bir Feign Client için yapılandırmayı yaparken Client adını aşağıdaki gibi bildirmemiz gerekirdi.

feign:
client:
config:
ticket-service:

Hem @Configuration bean hem de properties yapılandırmasını kullanırsak, properties yapılandırması @Configuration bean yapılandırmasını geçersiz kılar.

Interceptor Oluşturma

Feign tarafından sağlanan başka bir özellikte Interceptor eklemektir.

Interceptor, bütün HTTP request/response lar için, kimlik doğrulamasından loglamaya kadar çeşitli görevleri gerçekleştirebilir.

Her talebe temel bir kimlik doğrulama ekleyen bir Interceptor yazalım.

@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
requestTemplate.header(“user”, username);
requestTemplate.header(“password”, password);
requestTemplate.header(“Accept”,
ContentType.APPLICATION_JSON.getMimeType());
};
}

Bu Interceptor’ımızı @Configuration sınıfına eklememiz yeterlidir veya daha öncede bahsedildiği gibi yapılandırma dosyasında belirtebiliriz.

feign:
client:
config:
default:
requestInterceptors:
com.baeldung.cloud.openfeign.TicketServiceInterceptor

Hystrix Desteği

Feign, Hystrix’i desteklemektedir. Eğer etkinleştirilirse fallback metodunu kullanabiliriz.

Fallback metodu ile, bir servis çağrısı başarısız olduğunda, hata fırlatmak yerine, servis çağrısı eylemini başka bir yöntemle gerçekleştirmeye çalışmak için alternatif bir yol uygulayabiliriz.

Bunun için öncelikle properties içinde “feign.hystrix.enabled” parametresini “true” yaparak Hystrix’i etkinleştirmemiz gerekiyor. Hystrix ile ilgili detaylı bilgi için Spring Cloud — Netflix Hystrix ile Circuit Breaker yazımı inceleyebilirsiniz.

Aşağıdaki kod ile servis çağrısı başarısız olduğunda fallback metodu uygulanmaktadır.

@Component
public class TicketServiceFallback implements TicketServiceClient {

@Override
public List<Ticket> getTickets() {
return Collections.emptyList();
}

@Override
public Ticket getTicketById(Long ticketId) {
return null;
}
}

Feign’e fallback metodunun sağlandığını bildirmek için, fallback sınıfımızı da @FeignClient anotasyonunda belirtiyoruz.

@FeignClient(value = “ticket-service”,
fallback = TicketServiceFallback.class)
public interface TicketServiceClient {
// API’ler
}

Loglama

Her bir Feign Client varsayılan olarak bir logger oluşturur.

Logların kaydedilmesini etkinleştirmek için application.properties yapılandırma dosyamızda paket adını kullarak bildirmemiz gerekiyor.

logging.level.com.baeldung.cloud.openfeign.client: DEBUG

Bunun yanı sıra bir paket içerisindeki yalnızca belirli bir Client için belirlemek istiyorsak, sınıfın tam adını yazmamız gerekiyor.

logging.level.com.baeldung.cloud.openfeign.client.TicketServiceClient: DEBUG

Feign loglama mekanizması yalnızca DEBUG seviyesine yanıt vermektedir.

Logger.Level ise her Client için ne kadar seviyede loglama yapılacağını belirtmek için kullanılmaktadır. Örneğin;

@Configuration
public class ClientConfiguration {

@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}

Yukarıdaki ayar ile request ve response için body, headers ve metadata verilerinin hepsinin loga yazılmasını sağlamış oluruz.

Log kaydı seviyeleri toplamda 4 adettir.

  • NONE: Varsayılan seviye budur ve log kaydetmesi yapılmamaktadır.
  • BASIC: Yalnızca request metotları loglar. URL ve response durum bilgisi gibi.
  • HEADERS: Temel bilgilerin yanı sıra request ve response’ların headers bilgilerini de loglar.
  • FULL: Request ve response için body, headers ve metadata verilerinin hepsinin loglanmasını sağlar.

ErrorDecoder ile Error Handling (Hata İşleme)

Feign’in varsayılan error handling mekanizması her zaman sadece FeignException türünde bir hata fırlatmaktadır.

Bu şekilde bir davranış kullanışlı olmamaktadır. Bu atılan FeignException hatasını özelleştirmek için bir CustomErrorDecoder kullanabiliriz.

public class CustomErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {

switch (response.status()){
case 400:
return new BadRequestException();
case 404:
return new
NotFoundException();
default:
return new
Exception(“Generic error”);
}
}
}

Yukarıdaki sınıfta, response durum kodlarına göre özelleştirilmiş hataları fırlatıyoruz.

CustomErrorDecoder sınıfının aktif olması için @Configuration sınıfına bean tanımlamamız gerekmektedir.

@Configuration
public class ClientConfiguration {

@Bean
public ErrorDecoder errorDecoder() {
return new CustomErrorDecoder();
}
}

Bu yazımızda bir Client’ı nasıl yapılandıracağımızı ve özelleştirebileceğimizi, requestlerimiz için Interceptor tanımını nasıl yapacağımızı görmüş olduk. Ayrıca Hystrix ve ErrorDecoder kullanarak hataları nasıl yakalacağımızı ve özelleştirebileceğimizi de uygulamalı şekilde gördük.

--

--