8.10. Load Balancer

概要

Load Balancer パターンにより、さまざまな負荷分散ポリシーを使用して、複数のエンドポイントのうちの 1 つにメッセージ処理を委譲することができます。

Java DSL の例

以下のルートは、ラウンドロビン負荷分散ポリシーを使用して、ターゲットエンドポイント (mock:xmock:ymock:z) 間で受信メッセージを分散します。

from("direct:start").loadBalance().roundRobin().to("mock:x", "mock:y", "mock:z");

XML 設定の例

以下の例は、XML で同じルートを設定する方法を示しています。

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <loadBalance>
        <roundRobin/>
        <to uri="mock:x"/>
        <to uri="mock:y"/>
        <to uri="mock:z"/>
    </loadBalance>
  </route>
</camelContext>

負荷分散ポリシー

Apache Camel ロードバランサーは、以下の負荷分散ポリシーをサポートしています。

ラウンドロビン

ラウンドロビン負荷分散ポリシーは、すべてのターゲットエンドポイントを循環し、各受信メッセージをサイクル内の次のエンドポイントに送信します。たとえば、ターゲットエンドポイントのリストが mock:xmock:ymock:z である場合、受信メッセージは mock:xmock:ymock:zmock:xmock:ymock:z のような順番でエンドポイントに送信されます。

以下のように、Java DSL では ラウンドロビン負荷分散ポリシーを指定することができます。

from("direct:start").loadBalance().roundRobin().to("mock:x", "mock:y", "mock:z");

または、以下のように XML で同じルートを定義することもできます。

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <loadBalance>
        <roundRobin/>
        <to uri="mock:x"/>
        <to uri="mock:y"/>
        <to uri="mock:z"/>
    </loadBalance>
  </route>
</camelContext>

ランダム

ランダム負荷分散ポリシーは、指定されたリストからターゲットエンドポイントを無作為に選択します。

以下のように、Java DSL ではランダム負荷分散ポリシーを指定することができます。

from("direct:start").loadBalance().random().to("mock:x", "mock:y", "mock:z");

または、以下のように XML で同じルートを定義することもできます。

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <loadBalance>
        <random/>
        <to uri="mock:x"/>
        <to uri="mock:y"/>
        <to uri="mock:z"/>
    </loadBalance>
  </route>
</camelContext>

スティッキー

スティッキー負荷分散ポリシーは、指定された式からハッシュ値を計算して選択されたエンドポイントに、In メッセージを送信します。この負荷分散ポリシーの利点は、同じ値の式であれば、常に同じサーバーに送信されることです。たとえば、ユーザー名が含まれるヘッダーのハッシュ値を計算することで、特定のユーザーのメッセージが同じターゲットエンドポイントへ常に送信されるようになります。もう 1 つの便利な方法は、受信メッセージからセッション ID を抽出する式を指定することです。これにより、同じセッションに属するすべてのメッセージが同じターゲットエンドポイントに送信されるようになります。

以下のように、Java DSL ではスティッキー負荷分散ポリシーを指定することができます。

from("direct:start").loadBalance().sticky(header("username")).to("mock:x", "mock:y", "mock:z");

または、以下のように XML で同じルートを定義することもできます。

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <loadBalance>
      <sticky>
        <correlationExpression>
          <simple>header.username</simple>
        </correlationExpression>
      </sticky>
      <to uri="mock:x"/>
      <to uri="mock:y"/>
      <to uri="mock:z"/>
    </loadBalance>
  </route>
</camelContext>
注記

スティッキーオプションをフェイルオーバーロードバランサーに追加すると、ロードバランサーは最後に認知した、正常なエンドポイントから開始します。

トピック

トピック負荷分散ポリシーは、各 In メッセージのコピーをリストされた すべて の宛先エンドポイントに送信します (JMS トピックのように、すべての宛先にメッセージを効果的にブロードキャストします)。

Java DSL を使用して、以下のようにトピック負荷分散ポリシーを指定することができます。

from("direct:start").loadBalance().topic().to("mock:x", "mock:y", "mock:z");

または、以下のように XML で同じルートを定義することもできます。

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="direct:start"/>
    <loadBalance>
        <topic/>
        <to uri="mock:x"/>
        <to uri="mock:y"/>
        <to uri="mock:z"/>
    </loadBalance>
  </route>
</camelContext>

Failover

Apache Camel 2.0 で利用可能 failover ロードバランサーは、エクスチェンジの処理中に exception で失敗した場合に、次のプロセッサーを試すことができます。フェイルオーバーをトリガーする特定の例外のリストを使い、failover を設定することができます。例外を指定しない場合、フェイルオーバーはいずれの例外でもトリガーされます。フェイルオーバーロードバランサーは、onException 例外句と同じストラテジーを使い、例外のマッチングを行います。

ストリーム使用時にストリームキャッシュを有効にする

