Дерево страниц
Перейти к концу метаданных
Переход к началу метаданных

Общее описание

Масштабирование сервиса Eltex-APB предполагает создание кластера из серверов APB и балансировку нагрузки на разные ноды кластера (доступно с версии SoftWLC 1.18). Для балансировки нагрузки используется проксирующий веб-сервер NGINX (может быть использован другой тип сервера). На нодах APB для синхронизации информации о пользователях-WiFi используется EHCACHE (универсальная система распределенного кэширования общего назначения). Для гарантированной доставки информации по синхронизации между нодами используется брокер сообщений ActiveMQ. Общая схема приведена ниже, на рис. 1.

Рис. 1.

Балансировка нагрузки происходит следующим образом:

1) ТД пытается подключиться к APB и шлет запрос на регистрацию на адрес балансировщика;

2) Балансировщик, с помощью проксирующего веб-сервера, перенаправляет этот запрос на одну из нод APB, распределяя ТД по source ip на различные ноды (могут использоваться различные алгоритмы балансировки);

3) ТД регистрируется на APB;

4) При авторизации пользователя ТД передает информацию о его сессии на одну из нод APB;

5) Получив эту информацию, APB передает её брокеру сообщений ActiveMQ, который выполняет распространяет её на остальные ноды кластера APB.

Как видно из приведенной схемы на рис.1, масштабирование сервиса обеспечивается кластером нод APB. За распределение нагрузки отвечает отдельный балансировщик, который выступает в качестве сервера APB для всех ТД, скрывая от них структуру кластера APB, и таким образом не требуя выполнения каких либо дополнительных настроек на ТД. Синхронизацию информации о сессиях пользователей WiFi обеспечивается с помощью EHCACHE. Гарантированную доставку сообщений синхронизации обеспечивает ActiveMQ. Таким образом обеспечивается консистентность данных о сессиях пользователей на всех нодах APB, что позволяет ТД получить актуальную информацию независимо от того к какой ноде APB она выполнила подключение.

Ниже, в таблице 1, приведны рекомендации по выбору количества нод в зависимости от количества ТД на платформе, использующих подключение к сервису APB.

Кол-во ТДколичество нод APBПримечание
10к1
20к2
30к3
>30киз расчета 1 нода на каждые 10к + 1

Таблица 1.

Настройка кластера APB

Настройка кластера APB рассматривается на примере. Предполагается, что требуется обеспечить возможность подключения до 30к ТД. В этом случае потребуется минимум 3 ноды.

Кластер включает в себя:

1) три ноды с установленным пакетом Eltex-APB - каждая на отдельном сервере или VM;

2) два сервера (основной и резервный), на которых будут развернуты NGINX и ActiveMQ, зарезервированные с помощью keepalived. Резервирование предполагается в режиме Active - Standby - при отказе основного сервера в работу вступает резервный.

Ниже, на рис. 2 приведена схема включения:

Рис. 2.

Кластер APB должен иметь как минимум две ноды, возможно расширение на большее число. При отказе одной из нод, NGINX определяет это и перестает проксировать подключения ТД на неё, распределяя ТД по оставшимся в работе нодам. Резервирование серверов с NGINX и ActiveMQ осуществляется по схеме active-standby по протоколу VRRP.

Используемые порты:

СервисПортПротоколНазначение
Eltex-APB8090TCPпрослушивание проксированных запросов от ТД с сервера балансировщика
Eltex-APB40001TCPобнаружение соседних нод в юникастовом режиме, отправка анонсов
Eltex-APB4446UDPобнаружение соседних нод в мультикастовом режиме, отправка мультикастовых анонсов
ActiveMQ61616TCPпрослушивание обращений от нод кластера APB
ActiveMQ8161TCPвеб-консоль для мониторинга
Nginx8090TCPпрослушивание запросов от ТД

Общие замечания по сетевой архитектуре:

1) ActiveMQ и Nginx могут находится на разных серверах.

2) Ноды APB, ActiveMQ и Nginx могут находится по отношению друг к другу в разных подсетях, доступных через маршрутизаторы. В этом случае, если используется мультикастовый режим для построения кластера - то должна быть обеспечена мультикастовая маршрутизация между нодами APB.

Настройка APB для работы в режиме кластера

Для корректной работы кластера APB файлы /etc/eltex-apb/apb.properties, /etc/eltex-apb/hosts.json и /etc/eltex-apb/hosts_kassa.json должны быть идентичны на всех нодах.

В файле /etc/default/eltex-apb добавляем для каждой ноды её адрес.

Для node-1:

JMX_OPTS="-Djava.rmi.server.hostname=100.123.0.3"

Для node-2:

JMX_OPTS="-Djava.rmi.server.hostname=100.123.0.5"

Для node-3:

JMX_OPTS="-Djava.rmi.server.hostname=100.123.0.7"


Настройка кластера выполняется в файле /etc/eltex-apb/ehcache.xml .

Для включения режима кластера на каждой из нод конфигурируем:

 APB node-1
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd" updateCheck="false" monitoring="autodetect"
         dynamicConfig="true">

    <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" 
            properties="port=40001,
                        hostName=100.123.0.3"/>

    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToLiveSeconds="8600"
            timeToIdleSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>
    </defaultCache>

    <cache name="users"
           maxBytesLocalHeap="1G"
           eternal="false"
           timeToLiveSeconds="43200"
           memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>

        <searchable keys="true" values="false" allowDynamicIndexing="false">
            <searchAttribute name="mobilityDomainBean" class="org.eltex.softwlc.apb.cache.MobilityDomainAttributeExtractor"/>
            <searchAttribute name="account" class="org.eltex.softwlc.apb.cache.AccountAttributeExtractor"/>
        </searchable>

        <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory" 
                properties=" 
                replicateAsynchronously=true,
                replicatePuts=true,
                replicateUpdates=true,
                replicateUpdatesViaCopy=true,
                replicateRemovals=true,
                asynchronousReplicationIntervalMillis=1000" 
        />
        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" 
                properties="bootstrapAsynchronously=false" 
        />

    </cache>

    <cache name="hosts" maxBytesLocalHeap="128M" eternal="true">
        <persistence strategy="none"/>
    </cache>

</ehcache>
 APB node-2
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd" updateCheck="false" monitoring="autodetect"
         dynamicConfig="true">

    <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" 
            properties="port=40001,
                        hostName=100.123.0.5"/>

    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToLiveSeconds="8600"
            timeToIdleSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>
    </defaultCache>

    <cache name="users"
           maxBytesLocalHeap="1G"
           eternal="false"
           timeToLiveSeconds="43200"
           memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>

        <searchable keys="true" values="false" allowDynamicIndexing="false">
            <searchAttribute name="mobilityDomainBean" class="org.eltex.softwlc.apb.cache.MobilityDomainAttributeExtractor"/>
            <searchAttribute name="account" class="org.eltex.softwlc.apb.cache.AccountAttributeExtractor"/>
        </searchable>

        <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory" 
                properties=" 
                replicateAsynchronously=true,
                replicatePuts=true,
                replicateUpdates=true,
                replicateUpdatesViaCopy=true,
                replicateRemovals=true,
                asynchronousReplicationIntervalMillis=1000" 
        />

        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" 
                properties="bootstrapAsynchronously=false" 
        />

    </cache>

    <cache name="hosts" maxBytesLocalHeap="128M" eternal="true">
        <persistence strategy="none"/>
    </cache>

</ehcache>
 APB node-3
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd" updateCheck="false" monitoring="autodetect"
         dynamicConfig="true">

    <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" 
            properties="port=40001,
                        hostName=100.123.0.7"/>

    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToLiveSeconds="8600"
            timeToIdleSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>
    </defaultCache>

    <cache name="users"
           maxBytesLocalHeap="1G"
           eternal="false"
           timeToLiveSeconds="43200"
           memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>

        <searchable keys="true" values="false" allowDynamicIndexing="false">
            <searchAttribute name="mobilityDomainBean" class="org.eltex.softwlc.apb.cache.MobilityDomainAttributeExtractor"/>
            <searchAttribute name="account" class="org.eltex.softwlc.apb.cache.AccountAttributeExtractor"/>
        </searchable>

        <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory" 
                properties=" 
                replicateAsynchronously=true,
                replicatePuts=true,
                replicateUpdates=true,
                replicateUpdatesViaCopy=true,
                replicateRemovals=true,
                asynchronousReplicationIntervalMillis=1000" 
        />

        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" 
                properties="bootstrapAsynchronously=false" 
        />

    </cache>

    <cache name="hosts" maxBytesLocalHeap="128M" eternal="true">
        <persistence strategy="none"/>
    </cache>

