1前書き


Dubbo

は、AlibabaのオープンソースRPCおよびマイクロサービスフレームワークです。

とりわけ、サービスガバナンスを強化し、従来のモノリスアプリケーションをスケーラブルな分散アーキテクチャにスムーズにリファクタリングすることを可能にします。

この記事では、Dubboとその最も重要な機能について紹介します。


2建築

Dubboはいくつかの役割を区別します。

  1. プロバイダ – サービスが公開されている場所. プロバイダが登録します

レジストリへのサービス
。コンテナ – サービスが開始、ロード、実行される場所

  1. 消費者 – リモートサービスを呼び出す人. 消費者が購読する

レジストリに必要なサービス
。レジストリ – サービスが登録され発見される場所

  1. 監視 – サービスの統計を記録します. たとえば、

与えられた時間間隔でのサービス呼び出し


(情報源:http://dubbo.io/images/dubbo-architecture.png)

プロバイダ、コンシューマ、およびレジストリ間の接続は永続的であるため、サービスプロバイダが停止したときはいつでも、レジストリが障害を検出してコンシューマに通知することができます。

レジストリとモニタはオプションです。消費者はサービスプロバイダーに直接接続できますが、システム全体の安定性に影響があります。


3 Mavenの依存関係

ダイビングする前に、

pom.xml

に次の依存関係を追加しましょう。

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.5.7</version>
</dependency>

最新版はhttps://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22com.alibaba%22%20AND%20a%3A%22dubbo%22[here]にあります。


4ブートストラップ

それでは、Dubboの基本機能を試してみましょう。

これは低侵襲のフレームワークであり、その機能の多くは外部の設定や注釈に依存しています。

それはSpringコンテナ(現在はSpring 4.3.10)に依存しているので、XML設定ファイルを使用することが正式に推奨されています。

XML設定を使用して、その機能のほとんどを説明します。


4.1. マルチキャストレジストリ – サービスプロバイダ

クイックスタートとしては、サービスプロバイダ、コンシューマ、および「見えない」レジストリだけが必要です。マルチキャストネットワークを使用しているため、レジストリは表示されません。

次の例では、プロバイダはコンシューマに「こんにちは」とだけ言っています。

public interface GreetingsService {
    String sayHi(String name);
}

public class GreetingsServiceImpl implements GreetingsService {

    @Override
    public String sayHi(String name) {
        return "hi, " + name;
    }
}

リモートプロシージャコールを実行するには、コンシューマはサービスプロバイダと共通のインタフェースを共有する必要があるため、インタフェース

GreetingsService

をコンシューマと共有する必要があります。


4.2. マルチキャストレジストリ – サービス登録

それでは、

GreetingsService

をレジストリに登録しましょう。プロバイダとコンシューマの両方が同じローカルネットワーク上にある場合、非常に便利な方法はマルチキャストレジストリを使用することです。

<dubbo:application name="demo-provider" version="1.0"/>
<dubbo:registry address="multicast://224.1.1.1:9090"/>
<dubbo:protocol name="dubbo" port="20880"/>
<bean id="greetingsService" class="com.baeldung.dubbo.remote.GreetingsServiceImpl"/>
<dubbo:service interface="com.baeldung.dubbo.remote.GreetingsService"
  ref="greetingsService"/>

上記のBean設定で、

GreetingsService



dubbo://127.0.0.1:20880

の下のURLに公開し、

<dubbo:registry/>

で指定されたマルチキャストアドレスにサービスを登録しました。

プロバイダの設定では、アプリケーションメタデータ、公開するインタフェース、およびその実装をそれぞれ

<dubbo:application/>



<dubbo:service/>

、および

<beans/>

で宣言しました。


dubbo

プロトコルはフレームワークがサポートする多くのプロトコルのうちの1つです。これは、Java NIOのノンブロッキング機能の上に構築されており、使用されるデフォルトのプロトコルです。

この記事の後半で詳しく説明します。


4.3. マルチキャストレジストリ – サービス利用者

一般に、消費者は起動するインタフェースとリモートサービスのアドレスを指定する必要があります。それが、消費者に必要なことです。

<dubbo:application name="demo-consumer" version="1.0"/>
<dubbo:registry address="multicast://224.1.1.1:9090"/>
<dubbo:reference interface="com.baeldung.dubbo.remote.GreetingsService"
  id="greetingsService"/>

これですべての設定が完了しました。次に、それらがどのように機能するかを見てみましょう。

public class MulticastRegistryTest {

    @Before
    public void initRemote() {
        ClassPathXmlApplicationContext remoteContext
          = new ClassPathXmlApplicationContext("multicast/provider-app.xml");
        remoteContext.start();
    }

    @Test
    public void givenProvider__whenConsumerSaysHi__thenGotResponse(){
        ClassPathXmlApplicationContext localContext
          = new ClassPathXmlApplicationContext("multicast/consumer-app.xml");
        localContext.start();
        GreetingsService greetingsService
          = (GreetingsService) localContext.getBean("greetingsService");
        String hiMessage = greetingsService.sayHi("baeldung");

        assertNotNull(hiMessage);
        assertEquals("hi, baeldung", hiMessage);
    }
}

プロバイダの

remoteContext

が起動すると、Dubboは自動的に

GreetingsService

をロードし、それを指定のレジストリに登録します。この場合、それはマルチキャストレジストリです。

コンシューマはマルチキャストレジストリを購読し、コンテキスト内に

GreetingsService

のプロキシを作成します。ローカルクライアントが

sayHi

メソッドを呼び出すと、透過的にリモートサービスが呼び出されます。

レジストリはオプションであり、消費者が公開ポートを介してプロバイダに直接接続できることを意味します。

<dubbo:reference interface="com.baeldung.dubbo.remote.GreetingsService"
  id="greetingsService" url="dubbo://127.0.0.1:20880"/>

基本的には、この手順は従来のWebサービスと似ていますが、Dubboはそれを単純明快で軽量にしているだけです。


4.4. 簡易レジストリ

「見えない」マルチキャストレジストリを使用する場合、レジストリサービスはスタンドアロンではありません。ただし、制限付きのローカルネットワークにのみ適用されます。

管理可能なレジストリを明示的に設定するには、https://github.com/apache/incubator-dubbo/blob/master/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/を使用できますdubbo/registry/dubbo/SimpleRegistryService.java[

SimpleRegistryService

]。

