Ribbonの基本概念と実装原理

Ribbonは、Netflixが開発したオープンソースのロードバランサーコンポーネントで、サービス消費者側にロードバランサーのロジックを組み込むことで、サーバーへのリクエストを分散します。

Ribbonはデフォルトで7つのロードバランシング戦略を提供しています:

  • BestAvailableRule:最も少ないリクエストを受けているサーバーを選択
  • ClientConfigEnabledRoundRobinRule:ラウンドロビン方式
  • RandomRule:ランダムにサーバーを選択
  • RoundRobinRule:ラウンドロビン方式
  • RetryRule:ラウンドロビン方式でリトライ
  • WeightedResponseTimeRule:応答時間が短いほど選択される確率が高い
  • ZoneAvoidanceRule:ゾーンと可用性に基づいてラウンドロビン

Ribbonの実装原理

@LoadBalancedアノテーション

Ribbonを使用するには、まず依存関係を追加します。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>

次に、RestTemplateインスタンスをインジェクトする際に@LoadBalancedアノテーションを付与します。

@Autowired
@LoadBalanced
private RestTemplate restTemplate;

@LoadBalancedアノテーション自体にはロジックは含まれていませんが、LoadBalancerAutoConfigurationクラスで処理されます。

@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();

@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
        final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
    return () -> restTemplateCustomizers.ifAvailable(customizers -> {
        for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
            for (RestTemplateCustomizer customizer : customizers) {
                customizer.customize(restTemplate);
            }
        }
    });
}

このコードでは、@LoadBalancedアノテーションが付けられたRestTemplateインスタンスを取得し、RestTemplateCustomizerを使ってカスタマイズを行います。

LoadBalancerInterceptor

LoadBalancerInterceptorクラスは、RestTemplateのリクエストをインターセプトしてロードバランシングを行います。

private LoadBalancerClient loadBalancer;

@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
                                    final ClientHttpRequestExecution execution) throws IOException {
    final URI originalUri = request.getURI();
    String serviceName = originalUri.getHost();
    Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
    return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
}

LoadBalancerClientの実装クラスであるRibbonLoadBalancerClientのexecuteメソッドが呼び出されます。

@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
    ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
    Server server = getServer(loadBalancer);
    if (server == null) {
        throw new IllegalStateException("No instances available for " + serviceId);
    }
    RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server));
    return execute(serviceId, ribbonServer, request);
}

ILoadBalancer

ILoadBalancerインターフェースは、ロードバランシングに関する操作を定義しています。

public interface ILoadBalancer {
    void addServers(List<Server> newServers);
    Server chooseServer(Object key);
    void markServerDown(Server server);
    List<Server> getReachableServers();
    List<Server> getAllServers();
}

DynamicServerListLoadBalancerクラスは、ILoadBalancerの実装クラスで、Eurekaからサービスリストを取得し、定期的にサーバーの状態をチェックします。

public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
                                     ServerList<T> serverList, ServerListFilter<T> filter,
                                     ServerListUpdater serverListUpdater) {
    super(clientConfig, rule, ping);
    this.serverListImpl = serverList;
    this.filter = filter;
    this.serverListUpdater = serverListUpdater;
    if (filter instanceof AbstractServerListFilter) {
        ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
    }
    restOfInit(clientConfig);
}

サーバーリストが取得されると、IRuleインターフェースの実装クラスによって適切なサーバーを選択します。

public interface IRule {
    Server choose(Object key);
    void setLoadBalancer(ILoadBalancer lb);
    ILoadBalancer getLoadBalancer();
}

ロードバランサーコンポーネントの比較

ロードバランサーはシステムの可用性を向上させる重要なコンポーネントです。主に、独立型とクライアント統合型の2つの実装方式があります。

  • 独立型: クライアントがロードバランサーを介してリクエストを送信します。ロードバランサーがリクエストを適切なサーバーにルーティングします。
  • クライアント統合型: クライアント自身がロードバランサーのロジックを持ち、ローカルでサーバーリストを維持します。

Ribbonはクライアント統合型のロードバランサーですが、nginxやSLBなども独立型の代表的なロードバランサーです。

タグ: SpringCloud Ribbon ロードバランサー Java SpringBoot

7月6日 20:18 投稿