Azure Events Hubs に Event を送信するための便利ツール

## はじめに

この記事を読んでくださっている皆様は、Azure Event Hubs にイベントを送信するために、何を使っていますか？私は、Azure Functions でイベントを送信するコードを書くことが多いです。

しかし、イマイチコードを書く気分でない日もあります。そんな日に、ふと Azure Event Hubs にイベントを送信したくなったらどうでしょう。検証作業は滞ってしまいますよね。え？そんな日はない？まぁまぁそう言わずに！

そこで、今回は追加のコード不要で Azure Event Hubs に Event を送信できる便利ツール **EventHubDataGenerator** をご紹介します。

## この記事のゴール

EventHubDataGenerator から Azure Event Hubs にイベントを送信し、Azure Functions でイベントを取得する、という構成を確認します。ローカルに環境を構築すると、 EventHubs Connection String と Event Hub 名を指定するだけでイベントを送信することが可能になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/3a2df1ee-62f8-920a-02b1-dc83abbf7057.png)


## 前提条件

本手順では、以下のツールが必要となります。必要に応じてインストールしてください。

- Microsoft Azure Subscription
- java 8 or higher
- Storage Emulator (インストール方法は[こちら](https://docs.microsoft.com/ja-jp/azure/storage/common/storage-use-azurite?toc=%2Fazure%2Fstorage%2Fblobs%2Ftoc.json&tabs=visual-studio))
- Docker 20.10.7
- Visual Studio Code （Docker 拡張機能インストール済み）

## 手順

### 1. Azure Event Hubs、Azure Functions の作成

Azure Event Hubs、Azure Functions の詳細な作成手順は本記事では割愛します。なお、今回はパーティション数 `32` の Event Hub を作成しています。

詳細な手順は、下記の Docs をご参照ください。

- [クイック スタート:Azure portal を使用したイベント ハブの作成](https://docs.microsoft.com/ja-jp/azure/event-hubs/event-hubs-create)
- [Visual Studio Code を使用して Azure Functions を開発する](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-develop-vs-code?tabs=java)

### 2. EventHubDataGenerator のインストール

ターミナルから下記コマンドを実行し、Docker Image を Pull します。

```shell
$ docker pull jalaluddin7/fakerapp:v1
v1: Pulling from jalaluddin7/fakerapp
4fe2ade4980c: Pull complete 
c245f6a8ecc5: Pull complete 
82bdc9503d50: Pull complete 
6227b595540d: Pull complete 
6bf9374bd0c1: Pull complete 
a3de6b8808da: Pull complete 
d12737c13302: Pull complete 
Digest: sha256:856c22de4dd5466a5d4ef4aa7573263ce022d8987de5329dd3e26fc45f83f8d0
Status: Downloaded newer image for jalaluddin7/fakerapp:v1
```

上記で Pull した Docker Image をベースとしたコンテナを作成し、Run します。今回は、Visual Studio Code にインストールした拡張機能を使うことにします。

`jalaluddin7/fakerapp` を選択します。

![スクリーンショット 2021-09-02 0.47.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/e8490fb1-c564-b58e-9bef-fd51e82aa8a6.png)

`v1` を選択します。

![スクリーンショット 2021-09-02 0.47.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/7e7c7f4c-718e-d1ae-a7d0-cabcbce05947.png)

コンテナが起動したことを確認します。

```shell
$ docker ps
CONTAINER ID        IMAGE                                COMMAND                  CREATED             STATUS              PORTS                                        NAMES
3c75e98e82af        jalaluddin7/fakerapp:v1              "/bin/sh -c 'node in…"   3 minutes ago       Up 3 minutes        0.0.0.0:3000->3000/tcp                       suspicious_villani
```

[http://localhost:3000](http://localhost:3000) にアクセスし、起動したコンテナに接続します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/ddaf3cbf-7398-3845-5081-60975334f51f.png)

あとは、ここに Event Hub Connection String と Event Hub Namespace を指定するだけで、簡単にイベントを送信することができます。簡単ですね！

### 3. Azure Event Hubs に送信されたイベントの確認

実際に Azure Functions を使って、イベントが送信されていることを確認します。

なお、以下に示すのは、一部のコードです。詳細は、[こちら](https://github.com/kohei3110/Real-Time-Data-Processing) のリポジトリに公開しているので、適宜ご参照ください。

また、`local.settings.json` の Azure Event Hubs への接続文字列は、適切な値に置き換えてください。下記はサンプルです。

```json:local.settings.sample.json
{
    "IsEncrypted": false,
    "Values": {
      "AzureWebJobsStorage": "UseDevelopmentStorage=true",
      "FUNCTIONS_WORKER_RUNTIME": "java",
      "realtimedatastreaming_RootManageSharedAccessKey_EVENTHUB": "Endpoint=sb://xxxxxxxxxx.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }
}
```

以下の Function を実行し、送信されたイベントを確認します。

```java:EventHubTrigger.java 
package com.function;

import com.microsoft.azure.functions.annotation.*;
import com.microsoft.azure.functions.*;
import java.util.*;

/**
 * Azure Functions with Event Hub trigger.
 */
public class EventHubTrigger {
    /**
     * This function will be invoked when an event is received from Event Hub.
     */
    @FunctionName("EventHubTrigger")
    public void run(

        // Event Hub 'eventhub01' has 1 partition.
        @com.microsoft.azure.functions.annotation.EventHubTrigger(name = "message", eventHubName = "eventhub01", connection = "realtimedatastreaming_RootManageSharedAccessKey_EVENTHUB", consumerGroup = "$Default", cardinality = Cardinality.MANY, dataType = "string") List<String> message,

        // Event Hub 'eventhub02' has 32 partitions.
        // @com.microsoft.azure.functions.annotation.EventHubTrigger(name = "message", eventHubName = "eventhub02", connection = "realtimedatastreaming_RootManageSharedAccessKey_EVENTHUB", consumerGroup = "$Default", cardinality = Cardinality.MANY, dataType = "") List<String> message,
        final ExecutionContext context
    ) {
        context.getLogger().info("Length:" + message.size());
        message.forEach(singleMessage -> context.getLogger().info(singleMessage));
    }
}
```

イベントは下記の通り取得できます。

```log:sample.log
[2021-08-28T11:41:25.010Z] Executing 'Functions.EventHubTrigger' (Reason='(null)', Id=7b9ca2bb-c6ec-4aa7-be93-1ffdf61ffd16)
[2021-08-28T11:41:25.089Z] Length:1
[2021-08-28T11:41:25.091Z] {"sensor_id":"45","sensor_temp":76,"sensor_status":"WARN"}
[2021-08-28T11:41:25.093Z] Function "EventHubTrigger" (Id: 7b9ca2bb-c6ec-4aa7-be93-1ffdf61ffd16) invoked by Java Worker
[2021-08-28T11:41:25.119Z] Executed 'Functions.EventHubTrigger' (Succeeded, Id=7b9ca2bb-c6ec-4aa7-be93-1ffdf61ffd16, Duration=117ms)
[2021-08-28T11:41:25.307Z] Executing 'Functions.EventHubTrigger' (Reason='(null)', Id=719e8f22-ed7a-4edf-b0fb-638f355ae68a)
[2021-08-28T11:41:25.312Z] Length:10
[2021-08-28T11:41:25.316Z] {"sensor_id":"47","sensor_temp":66,"sensor_status":"OK"}
[2021-08-28T11:41:25.318Z] {"sensor_id":"20","sensor_temp":50,"sensor_status":"FAIL"}
[2021-08-28T11:41:25.319Z] {"sensor_id":"59","sensor_temp":85,"sensor_status":"OK"}
[2021-08-28T11:41:25.320Z] {"sensor_id":"10","sensor_temp":93,"sensor_status":"OK"}
[2021-08-28T11:41:25.321Z] {"sensor_id":"42","sensor_temp":48,"sensor_status":"WARN"}
[2021-08-28T11:41:25.322Z] {"sensor_id":"30","sensor_temp":63,"sensor_status":"OK"}
[2021-08-28T11:41:25.323Z] {"sensor_id":"14","sensor_temp":22,"sensor_status":"WARN"}
[2021-08-28T11:41:25.324Z] {"sensor_id":"40","sensor_temp":61,"sensor_status":"OK"}
[2021-08-28T11:41:25.324Z] {"sensor_id":"55","sensor_temp":46,"sensor_status":"WARN"}
[2021-08-28T11:41:25.325Z] {"sensor_id":"37","sensor_temp":54,"sensor_status":"OK"}
[2021-08-28T11:41:25.326Z] Function "EventHubTrigger" (Id: 719e8f22-ed7a-4edf-b0fb-638f355ae68a) invoked by Java Worker
[2021-08-28T11:41:25.328Z] Executed 'Functions.EventHubTrigger' (Succeeded, Id=719e8f22-ed7a-4edf-b0fb-638f355ae68a, Duration=21ms)
[2021-08-28T11:41:25.909Z] Executing 'Functions.EventHubTrigger' (Reason='(null)', Id=8a1a31ef-a3b7-4516-952f-aead5a46f747)
[2021-08-28T11:41:25.917Z] Length:10
[2021-08-28T11:41:25.926Z] {"sensor_id":"29","sensor_temp":93,"sensor_status":"OK"}
[2021-08-28T11:41:25.929Z] {"sensor_id":"65","sensor_temp":53,"sensor_status":"FAIL"}
[2021-08-28T11:41:25.934Z] {"sensor_id":"56","sensor_temp":96,"sensor_status":"WARN"}
[2021-08-28T11:41:25.943Z] {"sensor_id":"38","sensor_temp":97,"sensor_status":"FAIL"}
[2021-08-28T11:41:25.946Z] {"sensor_id":"20","sensor_temp":35,"sensor_status":"FAIL"}
[2021-08-28T11:41:25.949Z] {"sensor_id":"4","sensor_temp":38,"sensor_status":"FAIL"}
[2021-08-28T11:41:25.958Z] {"sensor_id":"57","sensor_temp":49,"sensor_status":"OK"}
[2021-08-28T11:41:25.960Z] {"sensor_id":"26","sensor_temp":21,"sensor_status":"WARN"}
[2021-08-28T11:41:25.962Z] {"sensor_id":"13","sensor_temp":31,"sensor_status":"OK"}
[2021-08-28T11:41:25.965Z] {"sensor_id":"70","sensor_temp":35,"sensor_status":"OK"}
[2021-08-28T11:41:25.977Z] Function "EventHubTrigger" (Id: 8a1a31ef-a3b7-4516-952f-aead5a46f747) invoked by Java Worker
[2021-08-28T11:41:25.984Z] Executed 'Functions.EventHubTrigger' (Succeeded, Id=8a1a31ef-a3b7-4516-952f-aead5a46f747, Duration=76ms)
[2021-08-28T11:41:26.363Z] Executing 'Functions.EventHubTrigger' (Reason='(null)', Id=bdd31c10-468a-4d13-983d-ebb801a0a7d5)
[2021-08-28T11:41:26.373Z] Length:10
[2021-08-28T11:41:26.376Z] {"sensor_id":"52","sensor_temp":62,"sensor_status":"WARN"}
[2021-08-28T11:41:26.378Z] {"sensor_id":"96","sensor_temp":41,"sensor_status":"OK"}
[2021-08-28T11:41:26.382Z] {"sensor_id":"19","sensor_temp":50,"sensor_status":"FAIL"}
[2021-08-28T11:41:26.384Z] {"sensor_id":"35","sensor_temp":42,"sensor_status":"OK"}
[2021-08-28T11:41:26.386Z] {"sensor_id":"61","sensor_temp":93,"sensor_status":"FAIL"}
[2021-08-28T11:41:26.389Z] {"sensor_id":"45","sensor_temp":21,"sensor_status":"WARN"}
[2021-08-28T11:41:26.391Z] {"sensor_id":"42","sensor_temp":36,"sensor_status":"OK"}
[2021-08-28T11:41:26.393Z] {"sensor_id":"45","sensor_temp":58,"sensor_status":"WARN"}
[2021-08-28T11:41:26.395Z] {"sensor_id":"80","sensor_temp":100,"sensor_status":"FAIL"}
[2021-08-28T11:41:26.396Z] {"sensor_id":"81","sensor_temp":82,"sensor_status":"FAIL"}
[2021-08-28T11:41:26.398Z] Function "EventHubTrigger" (Id: bdd31c10-468a-4d13-983d-ebb801a0a7d5) invoked by Java Worker
[2021-08-28T11:41:26.400Z] Executed 'Functions.EventHubTrigger' (Succeeded, Id=bdd31c10-468a-4d13-983d-ebb801a0a7d5, Duration=38ms)
[2021-08-28T11:41:26.662Z] Executing 'Functions.EventHubTrigger' (Reason='(null)', Id=63516233-49ca-49fe-8631-31fc7a398db4)
[2021-08-28T11:41:26.670Z] Length:10
[2021-08-28T11:41:26.672Z] {"sensor_id":"1","sensor_temp":61,"sensor_status":"FAIL"}
[2021-08-28T11:41:26.674Z] {"sensor_id":"80","sensor_temp":63,"sensor_status":"WARN"}
[2021-08-28T11:41:26.676Z] {"sensor_id":"33","sensor_temp":75,"sensor_status":"FAIL"}
[2021-08-28T11:41:26.678Z] {"sensor_id":"77","sensor_temp":77,"sensor_status":"OK"}
[2021-08-28T11:41:26.679Z] {"sensor_id":"83","sensor_temp":31,"sensor_status":"OK"}
[2021-08-28T11:41:26.680Z] {"sensor_id":"35","sensor_temp":95,"sensor_status":"FAIL"}
[2021-08-28T11:41:26.681Z] {"sensor_id":"80","sensor_temp":39,"sensor_status":"OK"}
[2021-08-28T11:41:26.683Z] {"sensor_id":"52","sensor_temp":97,"sensor_status":"WARN"}
[2021-08-28T11:41:26.685Z] {"sensor_id":"88","sensor_temp":37,"sensor_status":"FAIL"}
[2021-08-28T11:41:26.686Z] {"sensor_id":"84","sensor_temp":88,"sensor_status":"OK"}
[2021-08-28T11:41:26.687Z] Function "EventHubTrigger" (Id: 63516233-49ca-49fe-8631-31fc7a398db4) invoked by Java Worker
[2021-08-28T11:41:26.689Z] Executed 'Functions.EventHubTrigger' (Succeeded, Id=63516233-49ca-49fe-8631-31fc7a398db4, Duration=27ms)
[2021-08-28T11:41:26.959Z] Executing 'Functions.EventHubTrigger' (Reason='(null)', Id=166bf77b-1b32-4246-a729-3d4fa948279c)
[2021-08-28T11:41:26.963Z] Length:9
[2021-08-28T11:41:26.965Z] {"sensor_id":"79","sensor_temp":91,"sensor_status":"FAIL"}
[2021-08-28T11:41:26.966Z] {"sensor_id":"45","sensor_temp":25,"sensor_status":"OK"}
[2021-08-28T11:41:26.967Z] {"sensor_id":"65","sensor_temp":59,"sensor_status":"WARN"}
[2021-08-28T11:41:26.968Z] {"sensor_id":"32","sensor_temp":67,"sensor_status":"OK"}
[2021-08-28T11:41:26.969Z] {"sensor_id":"60","sensor_temp":27,"sensor_status":"WARN"}
[2021-08-28T11:41:26.971Z] {"sensor_id":"19","sensor_temp":23,"sensor_status":"OK"}
[2021-08-28T11:41:26.973Z] {"sensor_id":"88","sensor_temp":34,"sensor_status":"WARN"}
[2021-08-28T11:41:26.975Z] {"sensor_id":"97","sensor_temp":79,"sensor_status":"FAIL"}
[2021-08-28T11:41:26.976Z] {"sensor_id":"62","sensor_temp":51,"sensor_status":"FAIL"}
[2021-08-28T11:41:26.977Z] Function "EventHubTrigger" (Id: 166bf77b-1b32-4246-a729-3d4fa948279c) invoked by Java Worker
[2021-08-28T11:41:26.979Z] Executed 'Functions.EventHubTrigger' (Succeeded, Id=166bf77b-1b32-4246-a729-3d4fa948279c, Duration=19ms)
```

カンタンに Azure Event Hubs に対してイベントを送信できました！

## おわりに

今回は、EventHubDataGenerator を使って簡単に Azure Event Hubs に対してイベントを送信する方法をご紹介しました。ぜひぜひ皆さんも検証時に活用してみてください！

## 参考

- [JalalUddin7/EventHubDataGenerator](https://github.com/JalalUddin7/EventHubDataGenerator)