CloudEvents APIを使ってAzure Event GridのTopicにイベントを投げ込む

このエントリは2020/10/30現在の情報に基づいています。将来の機能追加や変更に伴い、記載内容との乖離が発生する可能性があります。

Event GridとCloudEvents関連のエントリをすでに2件公開している。

CloudEventsを使う
https://logico-jp.io/2020/09/06/use-cloudevents-schema-in-azure-event-grid/
Azure Event Grid Java SDK で CloudEvents を扱う際の注意事項
https://logico-jp.io/2020/10/23/tips-for-using-event-grid-sdk-to-handle-cloudevents/

Azure Event Grid SDKを使ったイベントのポストはできて当然なので、今回は、Event Grid SDKではなく、CloudEventsのAPIを使ってInteroperabilityを確認してみる。CloudEventsのWebページに以下のように記載があるので、問題なくつながるはず(というか、つながらないとまずい)。

Azure Event Grid – Event Grid natively supports events in the JSON implementation of CloudEvents v1.0 and the HTTP protocol binding

構成

Event Gridの前後に以下のようなアプリケーションを配置する。このエントリで作成するのはクライアントアプリケーション。後日CloudEvents APIを使ってJAX-RSアプリケーションを作成する。また投げ込んだイベントを確認するため、チュートリアルで提供されるViewerアプリケーションも利用する。

前提条件

Event Gridで受付可能なイベント形式

Event Gridは、現時点ではStructured Content Modeのみをサポートしている(Binary Content Modeは未サポート)。そのため、JSON Event Format for CloudEventsの仕様に従う必要がある。

JSON Event Format for CloudEvents – Version 1.0
https://github.com/cloudevents/spec/blob/v1.0/json-format.md

JSONスキーマは以下。
https://github.com/cloudevents/spec/blob/v1.0/spec.json

Event Gridへのイベントポスト方法

SDKを使わず、Event Gridとの統合機能も使わない場合、HTTP POSTでEvent Gridトピックへイベントを投げ込むことができる。アクセス方法は以下のドキュメントに記載がある。

Azure Event Grid のカスタム トピックに投稿する / Post to custom topic for Azure Event Grid
https://docs.microsoft.com/azure/event-grid/post-to-custom-topic

上記ドキュメントによれば、以下のURLでPOSTできる。

https://{topic-endpoint}?api-version=2018-01-01

ただし、POSTにあたってはアクセスキーが必要。キーはPortalやCLIで取得できるが、利用期限を指定できるShared Access Signatureを使ってもよい。今回はPortalなどから取得できるアクセスキーを使っている。なお、Shared Access Signatureの生成方法は以下を参照。

Creating a shared access signature
https://github.com/Azure/azure-sdk-for-java/blob/master/sdk/eventgrid/azure-messaging-eventgrid/README.md#creating-a-shared-access-signature

CloudEventsについて

今更感があるが、CloudEventsの情報は以下から。

CloudEvents
https://cloudevents.io/

種々の言語用のSDKが用意されている。今回は(今回も)Javaで試す。

Java SDK for CloudEvents API
https://github.com/cloudevents/sdk-java

Java SDKにも種々のサブカテゴリが用意されている。ふつうならCore APIを使って低レベルの実装をするべきだが、楽をしたいので、JAX-RS用のAPIとJacksonを使ったHTTP Binding APIを使うことにする。

HTTP Protocol Binding for Jakarta RESTful Web Services
https://github.com/cloudevents/sdk-java/tree/master/http/restful-ws
Json EventFormat implementation with Jackson
https://github.com/cloudevents/sdk-java/tree/master/formats/json-jackson

依存関係

CloudEventsの上記APIを利用する上で必要な依存関係は以下の通り(2020/10/30現在)。

<dependency>
  <groupId>io.cloudevents</groupId>
  <artifactId>cloudevents-http-restful-ws</artifactId>
  <version>2.0.0-milestone3</version>
</dependency>
<dependency>
  <groupId>io.cloudevents</groupId>
  <artifactId>cloudevents-json-jackson</artifactId>
  <version>2.0.0-milestone3</version>
</dependency>

JAX-RS(Jakarta RESTful Web Services)を使うので、このための依存関係も追加しておく必要がある。今回は以下のような感じ。