</ehcache>


Добавляем настройку для взаимодействия с ActiveMQ, которая будет одинаковой для каждого из режимов:

 APB node-1/2 connect section for ActiveMQ
    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.jms.JMSCacheManagerPeerProviderFactory" 
            properties=" 
                providerURL=failover:tcp://100.123.0.11:61616,
                initialContextFactoryName=org.eltex.softwlc.apb.cache.replication.ActiveMQInitialContextFactoryImpl,
                replicationTopicConnectionFactoryBindingName=apbTopicConnectionFactory,
                replicationTopicBindingName=apbTopic,
                getQueueConnectionFactoryBindingName=apbQueueConnectionFactory,
                getQueueBindingName=apbQueue,
                timeoutMillis=10000" 
    />

Далее настраиваем взаимодействие в кластере. Включение нод в режим кластера может осуществляться в двух режимах - юникастовом и мультикастовом.

Для его включения мультикастового режима добавляем в файл /etc/eltex-apb/ehcache.xml для node 1, 2, 3 соответственно:

 APB node-1 cluster mode multicast
    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" 
            properties="peerDiscovery=automatic, 
                        multicastGroupAddress=224.1.0.1,
                        multicastGroupPort=4446,
                        timeToLive=1,
                        hostName=100.123.0.3"/>
 APB node-2 cluster mode multicast
    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" 
            properties="peerDiscovery=automatic,
                        multicastGroupAddress=224.1.0.1,
                        multicastGroupPort=4446,
                        timeToLive=1,
                        hostName=100.123.0.5"/>
 APB node-3 cluster mode multicast
    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" 
            properties="peerDiscovery=automatic,
                        multicastGroupAddress=224.1.0.1,
                        multicastGroupPort=4446,
                        timeToLive=1,
                        hostName=100.123.0.7"/>


Если используется юникастовый режим, то добавляем настройки:

 APB node-1 cluster mode unicast
    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" 
            properties="peerDiscovery=manual,
                        rmiUrls=//100.123.0.5:40001/users|//100.123.0.7:40001/users"/>
 APB node-2 cluster mode unicast
    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" 
            properties="peerDiscovery=manual,
                        rmiUrls=//100.123.0.3:40001/users|//100.123.0.7:40001/users"/>
 APB node-3 cluster mode unicast
    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" 
            properties="peerDiscovery=manual,
                        rmiUrls=//100.123.0.3:40001/users|//100.123.0.5:40001/users"/>


Как видно из приведенных настроек, при настройке юникастового режима необходимо указать все соседние ноды. Таким образом, при добавлении каждой новой ноды нужно будет выполнять изменение конфигурации на всех остальных нодах. Например при добавлении node-4 с адресом 100.123.0.9 настройка, отвечающая за взаимодействия с соседними нодами будет выглядеть так:

node-1: rmiUrls=//100.123.0.5:40001/users|//100.123.0.7:40001/users|//100.123.0.9:40001/users

node-2: rmiUrls=//100.123.0.3:40001/users|//100.123.0.7:40001/users|//100.123.0.9:40001/users

node-3: rmiUrls=//100.123.0.3:40001/users|//100.123.0.5:40001/users|//100.123.0.9:40001/users

node-4: rmiUrls=//100.123.0.3:40001/users|//100.123.0.5:40001/users|//100.123.0.7:40001/users

После внесения изменений необходимо выполнить перезапуск сервиса eltex-apb на каждой ноде: sudo systemctl restart eltex-apb.

Внимание!

Если ActiveMQ недоступен - то сервис eltex-apb, ожидая подключения к ActiveMQ, не будет доступен по своему IP-адресу:порту(8090).

Полные настройки /etc/eltex-apb/ehcache.xml

