Перейти к основному содержимому

Apache Camel с Spring Boot

· 7 мин. чтения

Задача: Медиана двух отсортированных массивов

Даны два отсортированных массива размерами n и m. Найдите медиану слияния этих двух массивов.
Временная сложность решения должна быть O(log(m + n)) ...

ANDROMEDA

1. Обзор

По своей сути Apache Camel представляет собой механизм интеграции, который, проще говоря, можно использовать для облегчения взаимодействия между широким и разнообразным набором технологий.

Эти мосты между сервисами и технологиями называются маршрутами. Маршруты реализованы на движке ( CamelContext ) и взаимодействуют с так называемыми «сообщениями обмена».

2. Зависимости Maven

Для начала нам нужно включить зависимости для Spring Boot, Camel, Rest API с Swagger и JSON:

<dependencies>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-servlet-starter</artifactId>
<version>3.15.0</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jackson-starter</artifactId>
<version>3.15.0</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-swagger-java-starter</artifactId>
<version>3.15.0</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>3.15.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

Последние версии зависимостей Apache Camel можно найти здесь .

3. Основной класс

Давайте сначала создадим приложение Spring Boot :

@SpringBootApplication
@ComponentScan(basePackages="com.foreach.camel")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

4. Верблюжьи конфигурации для Spring Boot

Давайте теперь настроим наше приложение с помощью Spring, начиная с файлов конфигурации (свойств).

Например, давайте настроим журнал для нашего приложения в файле application.properties в src/main/resources :

logging.config=classpath:logback.xml
camel.springboot.name=MyCamel
server.address=0.0.0.0
management.address=0.0.0.0
management.port=8081
endpoints.enabled = true
endpoints.health.enabled = true

В этом примере показан файл application.properties , который также устанавливает путь к конфигурации Logback. Установив IP-адрес «0.0.0.0», мы полностью ограничиваем доступ администратора и управления на веб-сервере, предоставляемом Spring Boot. Кроме того, мы обеспечиваем необходимый сетевой доступ к конечным точкам наших приложений, а также к конечным точкам проверки работоспособности.

Еще один файл конфигурации — application.yml . В нем мы добавим некоторые свойства, которые помогут нам вводить значения в наши маршруты приложений:

server:
port: 8080
camel:
springboot:
name: ServicesRest
management:
port: 8081
endpoints:
enabled: false
health:
enabled: true
quickstart:
generateOrderPeriod: 10s
processOrderPeriod: 30s

5 . Настройка верблюжьего сервлета

Один из способов начать использовать Camel — зарегистрировать его как сервлет, чтобы он мог перехватывать HTTP-запросы и перенаправлять их в наше приложение.

Как упоминалось ранее, начиная с версии Camel 2.18 и ниже, мы можем воспользоваться нашим application.yml , создав параметр для нашего конечного URL. Позже он будет внедрен в наш Java-код:

foreach:
api:
path: '/camel'

Вернемся к нашему классу Application . Нам нужно зарегистрировать сервлет Camel в корне нашего контекстного пути, который будет внедрен из ссылки foreach.api.path в application.yml при запуске приложения:

@Value("${foreach.api.path}")
String contextPath;

@Bean
ServletRegistrationBean servletRegistrationBean() {
ServletRegistrationBean servlet = new ServletRegistrationBean
(new CamelHttpTransportServlet(), contextPath+"/*");
servlet.setName("CamelServlet");
return servlet;
}

Начиная с версии Camel 2.19, эта конфигурация была удалена, так как CamelServlet по умолчанию имеет значение «/camel» .

6. Построение маршрута

Давайте начнем создавать маршрут, расширив класс RouteBuilder от Camel и установив его как @Component , чтобы процедура сканирования компонентов могла найти его во время инициализации веб-сервера:

@Component
class RestApi extends RouteBuilder {
@Override
public void configure() {
CamelContext context = new DefaultCamelContext();

restConfiguration()...
rest("/api/")...
from("direct:remoteService")...
}
}

В этом классе мы переопределяем метод configure() из класса Camel RouteBuilder .

Верблюду всегда нужен экземпляр CamelContext — основной компонент, в котором хранятся входящие и исходящие сообщения.

В этом простом примере достаточно DefaultCamelContext , так как он просто связывает сообщения и маршрутизирует их, как служба REST, которую мы собираемся создать.

6.1. Маршрут restConfiguration( )

Затем мы создаем объявление REST для конечных точек, которые мы планируем создать в методе restConfiguration() :

restConfiguration()
.contextPath(contextPath)
.port(serverPort)
.enableCORS(true)
.apiContextPath("/api-doc")
.apiProperty("api.title", "Test REST API")
.apiProperty("api.version", "v1")
.apiContextRouteId("doc-api")
.component("servlet")
.bindingMode(RestBindingMode.json)

Здесь мы регистрируем контекстный путь с нашим внедренным атрибутом из файла YAML. Та же логика была применена к порту нашего приложения. CORS включен, что позволяет использовать эту веб-службу на нескольких сайтах. Режим привязки разрешает и преобразует аргументы в наш API.

Затем мы добавляем документацию Swagger к URI, заголовку и версии, которые мы ранее установили. По мере создания методов/конечных точек для нашей веб-службы REST документация Swagger будет автоматически обновляться.

