Общая реализация механизма репликации, описанная в документе AIREP,
остаётся прежней; в данном документе будет описаны особенности настройки
репликации применительно к транспорту (МОМ) HornetQ
, встроенному в JBoss AS7
.
Схема настройки: имеется один центральный экземпляр ARTA SYNERGY
(далее Center
) и несколько региональных (подчинённых). С
центрального экземпляра на подчинённые реплицируется три каталога хранилища
в режиме «точка-многоточка» и «master → slave». Реплицируемые каталоги:
/company_root/
Маркетинговые материалы
/company_root/
База знаний
/company_root/
Проекты
В примере показана настройка центрального экземпляра и одного регионального (далее Astana) — при необходимости, по такой же схеме можно настроить ещё несколько региональных экземпляров.
Как уже было упомянуто выше, для передачи сообщений репликации мы будем
использовать встроенный в JBoss AS7
транспорт HornetQ
, а именно, такие его
возможности, как:
Core Bridges
(вместо мостов JMS
, так как они имеют встроенную возможность
передоставки при потерях подключения и прочие полезные вещи; кроме этого,
они гораздо проще настраиваются)
Core Queues
— для использования совместно
с Core Bridges
JMS Queues
— для использования с ARTA SYNERGY
, так как реализация механизма
репликации, как заявлено в [AIREP]
, может
работать с любой JMS
-совместимой МОМ
Netty Connectors
— для использования в
соединении очередей мостами через TCP/IP
-сеть
Для начала нам необходимо дать возможность серверам Center и Astana
соединяться друг с другом, для этого нужно добавить соответствующий outbound-socket-binding
в секции server
→ socket-binding-group:
На сервере Center:
<outbound-socket-binding name="messaging-outbound-astana-binding"> <remote-destination host="192.168.1.144" port="5445"/> </outbound-socket-binding>
На сервере Astana:
<outbound-socket-binding name="messaging-outbound-center-binding"> <remote-destination host="192.168.1.160" port="5445"/> </outbound-socket-binding>
Кроме этого, надо отключить аутентификацию в кластере HornetQ (считаем, что наша сеть доверенная)
На обоих серверах в секции server → profile →
subsystem xmlns="urn:jboss:domain:messaging:1.2" → hornetq-server
добавляем:
<security-enabled>false</security-enabled>
Добавляем коннекторы, используя свежесозданные socket-binding
-и, в секции server →
profile → subsystem xmlns=“urn:jboss:domain:messaging:1.2” →
hornetq-server → connectors
На сервере Center:
<netty-connector name="netty-astana" socket-binding="messaging-outbound-astana-binding"/>
На сервере Astana:
<netty-connector name="netty-center" socket-binding="messaging-outbound-center-binding"/>
Так как Core Bridges
не поддерживают
JMS Topics
, для реализации передачи типа
«точка-многоточка» нам придётся использовать, мягко говоря, нестандартный
подход для решения этой задачи, а именно, на сервере Center:
Создаётся non-durable JMS
-очередь (в
нашем случае, 3 штуки, для каждой реплицируемой
области), которая будет непосредственным приёмником сообщений от ARTA SYNERGY
(в секции server → profile
→ subsystem xmlns=“urn:jboss:domain:messaging:1.2” →
hornetq-server →jms-destinations)
<jms-queue name="CenterBzRep"> <entry name="queue/CenterBzRep"/> <entry name="java:global/queue/CenterBzRep"/> <entry name="java:jboss/exported/jms/queue/CenterBzRep"/> <durable>false</durable> </jms-queue> <jms-queue name="CenterMmRep"> <entry name="queue/CenterMmRep"/> <entry name="java:global/queue/CenterMmRep"/> <entry name="java:jboss/exported/jms/queue/CenterMmRep"/> <durable>false</durable> </jms-queue> <jms-queue name="CenterProjectsRep"> <entry name="queue/CenterProjectsRep"/> <entry name="java:global/queue/CenterProjectsRep"/> <entry name="java:jboss/exported/jms/queue/CenterProjectsRep"/> <durable>false</durable> </jms-queue>
Обратите внимание, что у этой очереди добавляется ещё одна JNDI Name
Entry
, с префиксом java:global
. Именно это имя мы будем использовать
далее при настройке ARTA SYNERGY
Создаются соответствующие им Core
-очереди
— на самом деле, эти записи описывают те же самые очереди, что и в
предыдущем пункте (в секции server → profile → subsystem
xmlns=“urn:jboss:domain:messaging:1.2” → hornetq-server →
jms-destinations):
<queue name="jms.queue.CenterBzRep"> <address>jms.queue.CenterBzRep</address> <durable>false</durable> </queue> <queue name="jms.queue.CenterMmRep"> <address>jms.queue.CenterMmRep</address> <durable>false</durable> </queue> <queue name="jms.queue.CenterProjectsRep"> <address>jms.queue.CenterProjectsRep</address> <durable>false</durable> </queue>
Затем для каждой реплицируемой области (M
, в нашем случае M
=3) и каждого подчинённого сервера (N
, в нашем случае N
=1) создаём соответствующую Core
-очередь (всего должно получиться MxN
, в нашем случае 3 очереди), используя такой
трюк: задаём для каждой очереди, соответствующей реплицируемой области,
не уникальный адрес, а адрес соответствующей
JMS-очереди (он формируется как jms.queue.Name
, где Name
— атрибут name JMS
-очереди) — в секции server → profile
→ subsystem xmlns=“urn:jboss:domain:messaging:1.2”→
hornetq-server → core-queues:
<queue name="jms.queue.CenterBzRepAstana"> <address>jms.queue.CenterBzRep</address> <filter string="airClientId IN ('Astana', 'ALL')"/> <durable>true</durable> </queue> <queue name="jms.queue.CenterMmRepAstana"> <address>jms.queue.CenterMmRep</address> <filter string="airClientId IN ('Astana', 'ALL')"/> <durable>true</durable> </queue> <queue name="jms.queue.CenterProjectsRepAstana"> <address>jms.queue.CenterProjectsRep</address> <filter string="airClientId IN ('Astana', 'ALL')"/> <durable>true</durable> </queue>
Таким образом, сообщения, отправленные по указанном адресу, попадут в
MxN+M Core
-очереди — очереди,
соответствующие JMS
-очередям, будут
использованы как ретрансляторы сообщений в Per-Replica-Per-Client
-очереди. Эти последние
очереди будут использованы непосредственно в мостах, доставляющих сообщения
до таких же удалённых очередей, поэтому они durable
. Обратите внимание, что для каждой такой
очереди указан фильтр по ClientId
— это
идентификатор подчинённого сервера. В итоге практически всё готово, однако
остался вопрос о том, куда будут деваться сообщения из самих
очередей-ретрансляторов. Для этого мы создаём специальную «/dev/null
» Core
и JMS
-очередь; Core
-очередь для того, чтобы, используя Core
-мост, перемещать в неё сообщения из
ретрансляционных очередей, а JMS
-обёртку
к ней — чтобы задействовать имеющийся в ARTA SYNERGY специальный
NullListener JMS-очереди (который просто берет сообщения из очереди и
выбрасывает их). Итак, в секции server → profile → subsystem
xmlns=“urn:jboss:domain:messaging:1.2” → hornetq-server →
core-queues:
<queue name="jms.queue.Null"> <address>jms.queue.Null</address> <durable>false</durable> </queue>
И в секции server → profile → subsystem xmlns=“urn:jboss:domain:messaging:1.2” → hornetq-server → jms-destinations:
<jms-queue name="Null"> <entry name="queue/Null"/> <entry name="java:jboss/exported/jms/queue/Null"/> <durable>false</durable> </jms-queue>
Кроме этого, проверяем наличие JMS
-очереди с именем REQ
, которая нам потом потребуется для обратной
связи Astana → Center.
Для связи «точка-точка» таких извращений не требуется: одна durable
и JMS/Core
очередь для одной репликации +
соответствующая JMS-очередь на удалённом сервере.
Далее, на сервере Astana
всё чуть проще:
необходимо добавить M JMS
-очередей и одну
очередь для связи с очередью REQ
на
Center (в секции server →
profile → subsystem xmlns=“urn:jboss:domain:messaging:1.2” →
hornetq-server → jms-destinations):
<jms-queue name="CenterBzRepAstana"> <entry name="queue/CenterBzRepAstana"/> <entry name="java:global/queue/CenterBzRepAstana"/> <entry name="java:jboss/exported/jms/queue/CenterBzRepAstana"/> <durable>true</durable> </jms-queue> <jms-queue name="CenterMmRepAstana"> <entry name="queue/CenterMmRepAstana"/> <entry name="java:global/queue/CenterMmRepAstana"/> <entry name="java:jboss/exported/jms/queue/CenterMmRepAstana"/> <durable>true</durable> </jms-queue> <jms-queue name="CenterProjectsRepAstana"> <entry name="queue/CenterProjectsRepAstana"/> <entry name="java:global/queue/CenterProjectsRepAstana"/> <entry name="java:jboss/exported/jms/queue/CenterProjectsRepAstana"/> <durable>true</durable> </jms-queue> <jms-queue name="REQCenter"> <entry name="queue/REQCenter"/> <entry name="java:global/queue/REQCenter"/> <entry name="java:jboss/exported/jms/queue/REQCenter"/> <durable>true</durable> </jms-queue>
Кроме этого, последнюю очередь необходимо также описать и как Core
-очередь для использования в Core
-мосте (в секции server → profile →
subsystem xmlns=“urn:jboss:domain:messaging:1.2” →
hornetq-server → core-queues):
<queue name="jms.queue.REQCenter"> <address>jms.queue.REQCenter</address> <durable>true</durable> </queue>
Теперь необходимо соединить все соответствующие друг другу очереди мостами. Мосты настраиваются в секции server → profile → subsystem xmlns=“urn:jboss:domain:messaging:1.2” → hornetq-server → bridges.
Для сервера Center: * Мосты, соединяющие
очереди-ретрансляторы с Null
-очередью:
<bridge name="CenterBzRepBridge"> <queue-name>jms.queue.CenterBzRep</queue-name> <forwarding-address>jms.queue.Null</forwarding-address> <reconnect-attempts>-1</reconnect-attempts> <static-connectors> <connector-ref>netty</connector-ref> </static-connectors> </bridge> <bridge name="CenterMmRepBridge"> <queue-name>jms.queue.CenterMmRep</queue-name> <forwarding-address>jms.queue.Null</forwarding-address> <reconnect-attempts>-1</reconnect-attempts> <static-connectors> <connector-ref>netty</connector-ref> </static-connectors> </bridge> <bridge name="CenterProjectsRepBridge"> <queue-name>jms.queue.CenterProjectsRep</queue-name> <forwarding-address>jms.queue.Null</forwarding-address> <reconnect-attempts>-1</reconnect-attempts> <static-connectors> <connector-ref>netty</connector-ref> </static-connectors> </bridge> <bridge name="CenterBzRepAstanaBridge"> <queue-name>jms.queue.CenterBzRepAstana</queue-name> <forwarding-address>jms.queue.CenterBzRepAstana</forwarding-address> <reconnect-attempts>-1</reconnect-attempts> <static-connectors> <connector-ref>netty-astana</connector-ref> </static-connectors> </bridge>
И, наконец, мосты, соединяющие локальные Per-Replica-Per-User
-очереди с удалёнными. Чтобы
не возникало путаницы, названия удалённых очередей у нас такие же, как и
названия локальных (см. выше «Очереди», пункт про JMS
-очереди на Astana
). Обратите внимание на connector-ref
:
<bridge name="CenterBzRepAstanaBridge"> <queue-name>jms.queue.CenterBzRepAstana</queue-name> <forwarding-address>jms.queue.CenterBzRepAstana</forwarding-address> <reconnect-attempts>-1</reconnect-attempts> <static-connectors> <connector-ref>netty-astana</connector-ref> </static-connectors> </bridge> <bridge name="CenterMmRepAstanaBridge"> <queue-name>jms.queue.CenterMmRepAstana</queue-name> <forwarding-address>jms.queue.CenterMmRepAstana</forwarding-address> <reconnect-attempts>-1</reconnect-attempts> <static-connectors> <connector-ref>netty-astana</connector-ref> </static-connectors> </bridge> <bridge name="CenterProjectsRepAstanaBridge"> <queue-name>jms.queue.CenterProjectsRepAstana</queue-name> <forwarding-address>jms.queue.CenterProjectsRepAstana</forwarding-address> <reconnect-attempts>-1</reconnect-attempts> <static-connectors> <connector-ref>netty-astana</connector-ref> </static-connectors> </bridge>
Для сервера Astana требуется всего один
мост, соединяющий очередь REQCenter
с
очередью REQ
на Center:
<bridge name="REQCenterBridge"> <queue-name>jms.queue.REQCenter</queue-name> <forwarding-address>jms.queue.REQ</forwarding-address> <reconnect-attempts>-1</reconnect-attempts> <static-connectors> <connector-ref>netty-center</connector-ref> </static-connectors> </bridge>
Для всех очередей, участвующих в механизме репликации, необходимо указать настройку, обеспечивающую доставку постоянную и без задержек. В секции server → profile → subsystem xmlns=“urn:jboss:domain:messaging:1.2” → hornetq-server → address-settings:
<address-setting match="jms.queue.Center#"> <redelivery-delay>0</redelivery-delay> <max-delivery-attempts>-1</max-delivery-attempts> </address-setting>
(Ранее мы предусмотрительно добавили ко всем именам очередей, которые
участвуют в репликации, префикс Center
,
поэтому мы можем воспользоваться оконечным wild-card
-ом «#»).
Способ настройки ARTA SYNERGY
для
репликации ничем не отличается от кого, который применялся при использовании
JBoss AS5
.
На сервере Center
, файл
${jboss.server.config.dir}ai/rrc.xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <configuration xmlns="http://www.arta.kz/xml/ns/ai" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.arta.kz/xml/ns/ai http://www.arta.kz/xml/ns/ai/rrc.xsd"> <broadcast-interval>60000</broadcast-interval> <!-- Multicast master-to-slave replications --> <replication> <name>CENTERBZREP</name> <path>/company_root/База знаний</path> <!-- Помните про java:global в описании очередей? --> <jms-destination>java:global/queue/CenterBzRep</jms-destination> </replication> <replication> <name>CENTERMMREP</name> <path>/company_root/Маркетинговые материалы</path> <jms-destination>java:global/queue/CenterMmRep</jms-destination> </replication> <replication> <name>CENTERPROJECTSREP</name> <path>/company_root/Проекты</path> <jms-destination>java:global/queue/CenterProjectsRep</jms-destination> </replication> <!-- END Multicast master-to-slave replications --> </configuration>
На сервере Astana, файл ${jboss.server.base.dir}/deployments/Synergy.ear/rr.jar/META-INF/ejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?> <ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"> <enterprise-beans> <message-driven> <ejb-name>RepositoryExporterSubscriberCenterProjectsRep</ejb-name> <ejb-class>arta.information.replication.subscriber.RepositoryExporterSubscriber</ejb-class> <activation-config> <activation-config-property> <activation-config-property-name>destination</activation-config-property-name> <activation-config-property-value>queue/CenterProjectsRepAstana</activation-config-property-value> </activation-config-property> <activation-config-property> <activation-config-property-name>destinationType</activation-config-property-name> <activation-config-property-value>javax.jms.Queue</activation-config-property-value> </activation-config-property> <activation-config-property> <activation-config-property-name>maxSession</activation-config-property-name> <activation-config-property-value>1</activation-config-property-value> </activation-config-property> <activation-config-property> <activation-config-property-name>useDLQ</activation-config-property-name> <activation-config-property-value>false</activation-config-property-value> </activation-config-property> </activation-config> <env-entry> <env-entry-name>param/dest</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>/company_root</env-entry-value> </env-entry> <env-entry> <env-entry-name>param/clientId</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>Astana</env-entry-value> </env-entry> <env-entry> <env-entry-name>param/needImport</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <!-- false — не использовать инициализацию --> <env-entry-value>true</env-entry-value> </env-entry> <message-destination-ref> <message-destination-ref-name>jms/Outbox</message-destination-ref-name> <message-destination-type>javax.jms.Queue</message-destination-type> <mapped-name>java:global/queue/REQCenter</mapped-name> </message-destination-ref> </message-driven> <message-driven> <ejb-name>RepositoryExporterSubscriberCenterBzRep</ejb-name> <ejb-class>arta.information.replication.subscriber.RepositoryExporterSubscriber</ejb-class> <activation-config> <activation-config-property> <activation-config-property-name>destination</activation-config-property-name> <activation-config-property-value>queue/CenterBzRepAstana</activation-config-property-value> </activation-config-property> <activation-config-property> <activation-config-property-name>destinationType</activation-config-property-name> <activation-config-property-value>javax.jms.Queue</activation-config-property-value> </activation-config-property> <activation-config-property> <activation-config-property-name>maxSession</activation-config-property-name> <activation-config-property-value>1</activation-config-property-value> </activation-config-property> <activation-config-property> <activation-config-property-name>useDLQ</activation-config-property-name> <activation-config-property-value>false</activation-config-property-value> </activation-config-property> </activation-config> <env-entry> <env-entry-name>param/dest</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>/company_root</env-entry-value> </env-entry> <env-entry> <env-entry-name>param/clientId</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>Astana</env-entry-value> </env-entry> <env-entry> <env-entry-name>param/needImport</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <!-- false — не использовать инициализацию --> <env-entry-value>true</env-entry-value> </env-entry> <message-destination-ref> <message-destination-ref-name>jms/Outbox</message-destination-ref-name> <message-destination-type>javax.jms.Queue</message-destination-type> <mapped-name>java:global/queue/REQCenter</mapped-name> </message-destination-ref> </message-driven> <message-driven> <ejb-name>RepositoryExporterSubscriberCenterMmRep</ejb-name> <ejb-class>arta.information.replication.subscriber.RepositoryExporterSubscriber</ejb-class> <activation-config> <activation-config-property> <activation-config-property-name>destination</activation-config-property-name> <activation-config-property-value>queue/CenterMmRepAstana</activation-config-property-value> </activation-config-property> <activation-config-property> <activation-config-property-name>destinationType</activation-config-property-name> <activation-config-property-value>javax.jms.Queue</activation-config-property-value> </activation-config-property> <activation-config-property> <activation-config-property-name>maxSession</activation-config-property-name> <activation-config-property-value>1</activation-config-property-value> </activation-config-property> <activation-config-property> <activation-config-property-name>useDLQ</activation-config-property-name> <activation-config-property-value>false</activation-config-property-value> </activation-config-property> </activation-config> <env-entry> <env-entry-name>param/dest</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>/company_root</env-entry-value> </env-entry> <env-entry> <env-entry-name>param/clientId</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>Astana</env-entry-value> </env-entry> <env-entry> <env-entry-name>param/needImport</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <!-- false — не использовать инициализацию --> <env-entry-value>true</env-entry-value> </env-entry> <message-destination-ref> <message-destination-ref-name>jms/Outbox</message-destination-ref-name> <message-destination-type>javax.jms.Queue</message-destination-type> <mapped-name>java:global/queue/REQCenter</mapped-name> </message-destination-ref> </message-driven> </enterprise-beans> </ejb-jar>
Не забудьте добавить архив rr.jar в application.xml Synergy.ear-а.
Настройка multi-master
«точка-точка»
репликации оставляется в качестве упражнения читателю.
AIREP
: Игорь Жуков , “AI
Репликация”