Рубрики
Без рубрики

Открытие микросервиса Spring Boot и Flask с помощью Netflix Eureka

В этом руководстве мы рассмотрим, как вызвать микросервис Python Flask из микросервиса Spring Boot, используя Netflix Eureka и его обнаружение служб.

Автор оригинала: David Landup.

Вступление

В этом руководстве мы будем использовать Netflix Eureka , службу обнаружения микросервисов для объединения микросервиса Spring Boot с микросервисом Flask, объединяя службы, написанные на совершенно разных языках программирования и платформах.

Мы будем создавать две службы – Службу Конечного пользователя , которая представляет собой службу загрузки Spring, ориентированную на конечного пользователя, которая собирает данные и отправляет их в службу Агрегирования данных -службу Python, использующую Pandas для выполнения агрегирования данных и возвращающую ответ JSON в службу Конечного пользователя .

Открытие сервиса Netflix Eureka

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

Netflix Eureka инструмент обнаружения служб (также известный как сервер поиска или реестр служб ), который позволяет нам регистрировать несколько микросервисов и обрабатывает маршрутизацию запросов между ними.

Это центральный узел, где регистрируется каждая служба, и каждая из них взаимодействует с остальными через этот узел. Вместо отправки вызовов REST через имена хостов и порты – мы делегируем это Эврике и просто вызываем имя службы, зарегистрированной в хабе.

Для достижения этой цели типичная архитектура состоит из нескольких элементов:

Вы можете отключить сервер Eureka на любом языке, на котором есть оболочка Eureka, хотя, скорее всего, это делается на Java с помощью Spring Boot, поскольку это оригинальная реализация инструмента с официальной поддержкой.

Каждый сервер Eureka может зарегистрировать N Клиентов Eureka, каждый из которых, как правило, является индивидуальным проектом. Это также может быть сделано на любом языке или в любой среде, поэтому каждый микросервис использует то, что наиболее подходит для его задачи.

У нас будет два клиента:

  • Служба конечного пользователя (Клиент Eureka на базе Java)
  • Служба агрегирования данных (Клиент Eureka на основе Python)

Поскольку Eureka-это проект на основе Java, изначально предназначенный для решений Spring Boot, у него нет официальной реализации для Python. Тем не менее, мы можем использовать для этого оболочку Python, управляемую сообществом:

Имея это в виду, давайте сначала создадим сервер Эврика .

Создание сервера Эврика

Мы будем использовать Spring Boot для создания и обслуживания нашего сервера Eureka. Давайте начнем с создания каталога для размещения наших трех проектов, а в нем каталога для нашего сервера:

$ mkdir eureka-microservices
$ cd eureka-microservices
$ mkdir eureka-server
$ cd eureka-server

Каталог eureka-server будет корневым каталогом нашего сервера Eureka. Вы можете запустить проект Spring Boot здесь через интерфейс командной строки:

$ spring init -d=spring-cloud-starter-eureka-server

В качестве альтернативы вы можете использовать Spring Initializr и включить Сервер Эврика зависимость:

Если у вас уже есть проект и вы просто хотите включить новую зависимость, если вы используете Maven, добавьте:


    org.springframework.cloud
    spring-cloud-starter-eureka-server
    ${version}

Или если вы используете Gradle:

implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-eureka-server', version: ${version}

Независимо от типа инициализации – сервер Eureka требует, чтобы одна аннотация была помечена как сервер.

В вашем Приложении конечного пользователя классе файлов, который является нашей отправной точкой с аннотацией @SpringBootApplication , мы просто добавим @EnableEurekaServer :

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

Порт по умолчанию для серверов Eureka является 8761 , и это также рекомендовано Весенней командой. Хотя, для хорошей меры, давайте также установим его в файле application.properties :

server.port=8761

После этого наш сервер готов к работе. Запуск этого проекта приведет к запуску сервера Eureka, доступного по адресу localhost:8761 :