次のBean設定をSpringコンテキストにロードした後、簡単なレジストリサービスが開始されます。

<dubbo:application name="simple-registry"/>
<dubbo:protocol port="9090"/>
<dubbo:service interface="com.alibaba.dubbo.registry.RegistryService"
  ref="registryService" registry="N/A" ondisconnect="disconnect">
    <dubbo:method name="subscribe">
        <dubbo:argument index="1" callback="true"/>
    </dubbo:method>
    <dubbo:method name="unsubscribe">
        <dubbo:argument index="1" callback="true"/>
    </dubbo:method>
</dubbo:service>

<bean class="com.alibaba.dubbo.registry.simple.SimpleRegistryService"
  id="registryService"/>


SimpleRegistryService

クラスはアーティファクトに含まれていないため、https://github.com/apache/incubator-dubbo/blob/master/dubbo-registry/dubbo-registry-default/src/test/java/をコピーしました。 Githubリポジトリから直接、org/apache/dubbo/registry/dubbo/SimpleRegistryService.java[ソースコード]。

次に、プロバイダとコンシューマのレジストリ設定を調整します。

<dubbo:registry address="127.0.0.1:9090"/>


SimpleRegistryService

はテスト時にスタンドアロンのレジストリとして使用できますが、本番環境での使用はお勧めできません。


4.5. Javaの設定

Java API、プロパティファイル、および注釈による設定もサポートされています。ただし、プロパティファイルと注釈は、アーキテクチャがそれほど複雑ではない場合にのみ適用されます。

マルチキャストレジストリ用の以前のXML設定をAPI設定に変換する方法を見てみましょう。まず、プロバイダは次のように設定されています。

ApplicationConfig application = new ApplicationConfig();
application.setName("demo-provider");
application.setVersion("1.0");

RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("multicast://224.1.1.1:9090");

ServiceConfig<GreetingsService> service = new ServiceConfig<>();
service.setApplication(application);
service.setRegistry(registryConfig);
service.setInterface(GreetingsService.class);
service.setRef(new GreetingsServiceImpl());

service.export();

サービスはすでにマルチキャストレジストリを介して公開されているので、ローカルクライアントで利用しましょう。

ApplicationConfig application = new ApplicationConfig();
application.setName("demo-consumer");
application.setVersion("1.0");

RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("multicast://224.1.1.1:9090");

ReferenceConfig<GreetingsService> reference = new ReferenceConfig<>();
reference.setApplication(application);
reference.setRegistry(registryConfig);
reference.setInterface(GreetingsService.class);

GreetingsService greetingsService = reference.get();
String hiMessage = greetingsService.sayHi("baeldung");

上記のスニペットは前のXML構成例と同じように機能しますが、もう少し簡単です。当分の間、私たちがDubboを最大限に活用するつもりなら、XML設定は最初の選択であるべきです。


5プロトコルサポート

このフレームワークは、

dubbo



RMI



hessian



HTTP



web service



thrift



memcached



redis

など、複数のプロトコルをサポートしています。


dubbo

を除いて、ほとんどのプロトコルはよく知られています。このプロトコルの新機能を見てみましょう。


dubbo

プロトコルは、プロバイダとコンシューマ間の持続的な接続を維持します。長い接続とNIOのノンブロッキングネットワーク通信は、小規模のデータパケット(<100K)を送信しながらかなり優れたパフォーマンスをもたらします。

ポート、コンシューマあたりの接続数、最大許容接続数など、設定可能なプロパティがいくつかあります。

<dubbo:protocol name="dubbo" port="20880"
  connections="2" accepts="1000"/>

Dubboはまた、異なるプロトコルを介してサービスを同時に公開することもサポートしています。

<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:protocol name="rmi" port="1099"/>

<dubbo:service interface="com.baeldung.dubbo.remote.GreetingsService"
  version="1.0.0" ref="greetingsService" protocol="dubbo"/>
<dubbo:service interface="com.bealdung.dubbo.remote.AnotherService"
  version="1.0.0" ref="anotherService" protocol="rmi"/>

そしてはい、上のスニペットに示すように、異なるプロトコルを使用して異なるサービスを公開することができます。基礎となるトランスポータ、シリアル化の実装、およびネットワーキングに関連するその他の一般的なプロパティも設定可能です。


6. 結果キャッシング

ホットデータへのアクセスを高速化するために、ネイティブのリモート結果キャッシングがサポートされています。 Bean参照にcache属性を追加するのと同じくらい簡単です。

<dubbo:reference interface="com.baeldung.dubbo.remote.GreetingsService"
  id="greetingsService" cache="lru"/>

ここでは、最低使用頻度キャッシュを設定しました。キャッシュの振る舞いを検証するために、以前の標準実装で少し変更します(「特別な実装」と呼びましょう)

public class GreetingsServiceSpecialImpl implements GreetingsService {
    @Override
    public String sayHi(String name) {
        try {
            SECONDS.sleep(5);
        } catch (Exception ignored) { }
        return "hi, " + name;
    }
}

プロバイダを起動した後、コンシューマ側で検証できます。結果が複数回呼び出されたときにキャッシュされることがわかります。

@Test
public void givenProvider__whenConsumerSaysHi__thenGotResponse() {
    ClassPathXmlApplicationContext localContext
      = new ClassPathXmlApplicationContext("multicast/consumer-app.xml");
    localContext.start();
    GreetingsService greetingsService
      = (GreetingsService) localContext.getBean("greetingsService");

    long before = System.currentTimeMillis();
    String hiMessage = greetingsService.sayHi("baeldung");

    long timeElapsed = System.currentTimeMillis() - before;
    assertTrue(timeElapsed > 5000);
    assertNotNull(hiMessage);
    assertEquals("hi, baeldung", hiMessage);

    before = System.currentTimeMillis();
    hiMessage = greetingsService.sayHi("baeldung");
    timeElapsed = System.currentTimeMillis() - before;

    assertTrue(timeElapsed < 1000);
    assertNotNull(hiMessage);
    assertEquals("hi, baeldung", hiMessage);
}

ここでは、コンシューマが特別なサービス実装を呼び出しているため、呼び出しが最初に完了するまでに5秒以上かかりました。

再度呼び出すと、結果がキャッシュから返されるため、

sayHi

メソッドはほぼ即時に完了します。

スレッドローカルキャッシュとJCacheもサポートされています。


7. クラスタサポート

Dubboは、ロードバランシング機能といくつかのフォールトトレランス戦略を使用して、サービスを自由に拡張できます。ここでは、クラスタ内のサービスを管理するためのレジストリとしてZookeeperがあるとします。

プロバイダはZookeeperに自分のサービスを次のように登録できます。

<dubbo:registry address="zookeeper://127.0.0.1:2181"/>


POM

にこれらの追加の依存関係が必要であることに注意してください。

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.11</version>
</dependency>
<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.10</version>
</dependency>


zookeeper

の依存関係と

zkclient

の最新バージョンはhttps://search.maven.org/classic/#search%7C1%7Cg%3A%22org.apache.zookeeper%22%20AND%20a%3A%22zookeeper%にあります。 22[ここ]およびhttps://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22com.101tec%22%20AND%20a%3A%22zkclient%22[here]。


7.1. 負荷分散

現在、このフレームワークはいくつかの負荷分散戦略をサポートしています。

  • ランダム

  • ラウンドロビン

  • 最もアクティブでない

  • 一貫ハッシュ。

次の例では、クラスタ内のプロバイダとして2つのサービス実装があります。要求はラウンドロビン方式でルーティングされます。

まず、サービスプロバイダを設定しましょう。

@Before
public void initRemote() {
    ExecutorService executorService = Executors.newFixedThreadPool(2);
    executorService.submit(() -> {
        ClassPathXmlApplicationContext remoteContext
          = new ClassPathXmlApplicationContext("cluster/provider-app-default.xml");
        remoteContext.start();
    });
    executorService.submit(() -> {
        ClassPathXmlApplicationContext backupRemoteContext
          = new ClassPathXmlApplicationContext("cluster/provider-app-special.xml");
        backupRemoteContext.start();
    });
}

今すぐ私たちはすぐに応答する標準的な「速いプロバイダー」と、リクエストごとに5秒間スリープする特別な「遅いプロバイダー」を持っています。

ラウンドロビン方式で6回実行した後、平均応答時間は少なくとも2.5秒になると予想されます。

@Test
public void givenProviderCluster__whenConsumerSaysHi__thenResponseBalanced() {
    ClassPathXmlApplicationContext localContext
      = new ClassPathXmlApplicationContext("cluster/consumer-app-lb.xml");
    localContext.start();
    GreetingsService greetingsService
      = (GreetingsService) localContext.getBean("greetingsService");

    List<Long> elapseList = new ArrayList<>(6);
    for (int i = 0; i < 6; i++) {
        long current = System.currentTimeMillis();
        String hiMessage = greetingsService.sayHi("baeldung");
        assertNotNull(hiMessage);
        elapseList.add(System.currentTimeMillis() - current);
    }

    OptionalDouble avgElapse = elapseList
      .stream()
      .mapToLong(e -> e)
      .average();
    assertTrue(avgElapse.isPresent());
    assertTrue(avgElapse.getAsDouble() > 2500.0);
}

また、動的負荷分散を採用しています。次の例では、ラウンドロビン方式で、新しいプロバイダがオンラインになったときに、コンシューマが新しいサービスプロバイダを候補として自動的に選択することを示します。

「低速プロバイダ」は、システム起動後2秒後に登録されます。

@Before
public void initRemote() {
    ExecutorService executorService = Executors.newFixedThreadPool(2);
    executorService.submit(() -> {
        ClassPathXmlApplicationContext remoteContext
          = new ClassPathXmlApplicationContext("cluster/provider-app-default.xml");
        remoteContext.start();
    });
    executorService.submit(() -> {
        SECONDS.sleep(2);
        ClassPathXmlApplicationContext backupRemoteContext
          = new ClassPathXmlApplicationContext("cluster/provider-app-special.xml");
        backupRemoteContext.start();
        return null;
    });
}