<dependency>
  <groupId>org.glassfish.jersey.core</groupId>
  <artifactId>jersey-client</artifactId>
  <version>3.0.0-M6</version>
</dependency>
<dependency>
  <groupId>org.glassfish.jersey.inject</groupId>
  <artifactId>jersey-hk2</artifactId>
  <version>3.0.0-M6</version>
</dependency>
<dependency>
  <groupId>org.glassfish.jersey.media</groupId>
  <artifactId>jersey-media-json-jackson</artifactId>
  <version>3.0.0-M6</version>
</dependency>
<dependency>
  <groupId>org.glassfish</groupId>
  <artifactId>jakarta.json</artifactId>
  <version>2.0.0-RC3</version>
</dependency>
<dependency>
  <groupId>jakarta.ws.rs</groupId>
  <artifactId>jakarta.ws.rs-api</artifactId>
  <version>3.0.0-M1</version>
</dependency>
<dependency>
  <groupId>jakarta.json</groupId>
  <artifactId>jakarta.json-api</artifactId>
  <version>2.0.0-RC3</version>
</dependency>

イベントの作成

CloudEvents::v1() メソッドを使って、Fluent Styleで記述していく。カスタムデータは今回JSONで指定しているので、.withDataContentTypeを使ってContent-Typeとしてapplication/jsonを指定している。

JsonObject jsonObject = Json.createObjectBuilder().add("message", "Using CloudEvents.io API to send CloudEvents!!").build();

CloudEvent ce = CloudEventBuilder.v1()
        .withId("A234-1234-1234")
        .withType("io.logico-jp.ExampleEventType")
        .withSource(URI.create("io/logico-jp/source"))
        .withTime(OffsetDateTime.now(ZoneId.ofOffset("UTC", ZoneOffset.UTC)))
        .withDataContentType(MediaType.APPLICATION_JSON)
        .withData(jsonObject.toString().getBytes(StandardCharsets.UTF_8))
        .build();

シリアライズ

直前に作成したCloudEventsインスタンスをそのまま投げることはできず、JSONへシリアライズする必要がある。このために存在するのがJson EventFormat implementation with Jackson。

EventFormat format =EventFormatProvider
        .getInstance()
        .resolveFormat(JsonFormat.CONTENT_TYPE);

byte[] serialized = format.serialize(ce);

REST Clientの作成

ここは通常のJakarta RESTful Web Servicesと同じ。HTTP Headerとして指定すべきアクセスキーも指定する。注意すべきはContent-Typeはapplication/cloudevents+jsonを指定する点。

MultivaluedMap<String, Object> headers = new MultivaluedHashMap<>();
headers.add("aeg-sas-key", AEG_KEY);
Response response = ClientBuilder.newClient().target(AEG_ENDPOINT)
        .path("/api/events")
        .queryParam("api-version", "2018-01-01")
        .request("application/cloudevents+json")
        .headers(headers)
        .post(Entity.entity(serialized, "application/cloudevents+json"));

テスト

テストには、以下のチュートリアルで構成したイベント出力アプリを使う。

クイック スタート:Azure portal と Event Grid を使ったカスタム イベントの Web エンドポイントへのルーティング / Quickstart: Route custom events to web endpoint with the Azure portal and Event Grid
https://docs.microsoft.com/azure/event-grid/custom-event-quickstart-portal

このチュートリアルの中でデプロイするApp ServiceがイベントをJSON形式で表示してくれる。なお、イベントサブスクライブの際はCloudEvents 1.0形式に指定しておくこと(カスタムイベントでも問題ないが、CloudEvents 1.0にしておけばイベントのValidationができる)。当然ながら、Event Grid TopicはCloudEvents 1.0で受け取れるようにしておく。

先ほど作成したテストクライアントからイベントを投げてみると…。

Azure Portalからも、イベントが正常に配信されたことを確認できる。

まとめ

上記の通り、問題なくCloudEvents SDKのAPIでもAzure Event Gridにイベントを投入できることを確認した。次回はCloudEvents SDKでサーバーを実装し、Azure Event GridのトピックをサブスクライブしてWebhookでイベントを取り込む部分を試すことにする。

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください