1概要
このクイック記事では、Spring Reactorプロジェクトを紹介します。事後対応型のイベント駆動型アプリケーションの実際のシナリオを設定します。
2スプリングリアクターの基本
2.1. なぜリアクターなのか?
リアクティブ設計https://en.wikipedia.org/wiki/Reactor__pattern[pattern]は、単一または複数のサービスハンドラからの大量の同時サービス要求を非同期に処理するためのイベントベースのアーキテクチャです。
そしてSpring Reactorプロジェクトはこのパターンに基づいており、__JVM上に非同期で反応的なアプリケーションを構築するという明確で野心的な目標を持っています。
2.2. シナリオ例
始める前に、リアクティブアーキテクチャースタイルを利用することが意味のある、いくつかの興味深いシナリオを紹介します。
-
アマゾンのような大規模オンラインショッピングアプリケーションの通知サービス
-
銀行部門の巨大な取引処理サービス
-
株価が同時に変動する株取引事業
注意すべき1つの簡単な注意は、イベントバスの実装はイベントの永続化を提供しないということです。デフォルトのSpring Eventバスと同じように、これはインメモリ実装です。
3 Mavenの依存関係
次の依存関係を__pom.xmlに追加して、Spring Reactorを使い始めましょう。
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-bus</artifactId>
<version>2.0.8.RELEASE</version>
</dependency>
Central Maven Repository
で、最新バージョンのreactor-busを確認できます。
4デモアプリケーションの構築
原子炉ベースのアプローチの利点をよりよく理解するために、
実際的な例を見てみましょう
。
オンラインストアで注文が完了したら、メールと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メールゲートウェイを介してメッセージを送信する実際のシナリオを説明するために、
Thread.sleep(5000)による
initiateNotification__メソッドに意図的に5秒の遅延を導入しています
そのため、スレッドがサービスにヒットすると、5秒間ブロックされます。
4.3. 消費者
それでは、アプリケーションのより反応的な側面にジャンプしてコンシューマを実装しましょう。それを次に
reactorイベントbus
にマッピングします。
@Service
public class NotificationConsumer implements
Consumer<Event<NotificationData>> {
@Autowired
private NotificationService notificationService;
@Override
public void accept(Event<NotificationData> notificationDataEvent) {
NotificationData notificationData = notificationDataEvent.getData();
try {
notificationService.initiateNotification(notificationData);
} catch (InterruptedException e) {
//ignore
}
}
}
ご覧のとおり、コンシューマは
Consumer <T>
インタフェースを単一の
accept
メソッドで実装しているだけです。
典型的なSpringリスナー
と同じように、メインロジックを実行するのはこの単純な実装です。
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
を通してイベントを送信しています – ユニークなキーを使って
_.
_
つまり、単純に言うと、クライアントがパラメータ値10でURLにアクセスすると、合計10のイベントがバスを介して送信されます。
** 4.5. Javaの設定
**
もう終わりです。 Java ConfigにすべてをまとめてBootアプリケーションを作成しましょう。
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を介して
EventBus
Beanを作成しています。
今回のケースでは、環境内で使用可能なデフォルトのスレッドプールを使用してイベントバスをインスタンス化しています。
バスをもう少し制御したい場合は、スレッドカウントを実装に提供することもできます。
EventBus evBus = EventBus.create(
env,
Environment.newDispatcher(
REACTOR__THREAD__COUNT,REACTOR__THREAD__COUNT,
DispatcherType.THREAD__POOL__EXECUTOR));
-
次に – ここでは、
$
属性の静的インポートをどのように使用しているかにも注意してください。**
この機能は、元々フィールドを定義していたクラスを参照しなくても、定数(この場合は$属性)をコードに含めるための、タイプセーフなメカニズムを提供します。
私たちは
run
メソッドの実装でこの機能を利用しています –
マッチング通知があったときにトリガーされるようにコンシューマーを登録しています
これは、各消費者を識別できるようにする
一意のセレクタキー
に基づいています。
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. 結論
この小規模なアプリケーションでは、アプリケーション全体のパフォーマンスが向上するとともに、確実にスループットが向上します。
しかし、このシナリオは表面を傷つけているだけであり、反応パラダイムを理解するための良い基盤にすぎません。
いつものように、ソースコードはhttps://github.com/eugenp/tutorials/tree/master/spring-reactor[GitHubで利用可能]です。