Статьи

Балансировка нагрузки с помощью Apache Camel

В этом примере мы покажем вам, как использовать Apache Camel в качестве балансировщика нагрузки для вашей системы. В компьютерном мире балансировщик нагрузки — это устройство, которое действует как обратный прокси-сервер и распределяет сетевой трафик или трафик приложений на несколько серверов. Балансировщики нагрузки используются для увеличения емкости (одновременных пользователей) и надежности приложений. С помощью Camel мы можем быстро создать собственный программный балансировщик нагрузки. Наслаждайтесь ездой!

Балансировка нагрузки не является отдельным шаблоном в книге Enterprise Integration Patterns (как сказал Клаус Ибсен, это было бы, если бы было второе издание книги), но Camel рассматривает его как другой EIP. Несмотря на то, что синтаксис Camel делает балансировку нагрузки обманчиво простой, это все же сложная тема, и вам следует потратить некоторое время на планирование подходящей стратегии на этапе проектирования. Camel поставляется с рядом встроенных политик балансировки нагрузки. В этом примере мы рассмотрим некоторые из них, которые используются чаще.

Коды для этой статьи используют Maven 3.3.9, Eclipse Mars 4.5.0 и Apache Camel 2.17.1. Обратите внимание, что API балансировки нагрузки немного изменился с версии 2.15, если вы собираетесь использовать более ранние версии Camel, обратитесь к документации. Все классы в этом примере исходного кода используют аннотированный метод @Test, поэтому вы должны запускать их как тесты JUnit и надеяться увидеть зеленую полосу.

1. Создание базового проекта

Прежде чем мы перейдем к написанию реальных кодов, давайте сначала создадим проект Maven в Eclipse. Сначала выберите Файл -> Новый … -> Новый проект. Введите Maven и выберите проект Maven.

Создать новый проект Maven

Создать новый проект Maven

В следующем окне установите флажок «Создать простой проект» и нажмите «Далее»:

Создать простой проект Maven

Создать простой проект Maven

Наконец добавьте следующую конфигурацию и нажмите Готово:

Настроить Maven проект

Настроить Maven проект

Теперь отредактируйте файл pom.xml, чтобы он выглядел следующим образом:

pom.xml

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<modelVersion>4.0.0</modelVersion>
<groupId>com.javacodegeeks</groupId>
<artifactId>camelLoadBalancer</artifactId>
<version>1.0.0-SNAPSHOT</version>
 
    <dependencies>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-core</artifactId>
            <version>2.17.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-test</artifactId>
            <version>2.17.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>

2. Балансировка нагрузки

Мы упомянули разные политики, которые поддерживает Camel, но что это за политики? Политика определяет, как рабочая нагрузка распределяется между различными получателями (процессор, потребительский сервис …). В этой статье мы показываем примеры случайных, циклических и их взвешенных аналогов и тематических политик. В конце мы покажем вам, как сделать свою собственную политику.

2.1. случайный

Простейшая форма политики балансировки нагрузки — случайная. Как следует из названия, вы просто определяете конечные точки, которые должны обрабатывать нагрузку, а Camel решает, какой из них использовать случайным образом. Вот код, который реализует случайную политику. m1, m2 и m3 — конечные точки, которые обрабатывают сообщение. Конец всегда получает сообщение, но каждый раз, когда вы запускаете класс, используется одна из вышеупомянутых конечных точек. Этот выбор является абсолютно случайным, и вы можете проверить эту случайность, добавив свои собственные утверждения.

RandomLoadBalance.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.jcg;
 
import org.apache.camel.EndpointInject;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
 
public class RandomLoadBalance extends CamelTestSupport{
    @EndpointInject(uri="mock:m1")
    MockEndpoint m1;
    @EndpointInject(uri="mock:m2")
    MockEndpoint m2;
    @EndpointInject(uri="mock:m3")
    MockEndpoint m3;
    @EndpointInject(uri="mock:end")
    MockEndpoint end;
 
    @Override
    protected RouteBuilder createRouteBuilder() throws Exception{
        return new RouteBuilder(){
            @Override
            public void configure() throws Exception {
                from("direct:start").loadBalance().random().to(m1,m2,m3).end().to(end);
            }
        };
    }
     
    @Test
    public void testSending() throws Exception{
        end.expectedMessageCount(1);
        template.sendBody("direct:start", "");
        end.assertIsSatisfied();
    }
}

2.2. По-круговой

Round Robin — еще один простой тип балансировки нагрузки. В этой политике конечные точки используются по очереди один за другим. В примере кода вы видите, что сообщение проходит через m1, m2, m3 и снова m1.