Для мультикастового режима кластера:

 /etc/eltex-apb/ehcache.xml node-1 multicast mode
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd" updateCheck="false" monitoring="autodetect"
         dynamicConfig="true">

    <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" 
            properties="port=40001,
                        hostName=100.123.0.3"/>

    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToLiveSeconds="8600"
            timeToIdleSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>
    </defaultCache>

    <cache name="users"
           maxBytesLocalHeap="1G"
           eternal="false"
           timeToLiveSeconds="43200"
           memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>

        <searchable keys="true" values="false" allowDynamicIndexing="false">
            <searchAttribute name="mobilityDomainBean" class="org.eltex.softwlc.apb.cache.MobilityDomainAttributeExtractor"/>
            <searchAttribute name="account" class="org.eltex.softwlc.apb.cache.AccountAttributeExtractor"/>
        </searchable>

        <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory" 
                properties=" 
                replicateAsynchronously=true,
                replicatePuts=true,
                replicateUpdates=true,
                replicateUpdatesViaCopy=true,
                replicateRemovals=true,
                asynchronousReplicationIntervalMillis=1000" 
        />
        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" 
                properties="bootstrapAsynchronously=false" 
        />

    </cache>

    <cache name="hosts" maxBytesLocalHeap="128M" eternal="true">
        <persistence strategy="none"/>
    </cache>

    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.jms.JMSCacheManagerPeerProviderFactory" 
            properties=" 
                providerURL=failover:tcp://100.123.0.11:61616,
                initialContextFactoryName=org.eltex.softwlc.apb.cache.replication.ActiveMQInitialContextFactoryImpl,
                replicationTopicConnectionFactoryBindingName=apbTopicConnectionFactory,
                replicationTopicBindingName=apbTopic,
                getQueueConnectionFactoryBindingName=apbQueueConnectionFactory,
                getQueueBindingName=apbQueue,
                timeoutMillis=10000" 
    />

    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" 
            properties="peerDiscovery=automatic, 
                        multicastGroupAddress=224.1.0.1,
                        multicastGroupPort=4446,
                        timeToLive=1,
                        hostName=100.123.0.3"/>

</ehcache>
 /etc/eltex-apb/ehcache.xml node-2 multicast mode
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd" updateCheck="false" monitoring="autodetect"
         dynamicConfig="true">

    <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" 
            properties="port=40001,
                        hostName=100.123.0.5"/>

    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToLiveSeconds="8600"
            timeToIdleSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>
    </defaultCache>

    <cache name="users"
           maxBytesLocalHeap="1G"
           eternal="false"
           timeToLiveSeconds="43200"
           memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>

        <searchable keys="true" values="false" allowDynamicIndexing="false">
            <searchAttribute name="mobilityDomainBean" class="org.eltex.softwlc.apb.cache.MobilityDomainAttributeExtractor"/>
            <searchAttribute name="account" class="org.eltex.softwlc.apb.cache.AccountAttributeExtractor"/>
        </searchable>

        <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory" 
                properties=" 
                replicateAsynchronously=true,
                replicatePuts=true,
                replicateUpdates=true,
                replicateUpdatesViaCopy=true,
                replicateRemovals=true,
                asynchronousReplicationIntervalMillis=1000" 
        />

        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" 
                properties="bootstrapAsynchronously=false" 
        />

    </cache>

    <cache name="hosts" maxBytesLocalHeap="128M" eternal="true">
        <persistence strategy="none"/>
    </cache>

    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.jms.JMSCacheManagerPeerProviderFactory" 
            properties=" 
                providerURL=failover:tcp://100.123.0.11:61616,
                initialContextFactoryName=org.eltex.softwlc.apb.cache.replication.ActiveMQInitialContextFactoryImpl,
                replicationTopicConnectionFactoryBindingName=apbTopicConnectionFactory,
                replicationTopicBindingName=apbTopic,
                getQueueConnectionFactoryBindingName=apbQueueConnectionFactory,
                getQueueBindingName=apbQueue,
                timeoutMillis=10000" 
    />

    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" 
            properties="peerDiscovery=automatic,
                        multicastGroupAddress=224.1.0.1,
                        multicastGroupPort=4446,
                        timeToLive=1,
                        hostName=100.123.0.5"/>

