Как только в компании появляется хотя бы две информационных системы, которым необходимо обмениваться данными, возникает вопрос, как организовать их взаимодействие. Вариантов множество: файловый обмен, линки между базами данных, web или rest сервисы, различные системы обмена сообщениями, устаревшие RPC и CORBA, новомодный gRPC и т.д. Выбор зависит от предпочтений участников проекта и от возможностей систем (архитектура системы, используемая платформа, наличие готового API и пр.). Предположим, выбрали какой-то способ обмена, системы начали взаимодействовать, все хорошо. Но потом возникает третья система, с которой тоже надо интегрироваться, потом четвертая и т.д. Нужно опять садиться и выбирать способ обмена, и не факт что удастся ограничиться уже используемыми технологиями (где-то это продиктовано ограничениями новых систем, где-то разработчик настоял на другой технологии или захотел попробовать что-то новое). С ростом количества систем растет количество и сложность взаимодействий между ними, растет количество используемых технологий. В итоге вся интеграционная архитектура компании начинает напоминать запутанный клубок разноцветных ниток, как-то связывающих системы компании, который все сложнее распутывать при разборе ошибок и доработках. Рано или поздно начинают приходить мысли о создании единой интеграционной среды, которая прозрачно и расширяемо свяжет все системы воедино.

В этой статье я расскажу об опыте использования Apache ServiceMix (Camel) и RabbitMQ для построения такой интеграционной среды.

Стэк технологий


RabbitMQ


RabbitMQ – система обмена сообщениями на базе протокола AMQP. Подробно описывать саму систему не стану, на Хабре уже есть несколько статей, которые детально описывают функционал системы, расскажу о том, где и как RabbitMQ используется у нас.



Через RabbitMQ мы обмениваемся данными с информационными системами компании. Для каждой системы создается набор очередей. Например, нужно организовать поиск клиентов по ФИО и дате рождения в учетной системе. Создаем пару очередей. Одна входящая по отношению к учетной системе, в нее будем посылать запросы с указанием ФИО и даты рождения клиента. Учетная система слушает входящую очередь и обрабатывает поступающие запросы. Вторая — исходящая, в которую учетная система будет отправлять ответы со списком найденных клиентов, подходящих под условия запроса.

Почему это удобно:

  • Сообщения, которые хранятся в очереди, персистентные. Т.е. если система какое-то время недоступна, то сообщения никуда не пропадут и будут обработаны после того, как система поднимется. Перезапуск самого RabbitMQ также не приводит к потере сообщений.
  • Очередь служит буфером, который система может обрабатывать в комфортном режиме, избегая пиковых нагрузок. Понятно, что для запросов, выполнения которых ждет в интерфейсе пользователь, это не подойдет. В этом случае нужен незамедлительный ответ. А вот для разного рода асинхронных взаимодействий подходит очень хорошо.
  • Асинхронный режим взаимодействия. При этом также поддерживаются и синхронные вызовы.

Научить систему взаимодействовать с RabbitMQ не сложно. У RabbitMQ есть готовые клиенты под различные платформы (Java, .Net, Python и пр.). Клиент простой и понятный. Код, который читает сообщения из очереди и / или отправляет сообщения в очередь, занимает несколько строк. Понятно, что не каждую систему можно подружить с RabbitMQ, например, в случае с легаси и коробочными системами сделать это будет затруднительно. В этом случае обмен строится с использованием технологий, которые поддерживают эти системы, где-то мы вызываем хранимые процедуры в БД, где-то используем web и rest сервисы, где-то что-то еще. Для того чтобы организовать такое взаимодействие и для многих других интеграционных задач, используется продукт Apache ServiceMix.

Apache ServiceMix

ServiceMix – Karaf контейнер с предустановленным набором бандлов, которые пригодятся для решения различных интеграционных задач.



