Debezium 毎日の共有シリーズ: Debezium シグナルの送信と通知をカスタマイズする
Debezium 2.3 では、シグナルおよび通知機能に新たな改善が導入されています。Debezium によって提供される事前定義されたシグナルおよび通知チャネルに加えて、新しいシグナルおよび通知チャネルをセットアップすることもできます。この機能により、ユーザーは独自のニーズに合わせてシステムをカスタマイズし、既存のインフラストラクチャまたはサードパーティのソリューションと統合することができます。信号イベントを正確にキャプチャして通信し、優先チャネルを通じて通知をトリガーすることで、効果的なモニタリングとデータ変更へのプロアクティブな対応が可能になります。
Debezium 毎日の共有シリーズ: Debezium のシグナリングと通知 - パート 1
1. カスタム信号と通知チャネル
Debezium では、特定の要件を満たすようにシグナル チャネルと通知チャネルをカスタマイズできます。たとえば、シグナルと通知用の HTTP チャネルを作成することでカスタマイズを実現できます。この HTTP チャネルは http エンドポイントからシグナルを受信し、シグナルが配信された後にエンドポイントに通知を送り返すことができます。
Debezium Postgres コネクタ、シグナルを送信するモック サーバー、および http エンドポイント経由で通知を受信する Postbin を使用して、HTTP シグナリングおよび通知チャネルを作成および利用する方法を示す例を見てみましょう。
HTTP 信号チャネルを設定します。
- 関連するデータベースの変更が発生したときにシグナルを受信するように Debezium Postgres コネクタを構成します。
- HTTP チャネルを使用して Debezium にシグナルを送信するサービスをセットアップします。サービスは、データベース、サードパーティ アプリケーション、または http リクエストを送信できるその他のシステムである可能性があります。この例では、模擬サーバーを使用して Debezium にシグナルを送信します。モック サーバーは、http リクエストとレスポンスをモックするために使用できるサービスです。
- 適切な HTTP メソッド (POST など) を使用して http エンドポイント経由で信号を送信するようにモック サーバーを構成します。
- 必要に応じて HTTP チャネル設定をカスタマイズし、http エンドポイント URL、認証、ヘッダー、その他のパラメーターを定義します。
HTTP 通知チャネルを設定します。
- Debezium がシグナルを受信して処理すると、http エンドポイントへの通知をトリガーできます。この例では、HTTP チャネルを使用して Postbin ビンに通知を送信します。Postbin は、http リクエストを受信し、リクエストの詳細を表示するために使用できるサービスです。
- 通知用の HTTP チャネル設定をカスタマイズし、Postbin にビンを作成し、必要に応じて http エンドポイント URL、認証、ヘッダー、およびその他のパラメーターを定義します。
- 適切な HTTP メソッド (POST など) を使用して、通知イベントを http エンドポイントである Postbin bin に転送します。通知ペイロードは必要に応じてカスタマイズできます。
ブログ投稿にあるこのサンプルの完全なソース コードは、Debezium サンプル リポジトリの http-signal-notification ディレクトリで入手できます。
Java プロジェクトを作成して、HTTP シグナリングおよび通知チャネルを構築します。次のコマンドを実行して、Maven を使用して新しい Java プロジェクトを作成します。
mvn archetype:generate
-DgroupId=io.debezium.examples
-DartifactId=http-signaling-notification
Debezium バージョン (2.3 以降) の pom.xml ファイルに次の依存関係を追加します。
<dependency>
<groupId>io.debezium</groupId>
<artifactId>debezium-core</artifactId>
<version>2.3.0.Final</version>
</dependency>
モック サーバーでシグナルを受信するには、モック サーバー サービスを定義する Docker Compose ファイルを作成します。モック サーバー サービスは次のように構成されます。
services:
mockServer:
image: mockserver/mockserver:latest
ports:
- 1080:1080
environment:
- MOCKSERVER_WATCH_INITIALIZATION_JSON=true
- MOCKSERVER_INITIALIZATION_JSON_PATH=/config/initializerJson.json
volumes:
- ./initializerJson.json:/config/initializerJson.json
環境変数 MOCKSERVER_WATCH_INITIALIZATION_JSON および MOCKSERVER_INITIALIZATION_JSON_PATH を設定して、モック サーバーが初期化 JSON ファイルの変更を監視し、そのパスを指定できるようにします。シグナルの http リクエストおよびレスポンス情報を含む initializerJson.json ファイルが、モック サーバー コンテナーにインストールされます。
initializerJson.json ファイルは、クエリ文字列パラメーター code=10969 を使用して、パス /api/signal へのシミュレートされた http リクエストを定義します。モックサーバーがこのリクエストを受信すると、ID、タイプ、データを含む JSON 本文で応答します。応答のステータス コードは 200 で、応答が成功したことを示します。InitializerJson.json ファイルは次のように定義されます。
[
{
"httpRequest" : {
"method" : "GET",
"path" : "/api/signal",
"queryStringParameters" : {
"code" : ["10969"]
}
},
"httpResponse" : {
"body": "{
\"id\":\"924e3ff8-2245-43ca-ba77-2af9af02fa07\",\"type\":\"log\",\"data\":{
\"message\": \"Signal message received from http endpoint.\"}}",
"statusCode": 200
}
}
]
- id : 信号インスタンスを識別する任意の一意の文字列。
- type : 送信する信号のタイプ。この例では、タイプはログであり、コネクタがコネクタのログ ファイルにエントリを追加するように要求します。信号の処理後、コネクタは指定されたメッセージをログに出力します。
- data : シグナル イベントに渡される JSON 形式のパラメーター。この例では、メッセージ パラメーターがシグナル イベントに渡されます。
次のように SignalChannelReader インターフェイスを実装して、HTTP 信号チャネルを作成します。
public class HttpSignalChannel implements SignalChannelReader {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpSignalChannel.class);
public static final String CHANNEL_NAME = "http";
private static final List<SignalRecord> SIGNALS = new ArrayList<>();
public CommonConnectorConfig connectorConfig;
@Override
public String name() {
(1)
return CHANNEL_NAME;
}
@Override
public void init(CommonConnectorConfig connectorConfig) {
(2)
this.connectorConfig = connectorConfig;
}
@Override
public List<SignalRecord> read() {
(3)
try {
String requestUrl = "http://mockServer:1080/api/signal?code=10969";
// send http request to the mock server
HttpClient httpClient = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(requestUrl))
.GET()
.header("Content-Type", "application/json")
.build();
// read the response
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
ObjectMapper mapper = new ObjectMapper();
String responseBody = response.body();
// parse the response body
JsonNode signalJson = mapper.readTree(responseBody);
Map<String, Object> additionalData = signalJson.has("additionalData") ? mapper.convertValue(signalJson.get("additionalData"), new TypeReference<>() {
}) : new HashMap<>();
String id = signalJson.get("id").asText();
String type = signalJson.get("type").asText();
String data = signalJson.get("data").toString();
SignalRecord signal = new SignalRecord(id, type, data, additionalData);
LOGGER.info("Recorded signal event '{}' ", signal);
// process the signal
SIGNALS.add(signal);
} else {
LOGGER.warn("Error while reading signaling events from endpoint: {}", response.statusCode());
}
} catch (IOException | InterruptedException e) {
LOGGER.warn("Exception while preparing to process the signal '{}' from the endpoint", e.getMessage());
e.printStackTrace();
}
return SIGNALS;
}
@Override
public void close() {
(4)
SIGNALS.clear();
}
}
- name() メソッドは信号チャネルの名前を返します。Debezium がチャネルを使用できるようにするには、コネクタの signal.enabled.channels プロパティで名前 http を指定します。
- init() メソッドを使用すると、http チャネルに必要な特定の構成、変数、または接続を初期化できます。
- read() メソッドは、http エンドポイントからシグナルを読み取り、Debezium コネクタによって処理される SignalRecord オブジェクトのリストを返します。
- close() メソッドは、割り当てられたすべてのリソースを閉じます。
次のように、NotificationChannel インターフェイスを実装して、通知チャネルを作成します。
public class HttpNotificationChannel implements NotificationChannel {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpNotificationChannel.class);
public static final String CHANNEL_NAME = "http";
private static final String NOTIFICATION_PREFIX = "[HTTP NOTIFICATION SERVICE]";
@Override
public String name() {
(1)
return CHANNEL_NAME;
}
@Override
public void init(CommonConnectorConfig config) {
(2)
// custom configuration
}
@Override
public void send(Notification notification) {
(3)
LOGGER.info(String.format("%s Sending notification to http channel", NOTIFICATION_PREFIX));
String binId = createBin();
sendNotification(binId, notification);
}
private static String createBin() {
// Create a bin on the server
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://www.toptal.com/developers/postbin/api/bin"))
.POST(HttpRequest.BodyPublishers.ofString(" "))
.build();
HttpClient httpClient = HttpClient.newHttpClient();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == HTTP_CREATED) {
String binId = response.body().replaceAll(".*\"binId\":\"([^\"]+)\".*", "$1");
LOGGER.info("Bin created: " + response.body());
return binId;
}
} catch (URISyntaxException | InterruptedException | IOException e) {
throw new RuntimeException(e);
}
return null;
}
private static void sendNotification (String binId, Notification notification) {
// Get notification from the bin
try {
ObjectMapper mapper = new ObjectMapper();
String notificationString = mapper.writeValueAsString(notification);
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://www.toptal.com/developers/postbin/" + binId))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(notificationString))
.build();
HttpClient httpClient = HttpClient.newHttpClient();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == HTTP_OK) {
LOGGER.info("Notification received : " + response.body());
}
} catch (URISyntaxException | InterruptedException | IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void close() {
(4)
}
}
- name() メソッドは、通知チャネルの名前を返します。Debezium がチャネルを使用できるようにするには、コネクタの notification.enabled.channels プロパティで http を指定します。
- init() メソッドを使用すると、チャネルに必要な特定の構成、変数、または接続を初期化できます。
- send() メソッドはチャネルに通知を送信します。通知には、Debezium コネクタによって処理される SignalRecord オブジェクトが含まれています。
- close() メソッドは、割り当てられたすべてのリソースを閉じます。
META-INF/services ディレクトリの io.debezium.pipeline.signal.SignalChannelReader および io.debezium.pipeline.notification.channels.NotificationChannel ファイルで、HTTP シグナルと通知チャネルをそれぞれ宣言します。
Java プロジェクトをコンパイルし、JAR ファイルとしてエクスポートします。これは、Maven またはお気に入りのビルド ツールを使用して実行できます。JAR ファイルを、Debezium コネクタが使用する JAR ファイルが含まれるディレクトリにコピーします。たとえば、Debezium Postgres コネクタでカスタムのシグナルおよび通知チャネルを使用する場合は、JAR ファイルを /kafka/connect/debezium-connector-postgres ディレクトリにコピーします。
この例では、Mock Server、Zookeeper、Kafka Connect、Postgres データベースなどの必要なサービスを定義する Docker Compose ファイルを提供します。
サービスを開始するには、次のコマンドを実行します。
export DEBEZIUM_VERSION=2.3
docker-compose up -d
サービスが稼働中であり、Postgres データベースが接続を受け入れる準備ができていることを確認したら、次のステップはコネクタを登録することです。これには、コネクタ構成ファイルの作成が含まれます。次のプロパティを持つ register-postgres.json というファイルを作成しましょう。
{
"name": "inventory-connector",
"config": {
"connector.class": "io.debezium.connector.postgresql.PostgresConnector",
"tasks.max": 1,
"database.hostname": "postgres",
"database.port": 5432,
"database.user": "postgres",
"database.password": "postgres",
"database.dbname" : "postgres",
"topic.prefix": "dbserver1",
"schema.include.list": "inventory",
"signal.enabled.channels": "http", 1
"notification.enabled.channels": "http" 2
}
}
- signal.enabled.channels プロパティは、コネクタが使用する信号チャネルを指定します。この場合、コネクタは http シグナリング チャネルを使用します。
- notification.enabled.channels プロパティは、コネクタが使用する通知チャネルを指定します。この場合、コネクタは http 通知チャネルを使用します。
コネクタ構成ファイルの準備ができたので、次のコマンドを実行してコネクタを Kafka Connect に登録できます。
curl -i -X POST -H "Accept:application/json" \
-H "Content-Type:application/json" http://localhost:8083/connectors/ \
-d @register-postgres.json
コネクタが正常に登録されたら、コネクタ ログを表示して信号イベントを観察できます。これらのログは、信号関連の情報を含む、コネクタの処理と進行状況に関する洞察を提供します。次のようなログ メッセージが表示されます。
Recorded signal event 'SignalRecord{id='924e3ff8-2245-43ca-ba77-2af9af02fa07', type='log', data='{
"message":"Signal message received from http endpoint."}', additionalData={}}' [io.debezium.examples.signal.HttpSignalChannel]
さらに、メールボックスに送信された通知イベントに関連するログ メッセージが表示される場合があります。例えば:
[HTTP NOTIFICATION SERVICE] Sending notification to http channel [io.debezium.examples.notification.HttpNotificationChannel]
Bin created: {
"binId":"1688742588469-1816775151528","now":1688742588470,"expires":1688744388470} [io.debezium.examples.notification.HttpNotificationChannel]
これは、一意の識別子 (binId) を持つビンの作成やその他の関連詳細などの通知イベントに関する情報を提供します。Postbin から通知イベントを取得するには、ログ メッセージから binId を取得し、それを使用して Postbin から対応する通知イベントを要求します。通知イベントを表示するには、URL: https://www.toptal.com/developers/postbin/b/:binId を使用して Postbin にアクセスします。URL の :binId を、コネクタ ログから取得した実際の binId に置き換えます。
Postbin に送信される通知イベントは次のようになります。
2. 結論
このチュートリアルでは、Debezium コネクタ用のカスタム シグナルおよび通知チャネルを作成する方法を検討しました。HTTP エンドポイントからシグナル イベントを受信するカスタム シグナル チャネルを作成しました。また、通知イベントを HTTP エンドポイントに送信するためのカスタム通知チャネルも作成しました。
Debezium の包括的なシグナリングおよび通知システムは、サードパーティのソリューションとシームレスに統合され、Debezium コネクタのステータスと進行状況をユーザーに常に知らせます。システムの拡張性により、ユーザーはカスタマイズされたニーズに合わせて信号および通知チャネルをカスタマイズできます。