1. 개요

이 튜토리얼에서는 Netflix에서 개발 한 선언적 HTTP 클라이언트 인 Feign을 소개 합니다.

Feign은 HTTP API 클라이언트를 단순화하는 것을 목표로합니다. 간단히 말해서 개발자는 실제 구현이 런타임에 프로비저닝되는 동안 인터페이스를 선언하고 어노테이션을 달기 만하면됩니다.

2. 예

이 튜토리얼 전체 에서 REST API 엔드 포인트를 노출 하는 예제 서점 애플리케이션 을 사용합니다.

프로젝트를 쉽게 복제하고 로컬에서 실행할 수 있습니다.

mvn install spring-boot:run

3. 설정

먼저 필요한 의존성을 추가해 보겠습니다.

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
    <version>10.11</version>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-gson</artifactId>
    <version>10.11</version>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-slf4j</artifactId>
    <version>10.11</version>
</dependency>

외에 체하다 코어 (도에서 당기면) 의존성, 우리는 특히 몇 가지 플러그인을 사용합니다 : 체하다 - okhttp을 내부적으로 광장의 사용에 대한 OkHttp의 요청하도록 클라이언트를 체하다 - GSON JSON 프로세서와 구글의 GSON을 사용 feign-을 요청을 기록하기 위해 Simple Logging Facade사용하기위한 slf4j .

실제로 일부 로그 출력을 얻으려면 클래스 경로에서 선호하는 SLF4J 지원 로거 구현이 필요합니다.

클라이언트 인터페이스를 만들기 전에 먼저 데이터를 보관 하기위한 Book 모델을 설정합니다 .

public class Book {
    private String isbn;
    private String author;
    private String title;
    private String synopsis;
    private String language;

    // standard constructor, getters and setters
}

참고 : JSON 프로세서에는 최소한 "인수 없음 생성자"가 필요합니다.

사실, 우리의 REST 공급자는이다 하이퍼 미디어 기반의 API는 , 우리가 추가로 간단한 래퍼 클래스가 필요합니다 그래서 :

public class BookResource {
    private Book book;

    // standard constructor, getters and setters
}

참고 : 우리는 ' 킵 것이다 BookResource의 우리의 샘플 척하기 클라이언트 하이퍼 미디어 기능을 활용할 수 없기 때문에 간단하게!

4. 서버 측

Feign 클라이언트를 정의하는 방법을 이해하기 위해 먼저 REST 공급자가 지원하는 몇 가지 메서드와 응답을 살펴 보겠습니다.

모든 책을 나열하기 위해 간단한 curl shell 명령으로 시도해 봅시다. 모든 호출에 / api 접두사를 붙이는 것을 기억해야합니다 . 이는 애플리케이션의 servlet-context입니다.

curl http://localhost:8081/api/books

결과적으로 JSON으로 표시된 완전한 책 저장소를 얻을 수 있습니다.

[
  {
    "book": {
      "isbn": "1447264533",
      "author": "Margaret Mitchell",
      "title": "Gone with the Wind",
      "synopsis": null,
      "language": null
    },
    "links": [
      {
        "rel": "self",
        "href": "http://localhost:8081/api/books/1447264533"
      }
    ]
  },

  ...

  {
    "book": {
      "isbn": "0451524934",
      "author": "George Orwell",
      "title": "1984",
      "synopsis": null,
      "language": null
    },
    "links": [
      {
        "rel": "self",
        "href": "http://localhost:8081/api/books/0451524934"
      }
    ]
  }
]

가져 오기 요청에 ISBN을 추가하여 개별 도서 리소스를 쿼리 할 수도 있습니다 .

curl http://localhost:8081/api/books/1447264533

5. Feign 클라이언트

마지막으로 Feign 클라이언트를 정의하겠습니다.

우리는 사용합니다 @RequestLine의 는 HTTP 동사 및 인수로 경로 부분을 지정하는 어노테이션을. 매개 변수는 @Param 어노테이션을 사용하여 모델링 됩니다.