</ehcache>



 /etc/eltex-apb/ehcache.xml node-3 multicast mode
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd" updateCheck="false" monitoring="autodetect"
         dynamicConfig="true">

    <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" 
            properties="port=40001,
                        hostName=100.123.0.7"/>

    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToLiveSeconds="8600"
            timeToIdleSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>
    </defaultCache>

    <cache name="users"
           maxBytesLocalHeap="1G"
           eternal="false"
           timeToLiveSeconds="43200"
           memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>

        <searchable keys="true" values="false" allowDynamicIndexing="false">
            <searchAttribute name="mobilityDomainBean" class="org.eltex.softwlc.apb.cache.MobilityDomainAttributeExtractor"/>
            <searchAttribute name="account" class="org.eltex.softwlc.apb.cache.AccountAttributeExtractor"/>
        </searchable>

        <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory" 
                properties=" 
                replicateAsynchronously=true,
                replicatePuts=true,
                replicateUpdates=true,
                replicateUpdatesViaCopy=true,
                replicateRemovals=true,
                asynchronousReplicationIntervalMillis=1000" 
        />

        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" 
                properties="bootstrapAsynchronously=false" 
        />

    </cache>

    <cache name="hosts" maxBytesLocalHeap="128M" eternal="true">
        <persistence strategy="none"/>
    </cache>

    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.jms.JMSCacheManagerPeerProviderFactory" 
            properties=" 
                providerURL=failover:tcp://100.123.0.11:61616,
                initialContextFactoryName=org.eltex.softwlc.apb.cache.replication.ActiveMQInitialContextFactoryImpl,
                replicationTopicConnectionFactoryBindingName=apbTopicConnectionFactory,
                replicationTopicBindingName=apbTopic,
                getQueueConnectionFactoryBindingName=apbQueueConnectionFactory,
                getQueueBindingName=apbQueue,
                timeoutMillis=10000" 
    />

    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" 
            properties="peerDiscovery=automatic,
                        multicastGroupAddress=224.1.0.1,
                        multicastGroupPort=4446,
                        timeToLive=1,
                        hostName=100.123.0.7"/>

</ehcache>




Для юникастового режима:

 /etc/eltex-apb/ehcache.xml node-1 unicast mode
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd" updateCheck="false" monitoring="autodetect"
         dynamicConfig="true">

    <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" 
            properties="port=40001,
                        hostName=100.123.0.3"/>

    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToLiveSeconds="8600"
            timeToIdleSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>
    </defaultCache>

    <cache name="users"
           maxBytesLocalHeap="1G"
           eternal="false"
           timeToLiveSeconds="43200"
           memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>

        <searchable keys="true" values="false" allowDynamicIndexing="false">
            <searchAttribute name="mobilityDomainBean" class="org.eltex.softwlc.apb.cache.MobilityDomainAttributeExtractor"/>
            <searchAttribute name="account" class="org.eltex.softwlc.apb.cache.AccountAttributeExtractor"/>
        </searchable>

        <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory" 
                properties=" 
                replicateAsynchronously=true,
                replicatePuts=true,
                replicateUpdates=true,
                replicateUpdatesViaCopy=true,
                replicateRemovals=true,
                asynchronousReplicationIntervalMillis=1000" 
        />
        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" 
                properties="bootstrapAsynchronously=false" 
        />

    </cache>

    <cache name="hosts" maxBytesLocalHeap="128M" eternal="true">
        <persistence strategy="none"/>
    </cache>

    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.jms.JMSCacheManagerPeerProviderFactory" 
            properties=" 
                providerURL=failover:tcp://100.123.0.11:61616,
                initialContextFactoryName=org.eltex.softwlc.apb.cache.replication.ActiveMQInitialContextFactoryImpl,
                replicationTopicConnectionFactoryBindingName=apbTopicConnectionFactory,
                replicationTopicBindingName=apbTopic,
                getQueueConnectionFactoryBindingName=apbQueueConnectionFactory,
                getQueueBindingName=apbQueue,
                timeoutMillis=10000" 
    />

    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" 
            properties="peerDiscovery=manual,
                        rmiUrls=rmiUrls=//100.123.0.5:40001/users|//100.123.0.7:40001/users"/>