RoundRobinLoadBalance.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.jcg;
 
import javax.naming.Context;
import org.apache.camel.EndpointInject;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.dataset.SimpleDataSet;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
 
public class RoundRobinLoadBalance extends CamelTestSupport{
     
    @EndpointInject(uri="mock:m1")
    MockEndpoint m1;
    @EndpointInject(uri="mock:m2")
    MockEndpoint m2;
    @EndpointInject(uri="mock:m3")
    MockEndpoint m3;
    @EndpointInject(uri="mock:end")
    MockEndpoint end;
     
    @Override
    protected RouteBuilder createRouteBuilder() throws Exception{
        return new RouteBuilder(){
            @Override
            public void configure() throws Exception {
                from("dataset:start").loadBalance().roundRobin().to(m1,m2,m3).end().to(end);
            }
        };
    }
     
    @Override
    protected Context createJndiContext() throws Exception{
        SimpleDataSet sds = new SimpleDataSet();
        sds.setSize(4);
        Context context = super.createJndiContext();
        context.bind("start", sds);
        return context;
    }
     
    @Test
    public void testSending() throws Exception{
        m1.expectedMessageCount(2);
        m2.expectedMessageCount(1);
        m3.expectedMessageCount(1);
        end.expectedMessageCount(4);
        template.sendBody("dataset:start", "");
        m1.assertIsSatisfied();
        m2.assertIsSatisfied();
        m3.assertIsSatisfied();
        end.assertIsSatisfied();
    }
}

2,3. Взвешенная круговая малиновка

В реальном мире редко случается, что у вас есть идентичные машины, обслуживающие ваши запросы. Поэтому имеет смысл иметь одну более мощную машину, которая выполняет больше работы, чем другие. Взвешенная круговая схема и взвешенная случайная величина являются более сложными аналогами круговой схемы и случайной политики соответственно. С помощью взвешенного кругового (или случайного) управления вы можете контролировать балансировку нагрузки детальным образом. Здесь мы приведем пример взвешенного круглого робина. Взвешенная случайная величина идентична.

Весовой метод имеет два аргумента. Первым является логическое значение, которое определяет, является ли политика циклическим изменением (true) или случайным (false). Второй аргумент — это строка, которая определяет коэффициент распределения соответствующих конечных точек. Здесь «2,1» означает, что m1 получает вдвое больше трафика, чем m2. В более ранней версии Camel вы должны были использовать список целых чисел.

WeightedRoundRobinLoadBalance.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.jcg;
 
import javax.naming.Context;
import org.apache.camel.EndpointInject;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.dataset.SimpleDataSet;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
 
public class WeightedRoundRobinLoadBalance extends CamelTestSupport{
     
    @EndpointInject(uri="mock:m1")
    MockEndpoint m1;
    @EndpointInject(uri="mock:m2")
    MockEndpoint m2;
    @EndpointInject(uri="mock:end")
    MockEndpoint end;
     
    @Override
    protected RouteBuilder createRouteBuilder() throws Exception{
        return new RouteBuilder(){
            @Override
            public void configure() throws Exception {
                // first argument of weighted method is a boolean defines if the policy is
                // Round robin (true) or Random (false)
                from("dataset:start").loadBalance().weighted(true,"2,1").to(m1,m2).end().to(end);
            }
        };
    }
     
    @Override
    protected Context createJndiContext() throws Exception{
        SimpleDataSet sds = new SimpleDataSet();
        sds.setSize(6);
        Context context = super.createJndiContext();
        context.bind("start", sds);
        return context;
    }
     
    @Test
    public void testSending() throws Exception{
        m1.expectedMessageCount(4);
        m2.expectedMessageCount(2);
        end.expectedMessageCount(6);
        template.sendBody("dataset:start", "");
        m1.assertIsSatisfied();
        m2.assertIsSatisfied();
        end.assertIsSatisfied();
    }
}

2,4. Тема

Тема принципиально отличается от других политик, поскольку все конечные точки получают сообщение. В нашем примере код m1, m2 и m3 обрабатывает 5 сообщений, и конечная конечная точка тоже получает 5 сообщений. Тема может быть полезна для защиты от сбоев конечной точки.

TopicLoadBalance.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.jcg;
 
import javax.naming.Context;
import org.apache.camel.EndpointInject;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.dataset.SimpleDataSet;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
 
public class TopicLoadBalance extends CamelTestSupport{
     
    @EndpointInject(uri="mock:m1")
    MockEndpoint m1;
    @EndpointInject(uri="mock:m2")
    MockEndpoint m2;
    @EndpointInject(uri="mock:m3")
    MockEndpoint m3;
    @EndpointInject(uri="mock:end")
    MockEndpoint end;
     