public interface BookClient {
    @RequestLine("GET /{isbn}")
    BookResource findByIsbn(@Param("isbn") String isbn);

    @RequestLine("GET")
    List<BookResource> findAll();

    @RequestLine("POST")
    @Headers("Content-Type: application/json")
    void create(Book book);
}

참고 : Feign 클라이언트는 텍스트 기반 HTTP API 만 사용하는 데 사용할 수 있습니다. 즉, 파일 업로드 또는 다운로드와 같은 이진 데이터를 처리 할 수 ​​없습니다.

그게 다야! 이제 인터페이스 기반 클라이언트를 구성 하기 위해 Feign.builder ()사용할 것 입니다. 실제 구현은 런타임에 프로비저닝됩니다.

BookClient bookClient = Feign.builder()
  .client(new OkHttpClient())
  .encoder(new GsonEncoder())
  .decoder(new GsonDecoder())
  .logger(new Slf4jLogger(BookClient.class))
  .logLevel(Logger.Level.FULL)
  .target(BookClient.class, "http://localhost:8081/api/books");

Feign은 JSON / XML 인코더 및 디코더와 같은 다양한 플러그인 또는 요청을위한 기본 HTTP 클라이언트를 지원합니다.

6. 단위 테스트

클라이언트를 테스트하기 위해 세 가지 테스트 케이스를 만들어 보겠습니다. org.hamcrest.CoreMatchers. *org.junit.Assert. *에 대해 정적 가져 오기를 사용합니다 .

@Test
public void givenBookClient_shouldRunSuccessfully() throws Exception {
   List<Book> books = bookClient.findAll().stream()
     .map(BookResource::getBook)
     .collect(Collectors.toList());

   assertTrue(books.size() > 2);
}

@Test
public void givenBookClient_shouldFindOneBook() throws Exception {
    Book book = bookClient.findByIsbn("0151072558").getBook();
    assertThat(book.getAuthor(), containsString("Orwell"));
}

@Test
public void givenBookClient_shouldPostBook() throws Exception {
    String isbn = UUID.randomUUID().toString();
    Book book = new Book(isbn, "Me", "It's me!", null, null);
    bookClient.create(book);
    book = bookClient.findByIsbn(isbn).getBook();

    assertThat(book.getAuthor(), is("Me"));
}

7. 추가 읽기

서비스를 사용할 수없는 경우 일종의 폴 백이 필요하면 HystrixFeign 을 클래스 경로에 추가 하고 HystrixFeign.builder ()로 클라이언트를 빌드 할 수 있습니다 .

Hystrix에 대해 자세히 알아 보려면 이 전용 자습서 시리즈확인하십시오 .

또한 Spring Cloud Netflix Hystrix를 Feign과 통합하려는 경우 여기에 전용 기사가 있습니다 .

또한 클라이언트 측로드 밸런싱 및 / 또는 서비스 검색을 클라이언트에 추가 할 수도 있습니다.

클래스 경로에 리본추가 하고 다음과 같이 빌더를 사용하여 이를 달성 할 수 있습니다.

BookClient bookClient = Feign.builder()
  .client(RibbonClient.create())
  .target(BookClient.class, "http://localhost:8081/api/books");

서비스 검색을 위해서는 Spring Cloud Netflix Eureka를 활성화하여 서비스를 구축해야합니다. 그런 다음 Spring Cloud Netflix Feign과 간단히 통합하십시오. 그 결과 리본로드 밸런싱이 무료로 제공됩니다. 이에 대한 자세한 내용은 여기에서 확인할 수 있습니다 .

8. 결론

이 기사에서는 텍스트 기반 API를 사용하기 위해 Feign을 사용하여 선언적 HTTP 클라이언트를 빌드하는 방법을 설명했습니다.

평소처럼이 자습서에 표시된 모든 코드 샘플 은 GitHub에서 사용할 수  있습니다.