</ehcache>
 /etc/eltex-apb/ehcache.xml node-2 unicast mode
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd" updateCheck="false" monitoring="autodetect"
         dynamicConfig="true">

    <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" 
            properties="port=40001,
                        hostName=100.123.0.5"/>

    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToLiveSeconds="8600"
            timeToIdleSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>
    </defaultCache>

    <cache name="users"
           maxBytesLocalHeap="1G"
           eternal="false"
           timeToLiveSeconds="43200"
           memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>

        <searchable keys="true" values="false" allowDynamicIndexing="false">
            <searchAttribute name="mobilityDomainBean" class="org.eltex.softwlc.apb.cache.MobilityDomainAttributeExtractor"/>
            <searchAttribute name="account" class="org.eltex.softwlc.apb.cache.AccountAttributeExtractor"/>
        </searchable>

        <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory" 
                properties=" 
                replicateAsynchronously=true,
                replicatePuts=true,
                replicateUpdates=true,
                replicateUpdatesViaCopy=true,
                replicateRemovals=true,
                asynchronousReplicationIntervalMillis=1000" 
        />

        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" 
                properties="bootstrapAsynchronously=false" 
        />

    </cache>

    <cache name="hosts" maxBytesLocalHeap="128M" eternal="true">
        <persistence strategy="none"/>
    </cache>

    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.jms.JMSCacheManagerPeerProviderFactory" 
            properties=" 
                providerURL=failover:tcp://100.123.0.11:61616,
                initialContextFactoryName=org.eltex.softwlc.apb.cache.replication.ActiveMQInitialContextFactoryImpl,
                replicationTopicConnectionFactoryBindingName=apbTopicConnectionFactory,
                replicationTopicBindingName=apbTopic,
                getQueueConnectionFactoryBindingName=apbQueueConnectionFactory,
                getQueueBindingName=apbQueue,
                timeoutMillis=10000" 
    />

    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" 
            properties="peerDiscovery=manual,
                        rmiUrls=rmiUrls=//100.123.0.3:40001/users|//100.123.0.7:40001/users"/>

</ehcache>
 /etc/eltex-apb/ehcache.xml node-3 unicast mode
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd" updateCheck="false" monitoring="autodetect"
         dynamicConfig="true">

    <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" 
            properties="port=40001,
                        hostName=100.123.0.7"/>

    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToLiveSeconds="8600"
            timeToIdleSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>
    </defaultCache>

    <cache name="users"
           maxBytesLocalHeap="1G"
           eternal="false"
           timeToLiveSeconds="43200"
           memoryStoreEvictionPolicy="LRU">
        <pinning store="inCache" />
        <persistence strategy="none"/>

        <searchable keys="true" values="false" allowDynamicIndexing="false">
            <searchAttribute name="mobilityDomainBean" class="org.eltex.softwlc.apb.cache.MobilityDomainAttributeExtractor"/>
            <searchAttribute name="account" class="org.eltex.softwlc.apb.cache.AccountAttributeExtractor"/>
        </searchable>

        <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory" 
                properties=" 
                replicateAsynchronously=true,
                replicatePuts=true,
                replicateUpdates=true,
                replicateUpdatesViaCopy=true,
                replicateRemovals=true,
                asynchronousReplicationIntervalMillis=1000" 
        />

        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" 
                properties="bootstrapAsynchronously=false" 
        />

    </cache>

    <cache name="hosts" maxBytesLocalHeap="128M" eternal="true">
        <persistence strategy="none"/>
    </cache>

    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.jms.JMSCacheManagerPeerProviderFactory" 
            properties=" 
                providerURL=failover:tcp://100.123.0.11:61616,
                initialContextFactoryName=org.eltex.softwlc.apb.cache.replication.ActiveMQInitialContextFactoryImpl,
                replicationTopicConnectionFactoryBindingName=apbTopicConnectionFactory,
                replicationTopicBindingName=apbTopic,
                getQueueConnectionFactoryBindingName=apbQueueConnectionFactory,
                getQueueBindingName=apbQueue,
                timeoutMillis=10000" 
    />

    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" 
            properties="peerDiscovery=manual,
                        rmiUrls=rmiUrls=//100.123.0.3:40001/users|//100.123.0.5:40001/users"/>