Немного более подробно о том, что входит в состав продукта:

  1. Собственно сам Karaf контейнер, в котором работают бандлы и который позволяет ими управлять: устанавливать / удалять / останавливать / запускать, просматривать логи, видеть зависимости компонентов и т.д.
  2. Широкий набор бандлов, которые выполняют классические интеграционные функции: валидация, различные трансформации (например, из JSON в XML, трансформации при помощи XSLT и т.д.), обогащение, роутинг, split and join, мониторинг, исполнение интеграционных процессов и т.д.
  3. Широкий набор различных адаптеров: файловые адаптеры, адаптеры к web и rest сервисам, JMS, RabbitMQ, Kafka и т.д. Полный список адаптеров можно посмотреть на сайте Camel.

Ограничиться использованием только предустановленных бандлов вряд ли удастся, но для начала установленного набора должно хватить. Т.к. мы работаем с Karaf контейнером, то можно устанавливать любые необходимые бандлы, сделать это можно либо устанавливая фичи (наборы бандлов), либо просто устанавливая отдельные бандлы. Само собой, можно писать собственные бандлы, либо заворачивать в бандлы сторонние java библиотеки.

Apache Camel


Ключевым компонентом ServiceMix является фреймворк Apache Camel, который позволяет строить интеграционные процессы, которые в терминологии Camel называются роутами.
Покажу на примере — что такое роут:



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

Camel поддерживает различные нотации для описания роутов, наиболее распространенные Java DSL и Spring XML. Мы используем Spring XML. Вот как бы выглядел роут на картинке в нотации Spring XML:

