Статьи

Создание собственных приложений для облачной среды в Spring — часть 4

После выполнения частей 1 , 2 и 3 этого руководства у нас есть базовая настройка и запуск приложения для микросервисов. Это просто, но включает в себя многие из требований быть облачным нативным приложением , поэтому у нас хорошее начало.

В Spring Cloud Netflix есть несколько дополнительных функций , которые особенно полезны и достойны демонстрации. В этом посте я подумал, что мы взглянем на Feign . Вместо того, чтобы описывать это сам, документация Spring Cloud делает хорошую работу

Feign является декларативным клиентом веб-сервиса. Это облегчает написание клиентов веб-сервисов. Для использования Feign создайте интерфейс и аннотируйте его. Он имеет поддержку подключаемых аннотаций, включая аннотации Feign и аннотации JAX-RS. Feign также поддерживает подключаемые кодеры и декодеры. В Spring Cloud добавлена ​​поддержка аннотаций Spring MVC и использования тех же HttpMessageConverters, которые используются по умолчанию в Spring Web. Spring Cloud интегрирует Ribbon и Eureka для обеспечения http-клиента с балансировкой нагрузки при использовании Feign.

Итак, если вы хотите создать клиент REST API, Feign — ваш друг. Выполнение вызовов REST API из одного сервиса в другой — очень распространенный шаблон в приложениях микросервисов, поэтому эта функциональность окажется особенно полезной. Также обратите внимание, что Spring Cloud добавляет поддержку для ленты и Eureka в Feign. Мы уже знакомы с тем, что делает Эврика из предыдущих частей этого урока , но что такое Лента?

Лента — это еще один проект от Netflix OSS, который представляет собой балансировщик нагрузки на стороне клиента. Это важно по очевидным причинам, когда мы начинаем рассматривать службы, распределенные по разным центрам обработки данных и географическим регионам. Например, Ribbon будет использовать Eureka, чтобы выяснить, к какому экземпляру службы он должен обратиться. Мы поговорим о ленте подробнее в следующем сообщении в блоге, а пока просто знаем, что это будет балансировать нагрузку на наши запросы от наших клиентов Feign.

Начало работы с Feign

Теперь, когда мы знаем, что такое Feign и почему мы хотим его использовать, давайте используем его в нашем примере приложения. Прямо сейчас, чтобы получить участников для гонок, мы должны сделать запрос к / гонкам, чтобы получить все гонки, а затем сделать запрос к / участники / гонкам / {id}чтобы получить участников для данной гонки. В некоторых ситуациях это может быть хорошо, но, возможно, не для всех ситуаций. Рассмотрим случай, когда наше приложение используется на мобильном устройстве. Поскольку сеть на мобильном устройстве, как правило, намного медленнее, чем настольный компьютер, мы можем ограничить количество сетевых запросов, которые нам нужно сделать, чтобы получить все необходимые нам данные. Другими словами, мобильный клиент может захотеть сделать один запрос, чтобы получить все данные гонки и участника вместо нескольких запросов. Давайте представим новый API для мобильных клиентов, который использует именно это.

Наш новый API должен будет использовать как сервис гонок, так и сервис участников. Поскольку он действительно возвращает данные о расах, имеет смысл жить в гоночном сервисе. Нам нужен способ вызова службы участников из службы Races, вот где приходит Feign. Сначала давайте откроем файл POM для нашего сервиса Races и добавим зависимость Feign в наш проект. В разделе зависимостей вашего POM добавьте следующую зависимость:

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

Теперь давайте откроем OcrRacesApplication.java в com.ryanjbaxter.spring.cloud.ocr.races . Давайте создадим новый интерфейс, который действует как наш REST-клиент для общения со службой участников. Создайте новый класс с именем PlayersClient со следующим кодом:

@FeignClient("participants")
interface ParticipantsClient {

    @RequestMapping(method = RequestMethod.GET, value="/races/{raceId}")
    List<Participant> getParticipants(@PathVariable("raceId") String raceId);

}

Первое, что вы заметите, это то, что этот интерфейс использует аннотацию под названием @FeignClient . Эта аннотация говорит Feign, что мы будем разговаривать с сервисом под названием «участники» . Feign будет использовать Eureka, чтобы выяснить правильный URL-адрес для службы участников. Остальная часть интерфейса должна выглядеть довольно знакомой, если вы знакомы с Spring MVC. @RequestMapping аннотацию на getParticipants метод говорит симулировать сделать GET запрос на обслуживание участников по пути / рас / raceId .

На этом этапе у вас будут ошибки компиляции в вашем интерфейсе, потому что в сервисе гонок нет класса с именем Участник . Если вы опытный Java-разработчик, ваш первый инстинкт, вероятно, будет сделать некоторый рефакторинг. Вы можете перейти к услуге для участников, чтобы извлечь Участникакласс в свой проект. Затем вы измените сервис участников и гонок, чтобы они зависели от этого нового проекта. Это укоренилось в наших умах из-за принципа СУХОЙ (не повторяйся), который говорит, что мы не должны копировать и вставлять код повсюду из-за того, что он станет не поддерживаемым. Это, безусловно, серьезная проблема, однако мы должны сбалансировать принцип DRY с другими принципами микросервисов. Проблема такого подхода к рефакторингу нашего приложения состоит в том, что теперь у нас есть общий класс, используемый двумя (или более) службами. Что происходит, когда один сервис должен внести изменения в этот класс? Если изменение достаточно радикальное, вы можете сломать другой сервис. Это означает, что сервисы не могут развиваться независимо друг от друга,что является одним из преимуществ, которые мы пытаемся достичь с помощью микросервисов.