</ehcache>

Подробнее про конфигурирование и возможности EHCACHE можно ознакомиться: https://www.ehcache.org/

Настройка ActiveMQ

Устанавливаем ActiveMQ. Инструкцию можно найти на официальном сайте: https://activemq.apache.org/installation

В качестве ОС используем Ubuntu Server версии 16/18. Так же требуется установить Java JDK 8. 

Загружаем ActiveMQ с официального сайта https://activemq.apache.org/components/classic/download/.

Важно

В файле /etc/hosts должно быть указано имя сервера.

Cоздаем каталог /opt/activemq и распаковываем в него архив с ActiveMQ. Задаем всем файлам и папкам в качестве владельца root.

Создаем символическую ссылку: sudo ln -s /opt/activemq/apache-activemq-5.16.0 /opt/activemq/run

Для запуска через systemd создаем файл /lib/systemd/system/activemq.service (владелец root) и в нем настраиваем параметры запуска ActiveMQ:

 /lib/systemd/system/activemq.service
[Unit]
Description=Apache ActiveMQ
After=network-online.target

[Service]
Type=forking
WorkingDirectory=/opt/activemq/run/bin
ExecStart=/opt/activemq/run/bin/activemq start
ExecStop=/opt/activemq/run/bin/activemq stop
Restart=on-abort
RestartSec=60
User=root
Group=root

[Install]
WantedBy=multi-user.target

Затем файле /opt/activemq/run/conf/activemq.xml, находим в нем строку transportConnector name="openwire" и проверяем, что адрес, на котором осуществляется прослушивание "0.0.0.0":

<transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>

На этот адрес:порт ожидаются подключения с нод APB для синхронизации состояния кэша.

В файле /opt/activemq/run/conf/jetty.xml находим строку <property name="host" value="127.0.0.1"/> и заменяем на адрес "0.0.0.0":

  /opt/activemq/run/conf/jetty.xml
    <bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start">
             <!-- the default port number for the web console -->
        <property name="host" value="0.0.0.0"/>
        <property name="port" value="8161"/>
    </bean>

Данный адрес:порт служит для подключения через веб к консоли мониторинга ActiveMQ.

Далее выполняем:

sudo systemctl daemon-reload
sudo systemctl enable activemq
sudo systemctl start activemq

Настройка Nginx

Для начала нужно установить в систему Nginx. Инструкцию можно найти на официальном сайте: https://nginx.ru/en/linux_packages.html#stable. Рекомендуется использовать версию 1.12 и новее.

Проверим и при необходимости настроим файл конфигурации Nginx (если установка была выполнена с дефолтными параметрами он будет в /etc/nginx/nginx.conf)

 /etc/nginx/nginx.conf
user  nginx;
worker_processes  10;
worker_rlimit_nofile 100000;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  100000;
    multi_accept on;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;

    keepalive_timeout  120;
    keepalive_requests 10000;
    reset_timedout_connection on;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

Параметры, на которые надо обратить внимание:

worker_processes - число рабочих процессов.
worker_rlimit_nofile - максимальное число открытых файлов для рабочих процессов.
worker_connections - максимальное количество соединений, которое может быть открыто рабочим процессом.
keepalive_requests - Задаёт максимальное число запросов, которые можно сделать по одному keepalive соединению. После того, как сделано максимальное число запросов, соединение закрывается.

Затем нужно создать файл конфигурации для взаимодействие с кластером APB  etc/nginx/conf.d/apb-cluster.conf

 /etc/nginx/conf.d/apb-cluster.conf
map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream apb-cluster {
    ## load balancing method
    hash $remote_addr;

    ## list of APB nodes
    server 100.123.0.3:8090;
    server 100.123.0.7:8090;
    # ...

    keepalive_requests 1000;
    keepalive_timeout 70s;
    reset_timedout_connection on;
}

server {
    listen 8090;
    listen [::]:8090;

    location /apb/ {
        proxy_set_header Host $host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Port $remote_port;

        proxy_http_version 1.1;

        proxy_send_timeout 120s;
        proxy_read_timeout 120s;

        proxy_pass http://apb-cluster;
    }

    location / {
        deny all;
    }
}

И выполнить перезапуск Nginx.

Важно!