Примечание: Без регистрации каких-либо услуг Eureka может ошибочно утверждать, что НЕИЗВЕСТНЫЙ экземпляр запущен.

Создание службы конечного пользователя клиента Eureka в весенней загрузке

Теперь, когда наш сервер запущен и готов к регистрации сервисов, давайте продолжим и сделаем наш Сервис для конечных пользователей в весенней загрузке. У него будет одна конечная точка, которая принимает данные JSON, касающиеся Студента . Эти данные затем отправляются в формате JSON в нашу службу Агрегации данных , которая вычисляет общую статистику оценок.

На практике эта операция была бы заменена гораздо более трудоемкими операциями, которые имеет смысл выполнять в специализированных библиотеках обработки данных и которые оправдывают использование другой службы, а не выполнение их в одной и той же.

Тем не менее, давайте вернемся и создадим каталог для нашей Службы конечного пользователя :

$ cd..
$ mkdir end-user-service
$ cd end-user-service

Здесь давайте начнем новый проект через интерфейс командной строки и включим spring-cloud-starter-netflix-eureka-клиент зависимость. Мы также добавим зависимость web , так как это приложение будет фактически обращено к пользователю:

$ spring init -d=web, spring-cloud-starter-netflix-eureka-client

В качестве альтернативы вы можете использовать Spring Initializr и включить Клиент обнаружения Эврики зависимость:

Если у вас уже есть проект и вы просто хотите включить новую зависимость, если вы используете Maven, добавьте:


    org.springframework.cloud
    spring-cloud-starter-netflix-eureka-client
    ${version}

Или если вы используете Gradle:

implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-eureka-client', version: ${version}

Независимо от типа инициализации – чтобы отметить это приложение как клиент Eureka, мы просто добавляем аннотацию @EnableEurekaClient в основной класс:

@SpringBootApplication
@EnableEurekaClient
public class EndUserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(EndUserServiceApplication.class, args);
    }
    
    @LoadBalanced
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Примечание: В качестве альтернативы вы можете использовать аннотацию @EnableDiscoveryClient , которая является более всеобъемлющей аннотацией. Это может относиться к Эврике, Консулу или Хранителю зоопарка, в зависимости от того, какой инструмент используется.

Мы также определили @Bean здесь, чтобы мы могли @Autowire |/RestTemplate позже в нашем контроллере. Эта Табличка будет использоваться для отправки СООБЩЕНИЯ запроса в Службу агрегирования данных . Аннотация @LoadBalanced означает, что наш Resttemplate должен использовать Клиент балансировки нагрузки ленты при отправке запросов.

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

Поскольку это приложение является клиентом Eureka, мы захотим дать ему имя для реестра. Другие службы будут ссылаться на это имя, когда полагаются на него. Имя определяется в файле application.properties или application.yml :

server.port = 8060
spring.application.name = end-user-service
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka
server:
    port: 8060
spring:
    application:
        name: end-user-service
eureka:
    client:
      serviceUrl:
        defaultZone: http://localhost:8761/eureka/

Здесь мы установили порт для нашего приложения, который Эврика должен знать, чтобы направлять к нему запросы. Мы также указали название службы, на которое будут ссылаться другие службы.

Запуск этого приложения приведет к регистрации службы на сервере Eureka:

