Статьи

Разработка сервисов с использованием Apache Camel. Часть III. Интеграция Spring 4 и Spring Boot

Эта статья является третьей в серии о Apache Camel и о том, как я использовал ее вместо IBM Message Broker для клиента. Этим летом я использовал Apache Camel несколько месяцев для создания ряда сервисов SOAP. Эти службы выполняли различные сторонние поиски данных для наших клиентов. Предыдущие статьи см. В  части I: вдохновение  и в  части II: создание и тестирование маршрутов .

В конце июня я отправил электронное письмо инженерной команде моего клиента. Его тема: «Внешняя конфигурация и микросервисы». Я рекомендовал интегрировать  Spring Boot  в проект Apache Camel, над которым я работал. Я сказал им, что моей главной мотивацией была его  внешняя конфигурация  . Я также указал на функцию WAR без контейнера, в которой Tomcat (или Jetty) встроен в WAR, и вы можете запустить свое приложение с помощью «java -jar appname.war». Я упомянул  микросервисы  и то, что Spring Boot облегчит разделение проекта на структуру проекта для службы, если мы захотим пойти по этому пути. Затем я задал два простых вопроса:

  1. Можно ли интегрировать Spring Boot?
  2. Должен ли я разделить проект на микросервисы?

Оба эти предложения были хорошо приняты, поэтому я пошел на работу.

Весна 4

До того, как я интегрировал Spring Boot, я знал, что мне нужно перейти на  Spring 4 . Версия Camel, которую я использовал (2.13.1), не поддерживала Spring 4. Я обнаружил проблему CAMEL-7074 (Support spring 4.x) и  добавил комментарий,  чтобы увидеть, когда она будет исправлена. После работы с зависимостями и попытки использования Camel 2.14-SNAPSHOT я смог обновить CXF 3.0. Однако это не решило мою проблему. Были некоторые несовместимые изменения API между Spring 3.3.x и Spring 4.0.x, и модуль camel-test-spring не будет работать с обоими. Я предложил следующее:

Я думаю, что самый простой путь вперед — это создать два модуля: camel-test-spring и camel-test-spring3. Первый компилируется против Spring 4, а второй — против Spring 3. Вы можете переключить его так, чтобы по умолчанию camel-test-spring перешел на Spring 3, но camel-test-spring4, кажется, не смотрит вперед, как вы, надеюсь, не сделаете нужен верблюд-тест-весна5. 

Я сделал это изменение в вилке, и это работает в моем проекте. Я могу обновиться до Camel 2.14-SNAPSHOT и CXF 3.0 с Spring 3.2.8 (используя camel-test-spring3). Я также могу перейти на Spring 4, если использую обновленную тестовую верблюжью пружину. 

Вот запрос на удаление, который имеет это изменение:  https://github.com/apache/camel/pull/199

Команда Camel объединила мои предложенные изменения пару недель спустя. К сожалению,  похожая ситуация произошла с Spring 4.1 , поэтому вам придется ждать Camel 2.15, если вы хотите использовать Spring 4.1.

Сделав исправленную версию 2.14-SNAPSHOT доступной для моего проекта, я смог обновить ее до Spring 4 и CXF 3 с небольшими изменениями в моем pom.xml.

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- <camel.version>2.13.1</camel.version>
- <cxf.version>2.7.11</cxf.version>
- <spring.version>3.2.8.RELEASE</spring.version>
+ <camel.version>2.14-SNAPSHOT</camel.version>
+ <cxf.version>3.0.0</cxf.version>
+ <spring.version>4.0.5.RELEASE</spring.version>
</properties>
...
+ <!-- upgrade camel-spring dependencies -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-tx</artifactId>
+ <version>${spring.version}</version>
+ </dependency>

Мне также пришлось изменить некоторые импорты для CXF 3.0, поскольку он  включает новую основную версию Apache WSS4J (2.0.0) .

-import org.apache.ws.security.handler.WSHandlerConstants;
+import org.apache.wss4j.dom.handler.WSHandlerConstants;
...
-import org.apache.ws.security.WSPasswordCallback;
+import org.apache.wss4j.common.ext.WSPasswordCallback;

После того, как все было обновлено, я продолжил разработку сервисов в течение следующих нескольких недель.

Весенний ботинок

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