ストリーミングを使用する場合は、フェイルオーバーロードバランサーの使用時に、Stream Caching を有効にする必要があります。これは、フェイルオーバー時にストリームを再読み取りできるようにするために必要です。

failover ロードバランサーは以下のオプションをサポートします。

オプション

タイプ

デフォルト

説明

inheritErrorHandler

boolean

true

Camel 2.3: ルートに設定された errorHandler を使用するかどうかを指定します。次のエンドポイントへ即座にフェイルオーバーする場合は、このオプションを無効にする必要があります (false の値)。このオプションを有効にすると、Apache Camel は最初に errorHandler を使用してメッセージの処理を試みます。

たとえば、errorHandler はメッセージを再送し、試行間の遅延を使用するように設定されている可能性があります。Apache Camel は、最初に オリジナル のエンドポイントに再配信を試み、errorHandler を使い切った場合にのみ次のエンドポイントへフェイルオーバーします。

maximumFailoverAttempts

int

-1

Camel 2.3: 新しいエンドポイントへのフェイルオーバーの最大試行回数を指定します。値 0 は、フェイルオーバーされないことを意味し、値 -1 は、無制限にフェイルオーバーが試行されることを意味します。

roundRobin

boolean

false

Camel 2.3: failover ロードバランサーがラウンドロビンモードで動作するかどうかを指定します。false の場合、新規メッセージが処理される際には 常に 最初のエンドポイントから開始されます。つまり、すべてのメッセージは、それぞれ先頭にリセットされます。ラウンドロビンが有効になっている場合は、状態を保持し、ラウンドロビン方式で次のエンドポイントへと進みます。ラウンドロビンを使用する場合、最後に認識された適切なエンドポイントに 固定 されず、常に次のエンドポイントを選択して使用します。

以下の例では、IOException 例外が出力された場合にのみフェイルオーバーするように設定されています。

from("direct:start")
    // here we will load balance if IOException was thrown
    // any other kind of exception will result in the Exchange as failed
    // to failover over any kind of exception we can just omit the exception
    // in the failOver DSL
    .loadBalance().failover(IOException.class)
        .to("direct:x", "direct:y", "direct:z");

オプションで以下に示すように、フェイルオーバーする例外を複数指定することができます。

// enable redelivery so failover can react
errorHandler(defaultErrorHandler().maximumRedeliveries(5));

from("direct:foo")
    .loadBalance()
    .failover(IOException.class, MyOtherException.class)
    .to("direct:a", "direct:b");

XML で以下のように同じルートを設定できます。

<route errorHandlerRef="myErrorHandler">
    <from uri="direct:foo"/>
    <loadBalance>
        <failover>
            <exception>java.io.IOException</exception>
            <exception>com.mycompany.MyOtherException</exception>
        </failover>
        <to uri="direct:a"/>
        <to uri="direct:b"/>
    </loadBalance>
</route>

以下の例は、ラウンドロビンモードでフェイルオーバーする方法を示しています。

from("direct:start")
    // Use failover load balancer in stateful round robin mode,
    // which means it will fail over immediately in case of an exception
    // as it does NOT inherit error handler. It will also keep retrying, as
    // it is configured to retry indefinitely.
    .loadBalance().failover(-1, false, true)
    .to("direct:bad", "direct:bad2", "direct:good", "direct:good2");

XML で以下のように同じルートを設定できます。

<route>
    <from uri="direct:start"/>
    <loadBalance>
        <!-- failover using stateful round robin,
        which will keep retrying the 4 endpoints indefinitely.
        You can set the maximumFailoverAttempt to break out after X attempts -->
        <failover roundRobin="true"/>
        <to uri="direct:bad"/>
        <to uri="direct:bad2"/>
        <to uri="direct:good"/>
        <to uri="direct:good2"/>
    </loadBalance>
</route>

できるだけ早く次のエンドポイントにフェイルオーバーする場合は、inheritErrorHandler=false を設定することで inheritErrorHandler を無効にすることができます。エラーハンドラーを無効にすることで、エラーハンドラーが介入しないようにすることができます。これにより、フェイルオーバーロードバランサーはすぐにフェイルオーバー処理をできるようになります。roundRobin モードも有効にすると、成功するまでリトライが行われます。maximumFailoverAttempts オプションを高い値に設定すると、最終的にしきい値を越え、失敗することができます。

重み付きラウンドロビンおよび重み付きランダム

多くのエンタープライズ環境では、処理能力が不均等なサーバーノードがサービスをホストしており、通常は個々のサーバー処理能力に応じて負荷を分散することが望ましくなります。この問題に対処するために、 重み付きラウンドロビン アルゴリズム、または 重み付きランダム アルゴリズムを使用することができます。