В конце дня вы должны принять решение, которое подходит именно вам, вашей команде и вашему проекту. Хотите ли вы делиться кодом между вашими микросервисами или хотите получить возможность развивать свои сервисы независимо друг от друга? В этом случае мы НЕ будем следовать принципу СУХОЙ и создадим нового участника класс в нашем сервисе гонок. Почему? Подумайте о том, как бы вы работали, если бы создавали настоящее приложение для микросервисов промышленного уровня. Вы будете разработчиком в команде, которая отвечает за один сервис. Теоретически вы ничего не будете знать о реализации других сервисов, от которых вы зависите, единственное, на что вы можете положиться — это их публичный API. Они могут даже не быть реализованы на том же языке, который вы используете. Исходя из этой логики, для вас имеет смысл создать класс участников в вашем сервисе, соответствующий тому, что будет возвращать их общедоступный API. На мой взгляд, когда дело доходит до микросервисов, разделение кода между сервисами, как правило, не имеет смысла.

В OcrRacesApplication.java  создайте новый класс участников :

class Participant {
    private String firstName;
    private String lastName;
    private String homeState;
    private String shirtSize;
    private List<String> races;
    public Participant(String firstName, String lastName, String homeState,
            String shirtSize, List<String> races) {
        super();
        this.firstName = firstName;
        this.lastName = lastName;
        this.homeState = homeState;
        this.shirtSize = shirtSize;
        this.races = races;
    }

    public Participant(){}
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getHomeState() {
        return homeState;
    }
    public void setHomeState(String homeState) {
        this.homeState = homeState;
    }
    public String getShirtSize() {
        return shirtSize;
    }
    public void setShirtSize(String shirtSize) {
        this.shirtSize = shirtSize;
    }
    public List<String> getRaces() {
        return races;
    }
    public void setRaces(List<String> races) {
        this.races = races;
    }
}

Наконец нам понадобится новый класс Race, который содержит информацию об участниках гонки. Здесь снова у нас есть пара вариантов. Мы можем добавить список участников в наш существующий класс Race, но в результате мы получим это странное свойство участников в JSON, которое всегда будет пустым массивом при выполнении запросов к / races . Мне, как потребителю, странно иметь свойство, которое, казалось бы, никогда не используется, пока мы не назовем этот новый API, который мы создаем. Второй вариант — создать подкласс Race, который содержит участников. Добавьте новый класс с именем RacesWithParticipants:

class RaceWithParticipants extends Race {
    private List<Participant> participants;

    public RaceWithParticipants(Race r, List<Participant> participants) {
        super(r.getName(), r.getId(), r.getState(), r.getCity());
        this.participants = participants;
    }

    public List<Participant> getParticipants() {
        return participants;
    }

    public void setParticipants(List<Participant> participants) {
        this.participants = participants;
    }
}

 

Использование Feign Client

Теперь мы готовы создать наш новый API, который будет возвращать данные о гонках, включая информацию об участниках. В классе OcrRacesApplication, где у нас есть существующий API getRaces, создайте новый API с именем getRacesWithParticipants . Но прежде чем мы это сделаем, нам понадобится экземпляр ParticipantClient, который мы будем использовать в новом API для вызова службы участников. Добавьте новую переменную в класс:

@Autowired
private ParticipantsClient participantsClient;

Теперь добавьте новый API:

@RequestMapping("/participants")
    public List<RaceWithParticipants> getRacesWithParticipants() {
        List<RaceWithParticipants> returnRaces = new ArrayList<RaceWithParticipants>();
        for(Race r : races) {
            returnRaces.add(new RaceWithParticipants(r, participantsClient.getParticipants(r.getId())));
        }
        return returnRaces;
    }

Вот и весь код, который нам нужно написать, теперь давайте протестируем наш новый API. Запустите eureka, гонки, участников и веб-сервисы и перейдите по адресу http: // localhost: 8080 / races / members . (Убедитесь, что все службы зарегистрировались в Eureka, прежде чем пытаться использовать API.) Это должно вернуть некоторый JSON, который выглядит следующим образом:

[
   {
      "name":"Spartan Beast",
      "id":"123",
      "state":"MA",
      "city":"Boston",
      "participants":[
         {
            "firstName":"Ryan",
            "lastName":"Baxter",
            "homeState":"MA",
            "shirtSize":"S",
            "races":[
               "123",
               "456"
            ]
         }
      ]
   },
   {
      "name":"Tough Mudder RI",
      "id":"456",
      "state":"RI",
      "city":"Providence",
      "participants":[
         {
            "firstName":"Ryan",
            "lastName":"Baxter",
            "homeState":"MA",
            "shirtSize":"S",
            "races":[
               "123",
               "456"
            ]
         },
         {
            "firstName":"Stephanie",
            "lastName":"Baxter",
            "homeState":"MA",
            "shirtSize":"S",
            "races":[
               "456"
            ]
         }
      ]
   }
]

В то же время вы можете продолжать использовать API / Races по адресу  http: // localhost: 8080 / Race, чтобы просто получить данные о гонке.

Как вы можете видеть, Feign позволяет легко создавать REST-клиенты для других сервисов в вашем приложении для микросервисов. Просто используя несколько аннотаций и минимальный Java-код, вы можете легко создать REST-клиент для любого сервиса, который вам нужен.