+ <parent>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-parent</artifactId>
+ <version>1.1.4.RELEASE</version>
+ </parent>
...
<cxf.version>3.0.1</cxf.version>
+ <java.version>1.7</java.version>
+ <servlet-api.version>3.1.0</servlet-api.version>
<spring.version>4.0.6.RELEASE</spring.version>
...
- <artifactId>maven-compiler-plugin</artifactId>
- <version>2.5.1</version>
- <configuration>
- <source>1.7</source>
- <target>1.7</target>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ </plugin>
</plugins>
</build>
<dependencies>
+ <!-- spring boot -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-actuator</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-logging</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-log4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-tomcat</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
<!-- camel -->
...
- <!-- upgrade camel-spring dependencies -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- <version>${spring.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-aop</artifactId>
- <version>${spring.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-tx</artifactId>
- <version>${spring.version}</version>
- </dependency>
...
- <!-- logging -->
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.6</version>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>1.7.6</version>
- </dependency>
- <dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- <version>1.2.17</version>
- </dependency>
-
<!-- utilities -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
- <version>2.3</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
- <version>1.4</version>
...
<!-- testing -->
<dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-logging</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-test</artifactId>
- <version>${spring.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>1.9.5</version>
- <scope>test</scope>
- </dependency>

Затем я удалил  AppInitializer.java класс, упомянутый во  второй части,  и добавил  Application.java класс.

@Configuration
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })
@ComponentScan
public class Application extends SpringBootServletInitializer {




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




@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}




@Bean
public ServletRegistrationBean servletRegistrationBean() {
CXFServlet servlet = new CXFServlet();
return new ServletRegistrationBean(servlet, "/api/*");
}




@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return new EmbeddedServletContainerCustomizer() {




@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
ErrorPage error401Page = new ErrorPage(HttpStatus.UNAUTHORIZED, "/401.html");
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/404.html");
ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500.html");
container.addErrorPages(error401Page, error404Page, error500Page);
}
};
}
}

Страницы ошибок, которые вы видите сконфигурированными выше, были настроены и скопированы с пользовательских страниц ошибок Тима Спорчича  с Spring Boot .

Динамические источники данных

Я исключил связанные с DataSource классы автоконфигурации, потому что у этого приложения было много источников данных. Также требовалось разрешить добавление источников данных на лету простым редактированием application.properties. Я  спросил, как это сделать в Stack Overflow,  и получил  отличный ответ  от  Стефана Николла .

Проблемы с весенней загрузкой

Я столкнулся с парой проблем после интеграции Spring Boot. Во-первых,  он удалял заголовки content- * для ответов CXF . Это произошло только при запуске WAR в Tomcat, и я смог найти обходной путь с помощью настраиваемого ResponseWrapper и Filter. Эта проблема была исправлена ​​в Spring Boot 1.1.6.

Другая проблема заключалась в том, что функция переопределения свойств не работала при установке переменных среды. setenv.sh Обходным решением было создать  скрипт в $ CATALINA_HOME / bin и добавить туда переменные окружения. См. Раздел 3.4  в RUNNING.txt Tomcat 7 для получения дополнительной информации.

Неисправности SOAP

После обновления до Spring 4 и интеграции Spring Boot я продолжил миграцию потоков IBM Message Broker. Моей целью было сделать все новые сервисы обратно совместимыми, но я столкнулся с проблемой. С новыми сервисами ошибки SOAP отправлялись обратно клиенту вместо сообщений об ошибках в сообщении SOAP. Я предложил исправить это одним из двух способов:

  1. Измените клиент так, чтобы он смотрел на ошибки SOAP и обрабатывал их соответствующим образом.
  2. Измените новые службы, чтобы сообщения возвращались вместо сбоев.

Для # 2 я узнал, как  преобразовывать сообщения  об ошибках в список рассылки пользователей Camel. Однако команда решила улучшить клиента, и вместо этого мы добавили обработку ошибок.

Микросервис Развертывание

Когда я впервые интегрировал Spring Boot, я планировал разделить наш проект на проект за услугу. Это позволило бы каждой службе развиваться самостоятельно, вместо монолитной войны, которая содержит все службы. В групповых обсуждениях была некоторая обеспокоенность по поводу нехватки памяти при запуске нескольких экземпляров вместо одного.

Я указал на  интересную ветку  в списке рассылки Camel о развертывании маршрутов с маршрутом-на-jvm или всеми в одной и той же JVM. Рекомендация этого потока состояла в том, чтобы объединить похожие маршруты, если вы хотите их разделить.

В конце концов, мы решили позволить нашей операционной команде решить, как они хотят управлять / развертывать все. Я упоминал, что Spring Boot может работать с  Tomcat, Jetty, JBoss и даже облачными провайдерами, такими как Heroku и Cloud Foundry . Я подсчитал, что разделение проекта займет меньше дня, как и превращение его в монолитную ВОЙНУ.

Резюме

В этой статье объясняется, как мы обновили наше приложение Apache Camel до Spring 4 и интегрировали Spring Boot. Было немного трудно заставить вещи работать, но ничего, что не могли исправить несколько запросов и обходных путей. Мы обнаружили некоторые проблемы с настройкой переменных среды для Tomcat и решили не разбивать наш проект на небольшие микросервисы. Надеемся, что эта статья поможет людям, пытающимся установить  Camelize в Spring Boot  .

В следующей статье я расскажу о нагрузочном тестировании с  Gatling , регистрации в  Log4j2  и мониторинге с помощью  hawtio  и  New Relic .