重み付けされた負荷分散ポリシーを使用すると、サーバーごとに負荷 分散率 を指定できます。この値を、各サーバーごとに正の処理の重みとして指定することができます。数値が大きいほど、サーバーがより大きな負荷を処理できることを示します。処理の重みを使用して、各処理エンドポイントに対するペイロードの分散比率を決定します。

使用可能なパラメーターは、以下の表に記載されています。

表8.3 重み付けオプション

オプションタイプデフォルト説明

roundRobin

boolean

false

ラウンドロビンのデフォルト値は false です。この設定またはパラメーターがない場合、使用される負荷分散アルゴリズムはランダムです。

distributionRatioDelimiter

String

,

distributionRatioDelimiterdistributionRatio を指定するために使用される区切り文字です。この属性が指定されていない場合、コンマ , がデフォルトの区切り文字になります。

以下の Java DSL の例では、重み付けされたラウンドロビンのルートと、重み付けされたランダムのルートを定義する方法を示しています。

// Java
// round-robin
from("direct:start")
  .loadBalance().weighted(true, "4:2:1" distributionRatioDelimiter=":")
  .to("mock:x", "mock:y", "mock:z");

//random
from("direct:start")
  .loadBalance().weighted(false, "4,2,1")
  .to("mock:x", "mock:y", "mock:z");

XML で以下のようにラウンドロビンのルートを設定できます。

<!-- round-robin -->
<route>
  <from uri="direct:start"/>
  <loadBalance>
    <weighted roundRobin="true" distributionRatio="4:2:1" distributionRatioDelimiter=":" />
    <to uri="mock:x"/>
    <to uri="mock:y"/>
    <to uri="mock:z"/>
  </loadBalance>
</route>

カスタムロードバランサー

カスタムロードバランサー (独自の実装など) も使用することができます。

Java DSL を使用した例:

from("direct:start")
     // using our custom load balancer
     .loadBalance(new MyLoadBalancer())
     .to("mock:x", "mock:y", "mock:z");

XML DSL を使用した場合の同じ例:

<!-- this is the implementation of our custom load balancer -->
 <bean id="myBalancer" class="org.apache.camel.processor.CustomLoadBalanceTest$MyLoadBalancer"/>

 <camelContext xmlns="http://camel.apache.org/schema/spring">
   <route>
     <from uri="direct:start"/>
     <loadBalance>
       <!-- refer to my custom load balancer -->
       <custom ref="myBalancer"/>
       <!-- these are the endpoints to balancer -->
       <to uri="mock:x"/>
       <to uri="mock:y"/>
       <to uri="mock:z"/>
     </loadBalance>
   </route>
 </camelContext>

上記の XML DSL では、Camel 2.8 以降でのみ利用できる <custom> を使用していることに注意してください。以前のリリースでは、代わりに以下のようにする必要がありました。

       <loadBalance ref="myBalancer">
         <!-- these are the endpoints to balancer -->
         <to uri="mock:x"/>
         <to uri="mock:y"/>
         <to uri="mock:z"/>
       </loadBalance>

カスタムロードバランサーを実装するには、LoadBalancerSupport および SimpleLoadBalancerSupport などの一部のサポートクラスを拡張することができます。前者は非同期ルーティングエンジンに対応し、後者は対応していません。以下に例を示します。

public static class MyLoadBalancer extends LoadBalancerSupport {

     public boolean process(Exchange exchange, AsyncCallback callback) {
         String body = exchange.getIn().getBody(String.class);
         try {
             if ("x".equals(body)) {
                 getProcessors().get(0).process(exchange);
             } else if ("y".equals(body)) {
                 getProcessors().get(1).process(exchange);
             } else {
                 getProcessors().get(2).process(exchange);
             }
         } catch (Throwable e) {
             exchange.setException(e);
         }
         callback.done(true);
         return true;
     }
 }

サーキットブレーカー

サーキットブレーカーロードバランサーは、特定の例外に対するすべての呼び出しを監視するために使用されるステートフルなパターンです。初期状態では、サーキットブレーカーはクローズ状態であり、すべてのメッセージを渡します。失敗があり、しきい値に達すると、オープン状態に遷移し、halfOpenAfter タイムアウトに達するまですべての呼び出しを拒否します。タイムアウト後に新しい呼び出しがあった場合、サーキットブレーカーはすべてのメッセージを渡します。結果が成功すると、サーキットブレーカーはクローズ状態に戻ります。そうでない場合は、オープン状態に戻ります。

Java DSL の例:

from("direct:start").loadBalance()
    .circuitBreaker(2, 1000L, MyCustomException.class)
    .to("mock:result");

Spring XML の例:

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <route>
    <from uri="direct:start"/>
    <loadBalance>
        <circuitBreaker threshold="2" halfOpenAfter="1000">
            <exception>MyCustomException</exception>
        </circuitBreaker>
        <to uri="mock:result"/>
    </loadBalance>
</route>
</camelContext>