INFO 3220 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8060 (http) with context path ''
INFO 3220 --- [           main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8060
INFO 3220 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_END-USER-SERVICE/DESKTOP-8HAKM3G:end-user-service:8060 - registration status: 204
INFO 3220 --- [           main] c.m.e.EndUserServiceApplication          : Started EndUserServiceApplication in 1.978 seconds (JVM running for 2.276)
INFO 3220 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_END-USER-SERVICE/DESKTOP-8HAKM3G:end-user-service:8060 - Re-registering apps/END-USER-SERVICE
INFO 3220 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_END-USER-SERVICE/DESKTOP-8HAKM3G:end-user-service:8060: registering service...
INFO 3220 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_END-USER-SERVICE/DESKTOP-8HAKM3G:end-user-service:8060 - registration status: 204

Теперь, если мы посетим localhost:8761 , мы сможем увидеть его зарегистрированным на сервере:

Теперь давайте продолжим и определим Ученика модель:

public class Student {
    private String name;
    private double mathGrade;
    private double englishGrade;
    private double historyGrade;
    private double scienceGrade;
    
    // Constructor, getters and setters and toString()
}

Для студента мы хотим рассчитать некоторые сводные статистические данные об их успеваемости, такие как среднее, минимальное и максимальное их оценки. Поскольку для этого мы будем использовать Панд, мы воспользуемся очень удобной функцией DataFrame.describe () . Давайте также создадим Результат оценки модель, в которой будут храниться ваши данные после возврата из Службы агрегации данных :

public class GradesResult {
    private Map mathGrade;
    private Map englishGrade;
    private Map historyGrade;
    private Map scienceGrade;
    
    // Constructor, getters, setters and toString()
}

После завершения моделей давайте сделаем действительно простой @RestController , который принимает POST запрос, десериализует его в Студента и отправляет его в службу Агрегации данных , которую мы еще не сделали:

@Autowired
private RestTemplate restTemplate;

@RestController
public class HomeController {
    @PostMapping("/student")
    public ResponseEntity student(@RequestBody Student student) {
        GradesResult grades = restTemplate.getForObject("http://data-aggregation-service/calculateGrades", GradesResult.class);

        return ResponseEntity
            .status(HttpStatus.OK)
            .body(String.format("Sent the Student to the Data Aggregation Service: %s \nAnd got back:\n %s", student.toString(), gradesResult.toString()));
    }
}

Этот @RestController принимает POST запрос и десериализует его тело в объект Student . Затем мы отправляем запрос в нашу службу агрегации данных , которая еще не реализована , так как она будет зарегистрирована на Эврике, и мы упаковываем результаты JSON этого вызова в наш объект GradesResult .

Примечание: Если у сериализатора возникли проблемы с построением Результата оценки объекта из данного результата, вам потребуется вручную преобразовать его с помощью ObjectMapper Джексона :

String result = restTemplate.postForObject("http://data-aggregation-service/calculateGrades", student, String.class);
ObjectMapper objectMapper = new ObjectMapper();
GradesResult gradesResult = objectMapper.readValue(result, GradesResult.class);

Наконец, мы печатаем экземпляр student , который мы отправили, а также экземпляр grades , созданный на основе результата.

Теперь давайте продолжим и создадим Службу агрегирования данных .

Создание службы агрегирования клиентских данных Eureka в Flask

Единственным отсутствующим компонентом является Служба агрегации данных , которая принимает Студента в формате JSON и заполняет фрейм данных Pandas , выполняет определенные операции и возвращает результат обратно.

Давайте создадим каталог для нашего проекта и запустим для него виртуальную среду:

$ cd..
$ mkdir data-aggregation-service
$ python3 -m venv flask-microservice

Теперь, чтобы активировать виртуальную среду, запустите файл activate . На Окнах:

$ flask-microservice/Scripts/activate.bat

В Linux/Mac:

$ source flask-microservice/bin/activate

Для этого мы создадим простое приложение Flask, поэтому давайте установим зависимости как для Flask, так и для Eureka через pip в нашей активированной среде:

(flask-microservice) $ pip install flask pandas py-eureka-client

А теперь мы можем создать ваше приложение для колбы:

$ touch flask_app.py

Теперь откройте flask_app.py файл и импорт колб, Панд и клиентских библиотек Py-Eureka:

from flask import Flask, request
import pandas as pd
import py_eureka_client.eureka_client as eureka_client

Мы будем использовать Flask и запрос для обработки наших входящих запросов и возврата ответа, а также для запуска сервера. Мы будем использовать Pandas для сбора данных, и мы будем использовать py_eureka_client для регистрации нашего приложения Flask на сервере Eureka на localhost:8761 .

Давайте продолжим и настроим это приложение в качестве клиента Eureka и реализуем обработчик запросов POST для данных учащихся:

rest_port = 8050
eureka_client.init(eureka_server="http://localhost:8761/eureka",
                   app_name="data-aggregation-service",
                   instance_port=rest_port)

app = Flask(__name__)

@app.route("/calculateGrades", methods=['POST'])
def hello():
    data = request.json
    df = pd.DataFrame(data, index=[0])
    response = df.describe().to_json()
    return response

if __name__ == "__main__":
    app.run(host='0.0.0.0', port = rest_port)

Примечание: Мы должны установить хост в 0.0.0.0 чтобы открыть его для внешних служб, чтобы Flask не отказал им в подключении.

Это довольно минимальное приложение для колбы с одним @app.route() . Мы извлекли тело входящего СООБЩЕНИЯ запроса в данные словарь через request.json , после чего мы создали Фрейм данных с этими данными.

Поскольку в этом словаре вообще нет индекса, мы установили его вручную.

Наконец, мы вернули результаты функции describe() в формате JSON. Мы не использовали jsonify здесь, так как он возвращает Ответ объект, а не строку. Ответ объект, при отправке обратно будет содержать дополнительные \ символы:

{\"mathGrade\":...}
vs
{"mathGrade":...}

Их нужно было бы избежать, чтобы они не сбросили десериализатор.

В init() функции eureka_client мы задали URL-адрес нашего сервера Eureka , а также имя приложения/службы для обнаружения, а также указали порт, на котором он будет доступен. Это та же информация, которую мы предоставили в приложении Spring Boot.

Теперь давайте запустим это приложение для колбы:

(flask-microservice) $ python flask_app.py

И если мы проверим наш сервер Eureka на localhost:8761 , он зарегистрирован и готов к приему запросов:

Вызов службы Flask из службы Spring Boot с помощью Эврики

Поскольку обе наши службы запущены и работают, зарегистрированы в Eureka и могут взаимодействовать друг с другом, давайте отправим СООБЩЕНИЕ запрос в нашу Службу конечных пользователей , содержащую некоторые данные о студентах, которые , в свою очередь, отправят СООБЩЕНИЕ запрос в Службу агрегации данных , получим ответ и перешлем его нам:

$ curl -X POST -H "Content-type: application/json" -d "{\"name\" : \"David\", \"mathGrade\" : \"8\", \"englishGrade\" : \"10\", \"historyGrade\" : \"7\", \"scienceGrade\" : \"10\"}" "http://localhost:8060/student"

Это приводит к ответу сервера конечному пользователю:

Sent the Student to the Data Aggregation Service: Student{name='David', mathGrade=8.0, englishGrade=10.0, historyGrade=7.0, scienceGrade=10.0}
And got back:
GradesResult{mathGrade={count=1.0, mean=8.0, std=null, min=8.0, 25%=8.0, 50%=8.0, 75%=8.0, max=8.0}, englishGrade={count=1.0, mean=10.0, std=null, min=10.0, 25%=10.0, 50%=10.0, 75%=10.0, max=10.0}, historyGrade={count=1.0, mean=7.0, std=null, min=7.0, 25%=7.0, 50%=7.0, 75%=7.0, max=7.0}, scienceGrade={count=1.0, mean=10.0, std=null, min=10.0, 25%=10.0, 50%=10.0, 75%=10.0, max=10.0}}

Вывод

В этом руководстве мы создали среду микросервисов, в которой одна служба зависит от другой, и подключили их с помощью Netflix Eureka.

Эти службы построены с использованием разных платформ и разных языков программирования, хотя с помощью API REST взаимодействие между ними является простым и легким.

Исходный код для этих двух служб, включая сервер Eureka, доступен на Github .