<route id="Normalizer">
	<from uri="входной endpoint" />
			
	<!-- Предположим что формат определяется на основании названия корневого тэга сообщения -->
	<choice>
		<when>
			<xpath>/*[local-name()='Format_1']</xpath>
			<to uri="xslt:classpath:xslt/Format_1_Transformation.xslt" />
		</when>
		<when>
			<xpath>/*[local-name()='Format_2']</xpath>
			<to uri="xslt:classpath:xslt/Format_2_Transformation.xslt" />
		</when>
		<when>
			<xpath>/*[local-name()='Format_3']</xpath>
			<to uri="xslt:classpath:xslt/Format_3_Transformation.xslt" />
		</when>
	</choice>
			
	<to uri="выходной endpoint" />
</route>

Весьма приятным дополнением является то, что Camel прекрасно интегрирован со Spring. Можно использовать привычный Spring XML, в котором определяются бины, и в этом же Spring XML определять Camel роуты. При этом, из роутов можно вызывать бины, а из бинов можно обращаться к роутам. Вызов бинов из роутов реализован со свойственной Camel гибкостью, можно передавать в метод бина тело сообщения, можно передавать заголовки + тело, а можно передать только значение определенного тэга XML сообщения, указанного через XPATH выражение, а результат выполнения метода использовать в конструкции choice для последующего определения маршрута. И все это практически в одну строку.

Вот пример элегантности в стиле Camel:

<camel:camelContext>
	<route id="Bean method invocation">
		<from uri="входной endpoint" />
			
		<when>
			<simple>${bean:authManager?method=checkToken(${body})}</simple>
				
			<to uri="продолжить обработку" />
		</when>	
	</route>
		
</camel:camelContext>

<bean id="authManager" class="pachage.AuthManager" />

public class AuthManager {
	
	public boolean checkToken(@Body Document xml, @XPath("/Root/Token/@Value") String token) {
		return checkSessionToken(token);
	}
		
}

Еще одно важное понятие в Camel – это endpoint (далее эндпоинт). Роуты могут читать сообщения из эндпоинтов, могут посылать сообщения в эндпоинты. Эндпоинтом может выступать, например, RabbitMQ очередь, файл в директории, опубликованный rest сервис и т.д. Если роут читает эндпоинт, то при поступлении сообщения в этот эндпоинт роут начинает его обработку. Это позволяет опубликовать роут, т.е. дать возможность обращаться к нему внешним системам. Если у вас есть роут, который выполняет какую-то задачу, например, проверяет корректность заполненных в анкете данных, то вы его можете опубликовать как web сервис, или дать возможность обращаться к нему через JMS, а можете сделать и то и другое, чтобы внешние системы могли пользоваться возможностями вашего роута, кто-то через вызовы web сервиса, а кто-то путем обмена сообщениями через JMS очереди.

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

Интеграционная архитектура


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


Для каждой системы выбирается одна технология обмена данными между системой и ServiceMix. Можно выбрать несколько, но это усложняет реализацию, как на стороне системы, так и на стороне ServiceMix. В общем случае использование нескольких разных технологий не оправдано, но технически вполне может быть реализовано. В основном для обмена с системами мы используем RabbitMQ (создаем наборы очередей, через которые ServiceMix обменивается сообщениями с интегрируемыми системами). Но встречаются и другие случаи, в которых нам очень помогает набор готовых адаптеров, который входит в состав ServiceMix. Например, в случае с нашей учетной системой мы используем хранимые процедуры в БД. Для вызова хранимых процедур используем компонент MyBatis, который позволяет маппировать сообщения, которые ходят по ServiceMix, на параметры хранимых процедур. Вот пример такого маппинга для процедуры установки логина пользователю по его ID:

<select id="setLogin" statementType="CALLABLE" parameterType="java.util.Map">
        {call esb.SetLogin (
       @UserId=#{UserId, mode=IN},
       @LoginId=#{LoginId, mode=IN}
        )}
</select>

Также адаптеры отвечают за преобразование форматов, в которых системы взаимодействуют с интеграционной платформой, к внутреннему формату. Кто-то предпочитает обмениваться в формате JSON, кто-то использует свой формат XML, но внутри интеграционной платформы компоненты обмениваются данными во внутреннем каноническом XML формате. Сейчас XML теряет популярность ввиду его тяжеловесности и появившихся альтернатив в виде JSON, Protobuf и т.д. Но, на мой взгляд, XML все же удобен для решения интеграционных задач. Есть много полезных технологий, таких как XSLT и XPATH, которые сильно упрощают жизнь, плюс он вполне человекочитаем.

Маршрутизация сообщений между компонентами (адаптерами) осуществляется на основании правил маршрутизации. Роуты внутри одного компонента интеграционной платформы взаимодействуют через внутренние эндпоинты Camel (direct, seda). Между собой компоненты обмениваются через очереди RabbitMQ. Это позволяет сделать компоненты интеграционной платформы независимыми. Падение одного компонента не влияет на работоспособность других. При временном повышении нагрузки сообщения будут накапливаться в очереди компонента, не оказывая влияния на весь остальной обмен.



Чем такая архитектура лучше, чем прямое взаимодействие между системами по принципу точка-точка:

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

Отказоустойчивость




Отказоустойчивость на уровне RabbitMQ достигается за счет создания кластера и синхронизации очередей. Каждое сообщение, которое попадает в очередь на одной из нод RabbitMQ, реплицируется на другие ноды согласно политике синхронизации (можно реплицировать на все ноды кластера, можно реплицировать на определенное количество нод, можно не реплицировать вообще). Мы используем конфигурацию из трех нод с репликацией сообщений на все ноды. Это позволяет сохранить полную работоспособность кластера в случае падения двух нод из трех. Но нужно понимать, что это и самый затратный вариант в плане времени обработки каждого сообщения и занимаемого места на диске. На клиенте к RabbitMQ прописываются все три ноды, при этом коннект открывается к одной из нод, но в случае ее падения клиент прозрачно переключится на другие ноды и продолжит работать.

Отказоустойчивость ServiceMix достигается за счет использования кластера из двух нод ServiceMix. При этом часть бандлов работают параллельно, если это допустимо. Например, адаптеры, которые читают одну RabbitMQ очередь, вполне могу делать это параллельно, одно сообщение всегда получит только кто-то один из них, но при этом сообщения будут равномерно распределяться между двумя нодами. В случае падения одной из нод всю нагрузку берет на себя вторая нода. Часть бандлов активна только на одной ноде, но при ее падении бандлы активируются на второй ноде. Например, в случае если адаптер читает общий сетевой каталог, то одновременное чтение одного и того же файла может привести к дублированию и коллизиям. Такой режим работы достигается за счет использования разделяемых блокировок на основе Hazelcast. Бандл, который первым захватил блокировку, переходит в активный режим и выполняет свою функцию, второй бандл висит в ожидании освобождения блокировки.

Hello world


В завершении хочу привести простой пример Hello world приложения на Camel, которое мы запустим на ServiceMix. Наше приложение будет просто один раз писать в лог ServiceMix фразу “Hello world” при запуске бандла (например, при деплое бандла или перезапуске ServiceMix).

  1. Качаем дистрибутив ServiceMix, распаковываем и запускаем servicemix.sh (bat).
  2. Создаем и настраиваем новый maven проект.
    В src/main/resources нужно создать каталог META-INF, в котором создать подкаталог spring.
    Т.к. нам нужно собрать бандл — правим pom.xml (добавляем инструкции packaging и build):

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>HelloWorld</groupId>
    	<artifactId>HelloWorld</artifactId>
    	<version>1.0.0-SNAPSHOT</version>
    	<name>HelloWorld</name>
    	<description>HelloWorld</description>
      
    	<packaging>bundle</packaging>
      
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.apache.felix</groupId>
    				<artifactId>maven-bundle-plugin</artifactId>
    				<version>3.0.1</version>
    				<extensions>true</extensions>
    				<configuration>
    					<instructions>
    						<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
                            <Import-Package>*</Import-Package>
                            <Export-Package>${project.groupId};version=${project.version}</Export-Package>
    					</instructions>
    				</configuration>
    			</plugin>
    		</plugins>
    	</build>
    </project>
    

    Каких-либо maven зависимостей добавлять не нужно.
  3. Настраиваем Camel контекст и роут.
    В каталоге spring нужно создать файл camel-context.xml (загрузчик автоматически ищет файл с описанием Camel контекста в META-INF/spring и запускает роуты). В файл camel-context.xml поместим следующее содержимое (по тексту даны комментарии):

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi"
    	xmlns:camel="http://camel.apache.org/schema/spring"
    	xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/osgi  http://www.springframework.org/schema/osgi/spring-osgi.xsd
           http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
    
    	<!-- Описание Camel контекста -->
    	<camel:camelContext xmlns="http://camel.apache.org/schema/spring" id="HelloWorldContext">
    		
    		<!-- Описание роута -->
    		<route id="HelloWorldRoute">
    			<!-- Запуск обработки по таймеру один раз при старте роута -->
    			<from uri="timer://startTimer?repeatCount=1" />
    
    			<!-- Записываем в тело сообщения текст Hello world -->
    			<setBody>
    				<constant>Hello world</constant>
    			</setBody>
    			
    			<!-- Выводим тело сообщения в лог -->
    			<log message="${body}"/>
    		</route>
    		
    	</camel:camelContext>
    </beans>
    

    Для того чтобы наш роут выполнил свою задачу (записал в лог текст “Hello world”), нужно передать ему на вход сообщение. В нашем случае эту задачу решает таймер <from uri=«timer://startTimer?repeatCount=1»/>, который, следуя инструкции repeatCount=1, один раз отправит на вход роута сообщение. Т.к. таймер посылает на вход роута пустое сообщение, нам нужно его чем-то заполнить – помещаем в тело сообщения текст “Hello world” <setBody>. В конце роута выводим содержимое тела сообщения в лог <log message="${body}">.
  4. Собираем наш проект: mvn package
  5. Деплоим собранный бандл.
    Одним из способов задеплоить бандл в ServiceMix является копирование jar файла в каталог deploy. Копируем собранный jar в каталог ServiceMix/deploy.

Теперь смотрим лог ServiceMix: ServiceMix/data/log/servicemix.log. Помимо информации, что мы установили и запустили новый бандл, должна появиться надпись “Hello world”:

HelloWorldRoute | 43 — org.apache.camel.camel-core — 2.16.3 | Hello world

Попробуйте зайти в ssh консоль и просмотреть список Camel контекстов context-list и список роутов route-list. В выводе команд вы найдете наши HelloWorldContext и HelloWorldRoute.

Выводы


В заключение хочу сказать, что ServiceMix и Camel отличные продукты для построения интеграционных решений. Функциональные возможности поражают, практически для любой задачи можно найти элегантное и простое решение. Видно, что продукты очень хорошо продуманы, разработчики действительно потрудились на славу. В нашей компании ServiceMix используется уже 2 года, на текущий момент мы интегрировали порядка 16 наших информационных систем и разработали более 150 интеграционных сервисов. С некоторыми компонентами все же возникали проблемы, но всегда есть аналогичные компоненты, из которых можно выбрать или, в крайнем случае, разработать свой компонент. В целом, продукты работают стабильно и надежно, лично мое впечатление самое положительное. Несомненным плюсом является открытость исходного кода и отсутствие необходимости покупать лицензию. При этом продукты совершенно не уступают коммерческим аналогам. Также хочу отметить, что ServiceMix и Camel можно использовать не только для решения интеграционных задач. Например, Karaf прекрасно подходит под разворачивание в него web приложений. Эту возможность мы используем для того, чтобы предоставлять web интерфейсы администраторам для настройки интеграционной платформы. Camel прекрасно интегрируется с различными бизнесовыми компонентами. Например, мы используем Drools (движок бизнес правил) для отнесения поступающих сделок на соответствующий портфель по определенным правилам (обогащение сделки портфелем), которые настраиваются пользователями из миддл-офиса. Очень интересным продуктом является Activiti (BPM движок), который может работать в Karaf контейнере и интегрироваться с Camel. Все вместе это позволяет создавать очень интересные решения, совмещающие в себе интеграционные и бизнес функции.

Комментарии (31)


  1. sshikov
    16.05.2018 19:59
    +1

    Никогда не понимал, зачем берут именно ServiceMix (или скажем Fuse)? Просто karaf настраивается до такого же состояния очень быстро. Я работал над проектами с чистым karaf и с Fuse, и не заметил никакой разницы ровным счетом, кроме того, что версия Camel (и всего остального что с этим связано) была дремучая до ужаса.


    1. vuper Автор
      17.05.2018 12:18

      Да, можно взять karaf и задеплоить необходимые бандлы, получится тот же самый ServiceMix. Тут никакой магии нет. Воспринимайте ServiceMix, как докер образ с настроенным окружением, который можно взять для быстрого старта, чтобы не заниматься предварительной настройкой.


      1. sshikov
        17.05.2018 20:47
        +1

        Ну я так и воспринимаю. Просто мой опыт с Fuse vs Karaf мне показал, что даже платная Fuse ровным счетом ничего не дает. Все равно, вы сами ставите (причем не бандлы, и фичи, что сильно крупнее), или по умолчанию что-то стоит, но рано или поздно хочется скажем camel обновить. И вот с этим все уже не очень просто. Обычно проще новый контейнер рядом развернуть, туда все задеплоить еще раз, и переключиться.


        1. igor_suhorukov
          17.05.2018 22:25

          Единственный плюс это поддержка(формально важна в банках при выборе решения, реально я не сталкивался чтобы ею пользовались) и раньше была IDE для роутов на базе эклипса. А так да, компоненты там с плесенью, стабильные…


          1. sshikov
            17.05.2018 22:42

            Я сам из банка. От той поддержки толку как от козла молока… Например, купили Fuse 6, который официально поддерживает Java 6, Camel внутри что-то типа 2.10, karaf из семейства 2.x. С тех пор, что называется, много воды утекло, и в новом карафе, и в новом camel исправлено много больше багов, чем зарепортили той поддержке.

            Чтобы штатно перейти на Java 8 (а мы же знаем, что уже и 7 не поддерживается пару лет, да и 8 осталось жить считанные месяцы возможно), нужно например купить Fuse 7.

            Ну т.е. может кому-то это и нужно, чтобы подстраховаться, а на уровне разработчика толку почти 0.

            Причем, поскольку у карафа есть скажем ssh консоль, это означает, что все процессы, которые делаются из консоли, можно легко автоматизировать груви скриптами, используя например ssh plugin от gradle. То есть, имея допустим установленный ServiceMix, я могу запустить скрипт, который получит список развернутых там фич и бандлов, с их maven координатами, и повторит это на голом karaf.


            1. igor_suhorukov
              17.05.2018 22:48

              С точки зрения разработчика я полностью согласен. С точки зрения менеджмента это выглядит непривычно, не платить за поддержку. Это так же как и с oracle coherence я тоже только страдал и декомпилировал, чтобы выжить. А менеджмент был счастлив что куплена «поддержка»


            1. igor_suhorukov
              17.05.2018 22:49

              Вы не наследие тройки диалог поддерживаете случаем?


              1. sshikov
                17.05.2018 22:50
                +1

                Поддерживал. Частично.


                1. igor_suhorukov
                  17.05.2018 22:54

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


    1. Throwable
      17.05.2018 13:17

      Меня всегда интересовал вопрос, как правильно поставить процесс разработки и тестирования в приложении с OSGi-контейнером? Модуль после каждого изменения приходится запаковывать и передеплоивать. Кроме того нет возможности стартануть контейнер из workspace с подцепленным classpath модуля, соответственно невозможно будет дебажить в IDE, или делать hot replace. Даже в JEE есть такие средства как Arquillian, OpenEJB, Microprofile, которые сильно упрощают разработку...


      1. vuper Автор
        17.05.2018 16:35

        Мы поступаем следующим образом. Разработка ведется локально. Java бины отлаживаются и тестируются, как standalone приложения (в pom прописываются зависимости аналогичные OSGi окружению). Также у каждого разработчика локально развернут ServiceMix, в котором отлаживаются роуты и решение целиком. Для отладки можно использовать remote debugging из IDE и инструмент hawtio.

        С тестированием дело обстоит сложнее. У Camel есть фреймворки для тестирования: Идея простая – можно мокать эндпоинты, посылать тестовые сообщения в роуты, и в мок энпоинтах сравнивать пришедшее сообщение с ожидаемым. Таким образом проверять, как отрабатывают роуты, не допуская отправки тестовых сообщений в реальные интегрируемые системы. При запуске тестов эмулируется OSGi окружение, в котором выполняются тесты. Но эмулируемое окружение не до конца соответствует реальному, поэтому нам это не подошло. Мы написали свой фреймворк для тестирования, он позволяет делать тоже самое, но тесты запускаются в реальном OSGi контейнере.


      1. sshikov
        17.05.2018 20:23
        +1

        В тестировании под OSGI нет почти никакой специфики. Во-первых, большинство бандлов в моей практике вообще ничего не знали о наличии контейнера — т.е. мы их тестируем в виде юнит тестов, без контейнера. Те же, которым нужен OSGI API, скажем, они пользуются сервисами 0..N, тестируются в контейнере с той же легкостью, как JEE тестируется там же начиная примерно с EJB 3.

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

        Запаковывать и передеплоивать? Ну хм, да, нужно. Это обычно настолько быстро, тем более все равно компилировать же.

        >нет возможности стартануть контейнер из workspace с подцепленным classpath модуля
        classpath модуля зависит от того, какие бандлы вы установили. IDE про это может ничего и не знать. Но вполне может и знать. Для IDEA есть соответствующая поддержка (правда, только в платной версии).

        Дебажить, hot replace? Всегда делал, никаких серьезных проблем не испытывал. Что я делал не так?


  1. igor_suhorukov
    16.05.2018 22:12

    Вадим, это реально не современное решение! Расскажи лучше людям про не поддерживаемые транзакции в Camel RabbitMQ компоненте и что всю сложность по реконсиляциям и взаимодействию перекладываешь на интегрируемые системы...


    1. vuper Автор
      17.05.2018 16:41

      Игорь, рад видеть тебя здесь.
      В Camel есть поддержка транзакций. Любой роут можно определить транзакционным, в этом случае, при возникновении ошибки происходит откат транзакции. Нужен только менеджер транзакций. Можно организовывать XA транзакции с несколькими дата сорсами. Что касается RabbitMQ, то он не поддерживает XA транзакции, но поддерживает обычные транзакции. Это позволяет в случае возникновения ошибки откатить транзакцию в дата сорсах и либо вернуть сообщение в исходную очередь для повторной обработки, либо в отдельную очередь для последующего разбора ошибки. Для наших задач этого вполне достаточно. Кроме того, ни одна технология не гарантирует 100% надежности, поэтому сверки между системами – полезная практика.


      1. igor_suhorukov
        17.05.2018 17:14

        Это все ясно, что RabbitMQ это умеет. Отлично знаю фичи Camel. Но в том что ты описываешь это не работает, так как в компоненте Camel-RabbitMQ до сих пор нет транзакционных продюсеров и консьюмеров… Так что похоже у вас на уровне шины возможны потери данных. По опыту работы серьезных международных инвестбанков, гарантии Exactly once или At Least Once должны быть в MOM/транспорте между системами, а в этом случае при падении servicemix сообщения заберутся из очереди и потеряются. Это деньги и репутация организации! Переносить ответственность транспорта и шины на интегрируемые системы не правильно!

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

        100% гарантий не бывает, но 99 с множеством 9 в периоде вполне! И к этому надо стремиться, а не пытаться переносить ответственность на интегрируемые системы)


        1. vuper Автор
          18.05.2018 15:14

          Ты очень узко видишь проблему.

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

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

          К обменам, в которых критичны потери данных, должно быть соответствующее критичное отношение, которое не ограничивается только передачей данных в транзакции. Такие обмены должны пристально мониториться и всегда должны быть механизмы решения возникающих проблем. Если бы мы жили в идеальном мире, не нужны бы были подразделения поддержки, администраторы баз данных и т.д. В нашем случае, все сообщения, которые передаются через ServiceMix, логируются и доступны для последующего анализа. Настроены аллерты, которые срабатывают при возникновении нештатных ситуация, плюс есть функционал, который позволяет повторно отправлять сообщения.

          Что касается потерь данных в RabbitMQ компоненте. Мы регулярно проводим тесты, роняем сервера ServiceMix при нагруженных обменах. Потерь не наблюдали.


          1. igor_suhorukov
            18.05.2018 15:34

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

            Конечно, у гарантий доставки есть своя цена. Следуя вашей логике нужно обложиться велосипедами, назначить ответственных и забить на транзакции совсем. А в учётных системах банка отключить целостность и внешние ключи, они же много ресурсов потребляют же. Настроить алерты, пусть сотрудники ночью во всех системах вручную восстанавливаются.
            Только в этом случае ни RabbitMQ не нужен, не реляционные БД. Это совсем другая область применимости и другой класс систем.

            Что касается потерь данных в RabbitMQ компоненте.

            Вся интеграция сделана через это место, судя по комментариям и статье.

            Вадим, крайне непрофессиональный подход к интеграции…
            Спасибо, расширил мое видение проблемы, вдохновил на публикацию!


    1. zQQrra
      17.05.2018 18:16

      А из того что доступно в open-source на данный момент, можете что то порекомендовать?


      1. igor_suhorukov
        17.05.2018 19:02

        То же самое, но «приготовленное» несколько иначе. Fabric8.io, например груви роуты проще отлаживать и работают все фичи из IntelliJ Idea при редактировании. Ну и RabbitMQ не лучшее решение для camel, по причинам что указал. ActiveMQ Artemis в качестве очереди сообщений. Apache Camel отличный фреймворк для интеграции чего угодно! И подход к интеграции более гуманный к интегрируемым системам, чем используется у автора статьи.


  1. igor_suhorukov
    16.05.2018 22:14

    А XML контекст спринга, так подавно веет 2000ми!


    1. sshikov
      17.05.2018 20:25

      Ну кстати, в отличие от обычного спринга, xml контекст можно деплоить в караф самого по себе. Т.е. без jar вовсе. И это чрезвычайно удобно. Если же хочется модного и молодежного — то blueprint по сути все тоже самое дает.


      1. igor_suhorukov
        17.05.2018 22:20

        Спасибо за разъяснения, но к счастью, я работал с karaf, jboss fuse, fabric8. Blueprint xml той же «молодежном и» что и spring xml context, очень похожий DI и так же позволяет не привязываться в коде к OSGI API и активаторам, как и спринг.


        1. sshikov
          17.05.2018 22:33

          Ну я тогда не понял, о чем именно был намек на XML, как артефакт из 2000-х?

          Иметь именно что голый XML, который представляет из себя совершенно полноценный бандл (ну, плюс-минус OSGI метаданные) — это же иногда реально необычайно удобно. Его еще и компилировать не нужно :)


          1. igor_suhorukov
            17.05.2018 22:37

            В его поддержке в IDE при разработке роутов. Груви тоже не нужно компилировать, а вот автодополнения для DSL camel работают даже в IntelliJ Community!


            1. sshikov
              17.05.2018 22:49

              А, я понял. Да, сами роуты наверное лучше не так делать, как тут автор расписал. Ну т.е. так тоже можно, и даже приятно, что они все еще работают в том же виде, после многих лет развития camel. Но уже какой-никакой split в xml делать — фу.

              P.S. Community не поддерживает Spring полноценно, но автодополнения camel xml из схемы вроде берутся? В смысле, они там тоже работают.


              1. igor_suhorukov
                17.05.2018 22:51

                Комьюнити отлично поддерживает роуты груви, я про это.


  1. Urgen
    17.05.2018 08:08

    а почему используете rabbitmq, а не activemq от того же апача?


    1. vuper Автор
      17.05.2018 12:47

      Мы смотрели оба продукта. Для нас была критична отказоустойчивость, чтобы можно было собрать кластер, который сохраняет полную работоспособность при падении одной из нод. У ActiveMQ есть возможность работать в кластере, для его работы требуется общее хранилище, которым может быть: сетевой диск, база данных или Zookeeper. Нужно было перебирать эти варианты и тестировать, чтобы выбрать наиболее подходящий, а во времени мы были ограничены. Для настройки кластера RabbitMQ ничего из вышеперечисленного не требуется, ноды синхронизируются через сокеты. Что очень важно, RabbitMQ в кластерной конфигурации с синхронизацией очередей показал лучшую производительность, чем ActiveMQ. Кроме того, подкупило то, что у RabbitMQ есть хорошая web консоль c широкими возможностями администрирования и с большим набором различных метрик.


      1. igor_suhorukov
        17.05.2018 17:17

        ActiveMQ Artemis не требует zookeeper и NFS…


      1. sshikov
        17.05.2018 20:50
        +1

        У ActiveMQ консоль тоже вполне хорошая. И потом, JMX же, многим можно рулить через него и Hawtio.


        1. igor_suhorukov
          17.05.2018 22:40

          Да, поддержка ActiveMQ в Hawtio отличная! Как и поддержка Camel