В этом примере мы покажем вам, как использовать 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.
В следующем окне установите флажок «Создать простой проект» и нажмите «Далее»:
Наконец добавьте следующую конфигурацию и нажмите Готово:
Теперь отредактируйте файл 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
|
< project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > < modelVersion >4.0.0</ modelVersion > < groupId >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.