8.10. Load Balancer
概述
负载均衡器 模式允许您使用各种不同负载平衡策略将消息处理委派给多个端点之一。
Java DSL 示例
以下路由利用轮询负载平衡策略在目标端点和模拟 :x
、模拟:y
、模拟: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 负载均衡器支持以下负载均衡策略:
轮循
轮循负载平衡策略循环遍历所有目标端点,并将每个传入的消息发送到循环中的下一个端点。例如,如果目标端点列表为 模拟:x
、模拟、模拟
、模拟:z
,则传入的消息将发送到以下端点序列: 模拟:x
、模拟
、模拟
、模拟、模拟
、模拟
、模拟、模拟: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>
sticky
粘性负载均衡策略将 In 消息定向到通过从指定表达式计算哈希值来选择的端点。此负载均衡策略的优点在于,相同值的表达式始终发送到同一服务器。例如,通过计算包含用户名的标头的 hash 值,可确保特定用户的消息始终发送到同一目标端点。另一个有用的方法是指定从传入消息中提取会话 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>
当您将 sticky 选项添加到故障切换负载均衡器时,负载均衡器将从上次已知良好的端点启动。
Topic
主题负载平衡策略会将每个 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>
故障切换
自 Apache Camel 2.0 起,当 Exchange 失败时,故障转移
负载均衡器可以在处理过程中 出现异常
时尝试下一个处理器。您可以使用触发 故障切换
的特定例外列表配置故障切换。如果您没有指定任何例外,则异常会触发故障切换。故障转移负载均衡器使用与 onException
异常相同的策略来匹配异常。
如果使用 streaming,您应该在使用 故障切换负载均衡器时启用流缓存。这是必要的,以便在失败时重新读取流。
故障转移
负载均衡器支持以下选项:
选项 | 类型 | 默认值 | 描述 |
|
|
|
Camel 2.3: 指定是否使用路由中配置的
例如, |
|
|
|
Camel 2.3: 指定切换到新端点的最大尝试次数。值 |
|
|
|
Camel 2.3: 指定 |
只有在引发 IOException
异常时,以下示例才会切换到 fail。
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>
以下示例演示了如何以 round robin 模式进行故障切换:
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
。通过禁用 Error Handler,您可以确保它不会干预。这样,故障转移负载均衡器可以尽快处理故障切换。如果您还要启用 roundRobin
模式,则会一直重试,直到成功为止。然后,您可以将 maximumFailoverAttempts
选项配置为高值,以便其最终耗尽和失败。
加权轮循,权重为随机
在许多企业环境中,如果无数处理能力的服务器节点正在托管服务,通常最好根据各个服务器处理容量来分发负载。可以使用 加权轮循 算法或 加权随机 算法来解决这个问题。
通过加权负载平衡策略,您可以为其他每台服务器指定处理负载 分布比。您可以将这个值指定为每台服务器的正处理权重。大于数字表示服务器可以处理更大的负载。处理 weight 用于确定与其他人相关的每个处理端点的有效负载分发率。
下表中描述了可以使用的参数:
选项 | 类型 | 默认值 | 描述 |
---|---|---|---|
|
|
|
round-robin 的默认值为 |
|
|
|
|
以下 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 中,使用 <custom>,它只在 Camel 2.8 起提供。在旧的版本中,您必须按如下操作:
<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; } }
断路器
Circuit Breaker 负载均衡器是一个有状态模式,用于监控特定异常的所有调用。最初,断路器处于关闭状态并传递所有消息。如果失败并且达到阈值,则会进入 open 状态并拒绝所有调用,直到达到 一半OpenAfter
超时。在超时后,如果存在新调用,则 Circuit Breaker 将传递所有消息。如果结果成功,则 Circuit Breaker 将变为闭路状态(如果不是),它会重新变为开状态。
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>