Проксирующий веб-сервер Nginx не требует рестарта после внесения изменений в конфигурацию.

Обновление конфигурации можно выполнить командой: sudo systemctl reload nginx

Настройка серверов для работы с резервированием по протоколу VRRP

Настройка резервирования с использованием keepalived рассмотрено в v1.18_keepalived_1.4.2. Настройки Nginx и ActiveMQ выполняются идентично на обоих сервера. В качестве адреса сервера APB на ТД указывается VRRP адрес серверов. Так же он используется в конфигурации нод APB как адрес брокера сообщений ActiveMQ.

Достоинства и недостатки мультикастового и юникастового режима кластера APB

Режим работыMulticastUnicast
Достоинства

возможность простого добавления дополнительных нод - требуется сконфигурировать одну ноду и включить её,

обнаружение происходит в результате отправки анонсов на одинаковый мультикастовый адрес с остальными нодами.

не требуется анализировать настройки мультикастового взаимодействия между нодами



более простая настройка (выделение адреса и настройка обычного роутинга при необходимости)

в схеме включения L3 (ноды находятся в разных подсетях и доступны по роутингу)

Недостатки

в схеме включения L2 (ноды находятся в рамках одной подсети) требуется разрешить прохождение

мультикастового трафика, если это запрещено настройками или политикой безопасности

при добавлении новой ноды потребуется внести информацию о её адресе в конфигурацию

ВСЕХ нод apb и выполнить рестарт сервиса APB на ВСЕХ нодах.


в схеме включения L3 (ноды находятся в разных подсетях и доступны по роутингу) подребуется выполнить

настройку мультикастовой маршрутизации и вероятно вписать её в схему уже существующих настроек


Таблица 2.

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

Мониторинг состояния кластера APB

Для просмотра состояния кластера надо зайти на ноду по её адресу http://<IP-адрес ноды>:8090/apb/cluster.jsp

В мультикастовом режиме отобразиться состояниекак на рис. 3:

Рис. 3.


Как видно на приведенном рисунке отображается режим работы кластера и ноды, состоящие в кластере. Так пометкой "self" указана нода, на которой выполняется просмотр данной информации.

В случае выхода из строя одной из нод. на других она будет отображаться красным цветом и будет показано время, когда она последний раз была доступна (рис. 4).

Рис. 4.


Вывод для режима юникаст отличается только в части режима работы (Рис. 5 и 6):

Рис. 5.


Рис. 6.

Если нода ни разу не вставала в работу - то она не будет отображаться в кластере. Отображаются только ноды хоть раз корректно вставшие в работу, в следствии чего аварийное состояние отображается только для таких нод.

Если произошел отказ ноды - то ТД будут направлены на находящиеся в работе ноды. После восстановления отказавшей ноды ТД на ней будут появляться по мере попыток переподключения к серверу APB.

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

Для мониторинга состояния ActiveMQ можно зайти на его веб интерфейс по адресу http://<IP-адрес сервера>:8161/, дефолтные логин/пароль admin/admin. Далее перейдя по ссылке "Manage ActiveMQ broker" можно посмотреть состояние службы ActiveMQ. Более подробно о возможностях мониторинга и настройки можно ознакомиться по ссылке на официальном сайте: http://activemq.apache.org/components/classic/documentation.

Варианты отказов

Отказ одной из нод APB

Рис. 7.

На рис. 7 приведена ситуация отказа одной из нод APB (node-1). В этом случае Nginx определяет, что нода APB стала недоступна и перестает выполнять проксирование подключений на неё. ТД, подключенные к отказавшей ноде после обнаружения её недоступности попробуют снова выполнить подключение к сервису APB. Nginx выполнит проксирование этих подключений к оставшимся нодам APB.

Отказ сервера с Nginx и ActiveMQ

Рис. 8.

На рис. 8 приведена ситуация отказа сервера VM1, на котором работает Nginx и ActiveMQ. В этом случае VIP адрес переедет на сервер VM2, на котором ранее аналогичным образом был настроен сервер Nginx и ActiveMQ. ТД обнаружив потерю подключения к сервису APB будут пытаться выполнить повторное подключение. Nginx выполнит распределение этих запросов по нодам APB.


  • Нет меток