スプリングリアクターの紹介

Spring Reactorの紹介

1. 概要

この簡単な記事では、SpringReactorプロジェクトを紹介します。 反応型のイベント駆動型アプリケーションの実際のシナリオを設定します。

2. スプリングリアクターの基本

2.1. なぜリアクトル?

リアクティブデザインpatternは、単一または複数のサービスハンドラーからの大量の同時サービス要求を非同期的に処理するためのイベントベースのアーキテクチャです。

そして、Spring Reactorプロジェクトはこのパターンに基づいており、JVM.で非同期のリアクティブアプリケーションを構築するという明確で野心的な目標を持っています。

2.2. シナリオ例

始める前に、リアクティブアーキテクチャスタイルを活用することが理にかなっているいくつかの興味深いシナリオを紹介します。

  • Amazonのような大規模なオンラインショッピングアプリケーションの通知サービス

  • 銀行セクターの巨大なトランザクション処理サービス

  • 株価が同時に変化する株式取引事業

注意すべき簡単な注意点の1つは、イベントバスの実装ではイベントの永続性が提供されないことです。デフォルトのSpringEventバスと同じように、これはメモリ内の実装です。

3. Mavenの依存関係

次の依存関係をpom.xml:に追加して、SpringReactorの使用を開始しましょう。


    io.projectreactor
    reactor-bus
    2.0.8.RELEASE

Central Maven Repositoryでreactor-busの最新バージョンを確認できます。

4. デモアプリケーションの構築

リアクターベースのアプローチの利点をよりよく理解するために、let’s look at a practical example

オンラインストアでの注文が完了した後、メールとSMSでユーザーに通知する簡単な通知アプリを作成します。

通常の同期実装は、当然SMSサービスのスループットに制限されます。 交通量の急増、そのような休日は一般的に問題があります。

リアクティブなアプローチにより、システムはより柔軟になり、SMSや電子メールサーバーなどのこれらのタイプの外部システムでの障害やタイムアウトによりよく適応することができます。

アプリケーションを見てみましょう。より伝統的な側面から始めて、より反応性の高い構造に移ります。

4.1. シンプルなPOJO

まず、通知データを表すPOJOクラスを作成しましょう。

public class NotificationData {

    private long id;
    private String name;
    private String email;
    private String mobile;

    // getter and setter methods
}

4.2. サービス層

次に、簡単なサービスレイヤーを設定しましょう。

public interface NotificationService {

    void initiateNotification(NotificationData notificationData)
      throws InterruptedException;

}

そして、ここでの長い操作をシミュレートする実装:

@Service
public class NotificationServiceimpl implements NotificationService {

    @Override
    public void initiateNotification(NotificationData notificationData)
      throws InterruptedException {

      System.out.println("Notification service started for "
        + "Notification ID: " + notificationData.getId());

      Thread.sleep(5000);

      System.out.println("Notification service ended for "
        + "Notification ID: " + notificationData.getId());
    }
}

SMSゲートウェイまたはEメールゲートウェイを介してメッセージを送信する実際のシナリオを説明するために、initiateNotificationメソッドにThread.sleep(5000).だけ5秒の遅延を意図的に導入していることに注意してください。

したがって、スレッドがサービスにヒットすると、5秒間ブロックされます。

4.3. 消費者

次に、アプリケーションのより反応的な側面に飛び込んで、コンシューマーを実装します。次に、これをreactor event busにマップします。

@Service
public class NotificationConsumer implements
  Consumer> {

    @Autowired
    private NotificationService notificationService;

    @Override
    public void accept(Event notificationDataEvent) {
        NotificationData notificationData = notificationDataEvent.getData();

        try {
            notificationService.initiateNotification(notificationData);
        } catch (InterruptedException e) {
            // ignore
        }
    }
}

ご覧のとおり、コンシューマーは、単一のacceptメソッドを使用してConsumer<T>インターフェイスを実装しているだけです。 a typical Spring listenerと同様に、メインロジックを実行するのはこの単純な実装です。

4.4. コントローラー

最後に、イベントを使用できるようになったので、イベントも生成しましょう。

単純なコントローラーでこれを行います。

@Controller
public class NotificationController {

    @Autowired
    private EventBus eventBus;

    @GetMapping("/startNotification/{param}")
    public void startNotification(@PathVariable Integer param) {
        for (int i = 0; i < param; i++) {
            NotificationData data = new NotificationData();
            data.setId(i);

            eventBus.notify("notificationConsumer", Event.wrap(data));

            System.out.println(
              "Notification " + i + ": notification task submitted successfully");
        }
    }
}