    @Override
    protected RouteBuilder createRouteBuilder() throws Exception{
        return new RouteBuilder(){
            @Override
            public void configure() throws Exception {
                from("dataset:start").loadBalance().topic().to(m1,m2,m3).end().to(end);
            }
        };
    }
     
    @Override
    protected Context createJndiContext() throws Exception{
        SimpleDataSet sds = new SimpleDataSet();
        sds.setSize(5);
        Context context = super.createJndiContext();
        context.bind("start", sds);
        return context;
    }
     
    @Test
    public void testSending() throws Exception{
        m1.expectedMessageCount(5);
        m2.expectedMessageCount(5);
        m3.expectedMessageCount(5);
        end.expectedMessageCount(5);
        template.sendBody("dataset:start", "");
        m1.assertIsSatisfied();
        m2.assertIsSatisfied();
        m3.assertIsSatisfied();
        end.assertIsSatisfied();
    }
}

2.5. Пользовательский балансировщик нагрузки

Верблюд предлагает много полезных политик, но всегда есть моменты, когда ни один из них не отвечает вашим потребностям. В таких ситуациях вы должны определить свою собственную стратегию балансировки нагрузки, и тем не менее Camel не оставляет вас в покое. Пользовательский балансировщик нагрузки позволяет легко определить собственную политику. В примере кода мы определяем балансировщик нагрузки, который проверяет поле заголовка сообщения «sessionID». Если оно четное, оно отправляет его в m1, если нечетное в m2. Конечно, это нереалистичный пример, но мы сознательно упростили процесс, чтобы сосредоточиться на реализации балансировки нагрузки, свободной от беспорядка бизнес-логики. Сначала мы создаем класс, который расширяет класс LoadBalancerSupport и переопределяет метод процесса. Затем мы передаем экземпляр этого класса в метод loadBalance.

SessionChecker.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.jcg;
 
import org.apache.camel.AsyncCallback;
import org.apache.camel.Exchange;
import org.apache.camel.processor.loadbalancer.LoadBalancerSupport;
 
public class SessionChecker extends LoadBalancerSupport{
 
    @Override
    public boolean process(Exchange exchange, AsyncCallback callback) {
         
        int id = exchange.getIn().getHeader("sessionID", Integer.class);
        try{
            if(id%2 == 0){
                getProcessors().get(0).process(exchange);
            } else{
                getProcessors().get(1).process(exchange);
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        callback.done(true);
        return true;
    }
}

CustomLoadBalance.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.jcg;
 
import java.util.HashMap;
import java.util.Map;
import javax.naming.Context;
import org.apache.camel.EndpointInject;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.dataset.SimpleDataSet;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
 
public class CustomLoadBalance extends CamelTestSupport{
     
    @EndpointInject(uri="mock:m1")
    MockEndpoint m1;
    @EndpointInject(uri="mock:m2")
    MockEndpoint m2;
    @EndpointInject(uri="mock:end")
    MockEndpoint end;
     
    @Override
    protected RouteBuilder createRouteBuilder() throws Exception{
        return new RouteBuilder(){
            @Override
            public void configure() throws Exception {
                from("dataset:start").loadBalance(new SessionChecker()).to(m1,m2).end().to(end);
            }
        };
    }
     
    @Override
    protected Context createJndiContext() throws Exception{
        SimpleDataSet sds = new SimpleDataSet();
        Map<String, Object> headers = new HashMap<>();
        headers.put("sessionID", 1);
        sds.setDefaultHeaders(headers);
        sds.setSize(2);
        Context context = super.createJndiContext();
        context.bind("start", sds);
        return context;
    }
     
    @Test
    public void testSending() throws Exception{
        m1.expectedMessageCount(0);
        m2.expectedMessageCount(2);
        end.expectedMessageCount(2);
        template.sendBody("dataset:start", "");
        m1.assertIsSatisfied();
        m2.assertIsSatisfied();
        end.assertIsSatisfied();
    }
}

3. Вывод

Все еще есть больше в балансировке нагрузки Camel. Мы не охватывали такие политики, как Failover, Sticky и Circuit Breaker. На самом деле верблюд выходит за рамки этого. Вы можете использовать балансировку нагрузки во многих других ситуациях, таких как HTTP-прокси между клиентом и сервером.

4. Скачать проект Eclipse

Это был пример различных политик балансировки нагрузки Camel.

Скачать
Вы можете скачать полный исходный код этого примера здесь: Распределитель нагрузки Camel