コンシューマは1秒に1回リモートサービスを呼び出します。 6回実行した後、平均応答時間は1.6秒を超えると予想されます。

@Test
public void givenProviderCluster__whenConsumerSaysHi__thenResponseBalanced()
  throws InterruptedException {
    ClassPathXmlApplicationContext localContext
      = new ClassPathXmlApplicationContext("cluster/consumer-app-lb.xml");
    localContext.start();
    GreetingsService greetingsService
      = (GreetingsService) localContext.getBean("greetingsService");
    List<Long> elapseList = new ArrayList<>(6);
    for (int i = 0; i < 6; i++) {
        long current = System.currentTimeMillis();
        String hiMessage = greetingsService.sayHi("baeldung");
        assertNotNull(hiMessage);
        elapseList.add(System.currentTimeMillis() - current);
        SECONDS.sleep(1);
    }

    OptionalDouble avgElapse = elapseList
      .stream()
      .mapToLong(e -> e)
      .average();

    assertTrue(avgElapse.isPresent());
    assertTrue(avgElapse.getAsDouble() > 1666.0);
}

ロードバランサは、コンシューマ側とプロバイダ側の両方で設定できます。これがコンシューマ側の設定例です。

<dubbo:reference interface="com.baeldung.dubbo.remote.GreetingsService"
  id="greetingsService" loadbalance="roundrobin"/>


7.2. フォールトトレランス

次のようないくつかのフォールトトレランス戦略がDubboでサポートされています。

  • フェイルオーバー

  • フェイルセーフ

  • フェイルファースト

  • フェイルバック

  • フォーク

フェイルオーバーの場合、1つのプロバイダに障害が発生したときに、コンシューマはクラスタ内の他のサービスプロバイダを試してみることができます。

フォールトトレランス戦略は、サービスプロバイダに対して次のように構成されています。

<dubbo:service interface="com.baeldung.dubbo.remote.GreetingsService"
  ref="greetingsService" cluster="failover"/>

実際のサービスフェイルオーバーを実演するために、

GreetingsService

のフェイルオーバー実装を作成しましょう。

public class GreetingsFailoverServiceImpl implements GreetingsService {

    @Override
    public String sayHi(String name) {
        return "hi, failover " + name;
    }
}

私たちの特別なサービス実装

GreetingsServiceSpecialImpl

がそれぞれのリクエストに対して5秒間スリープしていることを思い出すことができます。

2秒を超える応答がコンシューマにとっての要求の失敗と見なされる場合、フェールオーバーのシナリオがあります。

<dubbo:reference interface="com.baeldung.dubbo.remote.GreetingsService"
  id="greetingsService" retries="2" timeout="2000"/>

2つのプロバイダを起動した後、次のスニペットを使用してフェールオーバーの動作を確認できます。

@Test
public void whenConsumerSaysHi__thenGotFailoverResponse() {
    ClassPathXmlApplicationContext localContext
      = new ClassPathXmlApplicationContext(
      "cluster/consumer-app-failtest.xml");
    localContext.start();
    GreetingsService greetingsService
      = (GreetingsService) localContext.getBean("greetingsService");
    String hiMessage = greetingsService.sayHi("baeldung");

    assertNotNull(hiMessage);
    assertEquals("hi, failover baeldung", hiMessage);
}


8概要

このチュートリアルでは、Dubboについて少し説明しました。ほとんどのユーザーは、そのシンプルさと豊富で強力な機能に魅了されています。

この記事で紹介したもの以外に、フレームワークにはまだ検証されていない、パラメータ検証、通知とコールバック、一般化された実装と参照、リモート結果のグループ化とマージ、サービスのアップグレード、下位互換性などの機能があります。少し。

いつものように、完全な実装はhttps://github.com/eugenp/tutorials/tree/master/dubbo[over on Github]にあります。