これは非常に自明です–ここではEventBusを介してイベントを送信しています–一意のキー.を使用しています

したがって、単純に、クライアントがparam値が10のURLをヒットすると、合計10個のイベントがバスを介して送信されます。

4.5. Java構成

ほぼ完了です。すべてをJavaConfigと組み合わせて、ブートアプリケーションを作成しましょう。

import static reactor.bus.selector.Selectors.$;

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application implements CommandLineRunner {

    @Autowired
    private EventBus eventBus;

    @Autowired
    private NotificationConsumer notificationConsumer;

    @Bean
    Environment env() {
        return Environment.initializeIfEmpty().assignErrorJournal();
    }

    @Bean
    EventBus createEventBus(Environment env) {
        return EventBus.create(env, Environment.THREAD_POOL);
    }

    @Override
    public void run(String... args) throws Exception {
        eventBus.on($("notificationConsumer"), notificationConsumer);
    }

    public static void main(String[] args){
        SpringApplication.run(Application.class, args);
    }
}

ここで、EventBusの静的create APIを介してEventBusBeanを作成しています。

この例では、環境で使用可能なデフォルトのスレッドプールを使用してイベントバスをインスタンス化しています。

バスをもう少し制御したい場合は、スレッドカウントを実装に提供することもできます。

EventBus evBus = EventBus.create(
  env,
  Environment.newDispatcher(
    REACTOR_THREAD_COUNT,REACTOR_THREAD_COUNT,
    DispatcherType.THREAD_POOL_EXECUTOR));

次へ–ここで$属性の静的インポートをどのように使用しているかにも注目してください。

この機能は、フィールドを最初に定義したクラスを参照しなくても、定数(この場合は$属性)をコードに含めるためのタイプセーフなメカニズムを提供します。

runメソッドの実装でこの機能を利用しています–where we’re registering our consumer to be triggered when the matching notification.

これは、各コンシューマーを識別できるようにするa unique selector keyに基づいています。

5. アプリケーションをテストする

Mavenビルドを実行した後、java -jar name_of_the_application.jarを実行してアプリケーションを実行できるようになりました。

次に、アプリケーションをテストするための小さなJUnitテストクラスを作成しましょう。 Spring BootのSpringJUnit4ClassRunnerを使用して、テストケースを作成します。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {Application.class})
public class DataLoader {

    @Test
    public void exampleTest() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getForObject(
          "http://localhost:8080/startNotification/10", String.class);
    }
}

それでは、このテストケースを実行して、アプリケーションをテストしましょう。

Notification 0: notification task submitted successfully
Notification 1: notification task submitted successfully
Notification 2: notification task submitted successfully
Notification 3: notification task submitted successfully
Notification 4: notification task submitted successfully
Notification 5: notification task submitted successfully
Notification 6: notification task submitted successfully
Notification 7: notification task submitted successfully
Notification 8: notification task submitted successfully
Notification 9: notification task submitted successfully
Notification service started for Notification ID: 1
Notification service started for Notification ID: 2
Notification service started for Notification ID: 3
Notification service started for Notification ID: 0
Notification service ended for Notification ID: 1
Notification service ended for Notification ID: 0
Notification service started for Notification ID: 4
Notification service ended for Notification ID: 3
Notification service ended for Notification ID: 2
Notification service started for Notification ID: 6
Notification service started for Notification ID: 5
Notification service started for Notification ID: 7
Notification service ended for Notification ID: 4
Notification service started for Notification ID: 8
Notification service ended for Notification ID: 6
Notification service ended for Notification ID: 5
Notification service started for Notification ID: 9
Notification service ended for Notification ID: 7
Notification service ended for Notification ID: 8
Notification service ended for Notification ID: 9

ご覧のとおり、エンドポイントがヒットするとすぐに、ブロックを作成せずに10個のタスクすべてが即座に送信されます。 そして、送信されると、通知イベントは並行して処理されます。

このシナリオでは、これらのイベントを任意の順序で処理する必要はないことに注意してください。

6. 結論

この小さなアプリケーションでは、スループットが向上し、アプリケーション全体がより適切に動作します。

ただし、このシナリオは表面をスクラッチするだけであり、リアクティブパラダイムの理解を開始するための適切な基盤にすぎません。

いつものように、ソースコードはover on GitHubで利用できます。