Этот контекст Swagger сам по себе является маршрутом Camel, и мы можем увидеть некоторую техническую информацию о нем в журнале сервера во время процесса запуска. Наш пример документации по умолчанию находится по адресу http://localhost:8080/camel/api-doc.

6.2. Остальные () Маршрут

Теперь давайте реализуем вызов метода rest() из метода configure() , указанного выше:

rest("/api/")
.id("api-route")
.consumes("application/json")
.post("/bean")
.bindingMode(RestBindingMode.json_xml)
.type(MyBean.class)
.to("direct:remoteService");

Этот метод довольно прост для тех, кто знаком с API. Идентификатор — это идентификация маршрута внутри CamelContext . Следующая строка определяет тип MIME. Режим привязки определен здесь, чтобы показать, что мы можем установить режим в restConfiguration() .

Метод post() добавляет в API операцию, генерирующую конечную точку « POST/bean », а MyBean (обычный Java-бин с целочисленным идентификатором и строковым именем ) определяет ожидаемые параметры.

Точно так же действия HTTP, такие как GET, PUT и DELETE, также доступны в форме get() , put() , delete() .

Наконец, метод to() создает мост к другому маршруту. Здесь он говорит Camel искать внутри своего контекста/движка другой маршрут, который мы собираемся создать, названный и обнаруженный по значению/идентификатору « direct: … », совпадающий с маршрутом, определенным в методе from() .

6.3. Маршрут from() с функцией transform()

При работе с Camel маршрут получает параметры, а затем конвертирует, преобразовывает и обрабатывает эти параметры. После этого он отправляет эти параметры на другой маршрут, который перенаправляет результат на желаемый выход (файл, базу данных, SMTP-сервер или ответ REST API).

В этой статье мы создаем еще один маршрут только внутри метода configure() , который мы переопределяем. Это будет маршрут назначения для нашего последнего маршрута to() :

from("direct:remoteService")
.routeId("direct-route")
.tracing()
.log(">>> ${body.id}")
.log(">>> ${body.name}")
.transform().simple("Hello ${in.body.name}")
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200));

Метод from() следует тем же принципам и имеет многие из тех же методов, что и метод rest() , за исключением того, что он использует контекстные сообщения Camel. Это причина для параметра « direct-route », который создает ссылку на вышеупомянутый метод rest().to() .

Доступны многие другие преобразования , в том числе извлечение в виде примитивов (или объектов) Java и их отправка на уровень сохраняемости. Обратите внимание, что маршруты всегда считываются из входящих сообщений, поэтому связанные маршруты будут игнорировать исходящие сообщения.

Наш пример готов, и мы можем попробовать его:

  • Запустите команду приглашения: mvn spring-boot:run
  • Сделайте POST-запрос к http://localhost:8080/camel/api/bean с параметрами заголовка: Content-Type: application/json и полезной нагрузкой {"id": 1, name: "World"}
  • Мы должны получить код возврата 201 и ответ: Hello, World

6.4. ПРОСТОЙ язык сценариев

В этом примере журнал выводится с использованием метода tracing() . Обратите внимание, что мы использовали заполнители ${} ; они являются частью языка сценариев, который принадлежит Camel и называется SIMPLE. Он применяется к сообщениям, которыми обмениваются по маршруту, например к телу входящего сообщения.

В нашем примере мы используем SIMPLE для вывода в журнал атрибутов компонента, которые находятся внутри тела сообщения Camel.

Мы также можем использовать его для выполнения простых преобразований, как было показано с помощью метода transform() .

6.5. Маршрут from() с помощью process()

Давайте сделаем что-нибудь более осмысленное, например, вызовем сервисный уровень для возврата обработанных данных. SIMPLE не предназначен для тяжелой обработки данных, поэтому давайте заменим transform( ) методом process() :

from("direct:remoteService")
.routeId("direct-route")
.tracing()
.log(">>> ${body.id}")
.log(">>> ${body.name}")
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
MyBean bodyIn = (MyBean) exchange.getIn().getBody();
ExampleServices.example(bodyIn);
exchange.getIn().setBody(bodyIn);
}
})
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200));

Это позволяет нам извлекать данные в bean-компонент, тот же самый, который ранее был определен в методе type() , и обрабатывать его на нашем уровне ExampleServices .

Поскольку ранее мы установили для bindingMode() значение JSON, ответ уже имеет правильный формат JSON, сгенерированный на основе нашего POJO. Это означает, что для класса ExampleServices :

public class ExampleServices {
public static void example(MyBean bodyIn) {
bodyIn.setName( "Hello, " + bodyIn.getName() );
bodyIn.setId(bodyIn.getId() * 10);
}
}

Тот же HTTP-запрос теперь возвращается с кодом ответа 201 и телом: {“id”: 10”,name”: “Hello, World”} .

7. Заключение

С помощью нескольких строк кода нам удалось создать относительно законченное приложение. Все зависимости создаются, управляются и запускаются автоматически с помощью одной команды. Более того, мы можем создавать API, связывающие воедино все виды технологий.

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

Этот пример REST можно найти на GitHub .

Наконец, помимо API-интерфейсов filter() , process() , transform() и marshall() , в Camel доступно множество других шаблонов интеграции и манипуляций с данными: