Общая реализация механизма репликации, описанная в документе
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
Репликация»