Daprは、移植可能なイベントドリブンランタイムです。これにより、開発者はクラウドやエッジ環境で柔軟性のある無状態や有状態アプリケーションを簡単に構築できます。Daprは、サービス間通信、状態管理、パブリッシュ/サブスクライブ、シークレット管理などの共通APIを提供します。これにより、開発者はビジネスロジックに集中し、分散システムの課題を気にせず生産性を向上させることができます。
関連リンク
- DaprとNestJSの実践ガイド:Pub&Subデコレーターの作成
- Node.jsによるDaprのクラウドネイティブマイクロサービスアプリケーションの構築ガイド
- DockerComposeとNestJSを用いたDaprベースのRedisパブリッシュ/サブスクライブ分散アプリケーションの構築
JavaScriptSDKのインストール
DaprのJavaScriptSDKを開始するには、次のnpmコマンドを実行します:
npm install --save @dapr/dapr
SDKのインポート方法:
import { DaprClient, DaprServer, HttpMethod, CommunicationProtocolEnum } from "@dapr/dapr";
const dapriHost = "127.0.0.1"; // Daprサイドカーのホスト
const dapriPort = "3500"; // Daprサイドカーのポート
const serverHost = "127.0.0.1"; // アプリケーションのホスト
const serverPort = "50051"; // アプリケーションのポート
// HTTP通信例
const client = new DaprClient(dapriHost, dapriPort);
// gRPC通信例
const client = new DaprClient(dapriHost, dapriPort, CommunicationProtocolEnum.GRPC);
通信プロトコルの選択
Daprサイドカーとの通信はHTTP(デフォルト)またはgRPCを使用できます。
HTTP通信
import { DaprClient } from "@dapr/dapr";
const client = new DaprClient(dapriHost, dapriPort);
# Dapr(runコマンド)
dapr run --app-id sample-sdk --app-protocol http -- npm run start
# npmスクリプト例
npm run start:dapr-http
gRPC通信
gRPCを使用するには、通信プロトコルを明示的に指定します:
import { DaprClient, CommunicationProtocol } from "@dapr/dapr";
const client = new DaprClient(dapriHost, dapriPort, CommunicationProtocol.GRPC);
# Dapr(runコマンド)
dapr run --app-id sample-sdk --app-protocol grpc -- npm run start
# npmスクリプト例
npm run start:dapr-grpc
サービス間通信
Daprの呼び出しAPIを使用して他のサービスを呼び出すことができます。
import { DaprClient, HttpMethod } from "@dapr/dapr";
const dapriHost = "127.0.0.1";
const dapriPort = "3500";
async function start() {
const client = new DaprClient(dapriHost, dapriPort);
const targetAppId = "my-service-id";
const targetMethod = "greeting";
// POSTリクエスト
const responsePost = await client.invoker.invoke(targetAppId, targetMethod, HttpMethod.POST, { message: "Hello" });
// GETリクエスト
const responseGet = await client.invoker.invoke(targetAppId, targetMethod, HttpMethod.GET);
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
状態管理
アプリケーションの状態を保存、取得、削除します。
import { DaprClient } from "@dapr/dapr";
const dapriHost = "127.0.0.1";
const dapriPort = "3500";
async function manageState() {
const client = new DaprClient(dapriHost, dapriPort);
const storeName = "my-state-store";
// 状態の保存
await client.state.save(storeName, [
{ key: "status1", value: "active" },
{ key: "status2", value: "inactive" }
]);
// 状態の取得
const state = await client.state.get(storeName, "status1");
console.log(state);
// 状態のまとめて取得
const bulkState = await client.state.getBulk(storeName, ["status1", "status2"]);
console.log(bulkState);
// 状態のトランザクション操作
await client.state.transaction(storeName, [
{ operation: "upsert", request: { key: "status3", value: "new" } },
{ operation: "delete", request: { key: "status2" } }
]);
}
パブリッシュ/サブスクライブ
イベントを Publish または Subscribe します。
Publish例
import { DaprClient } from "@dapr/dapr";
const dapriHost = "127.0.0.1";
const dapriPort = "3500";
async function publishEvent() {
const client = new DaprClient(dapriHost, dapriPort);
const pubSubName = "my-pubsub";
const topic = "event-topic";
const message = { data: "event-data" };
await client.pubsub.publish(pubSubName, topic, message);
}
Subscribe例
import { DaprServer } from "@dapr/dapr";
const dapriHost = "127.0.0.1";
const dapriPort = "3500";
const serverHost = "127.0.0.1";
const serverPort = "50051";
async function subscribeEvent() {
const server = new DaprServer(serverHost, serverPort, dapriHost, dapriPort);
const pubSubName = "my-pubsub";
const topic = "event-topic";
await server.pubsub.subscribe(pubSubName, topic, (data) => {
console.log(`受信データ: ${JSON.stringify(data)}`);
});
await server.start();
}
アクター機能
Daprのアクター機能を使用して仮想アクターとやりとりします。
アクターインターフェース
export interface ParkingSensorActor {
carEnter(): Promise<void>;
carLeave(): Promise<void>;
}
アクター実装
import { AbstractActor } from "@dapr/dapr";
export class ParkingSensorActorImpl extends AbstractActor implements ParkingSensorActor {
async carEnter(): Promise<void> {
// 停車場の状態を更新する処理
}
async carLeave(): Promise<void> {
// 停車場の状態を復元する処理
}
async onActivate(): Promise<void> {
// アクター起動時の初期化処理
}
}
アクターの登録
import { DaprServer } from "@dapr/dapr";
const dapriHost = "127.0.0.1";
const dapriPort = "50000";
const serverHost = "127.0.0.1";
const serverPort = "50001";
async function registerActor() {
const server = new DaprServer(serverHost, serverPort, dapriHost, dapriPort);
await server.actor.init();
server.actor.registerActor(ParkingSensorActorImpl);
await server.start();
}
アクターの呼び出し
import { DaprClient, ActorId } from "@dapr/dapr";
const dapriHost = "127.0.0.1";
const dapriPort = "50000";
const actorId = new ActorId("sensor-123");
const client = new DaprClient(dapriHost, dapriPort);
const builder = new ActorProxyBuilder<ParkingSensorActor>(ParkingSensorActorImpl, client);
const actor = builder.build(actorId);
await actor.carEnter();
日志設定
SDKは内部動作をログで表示します。ログレベルを設定したり、独自のログサービスを実装したりできます。
ログレベルの設定
import { CommunicationProtocolEnum, DaprClient, LogLevel } from "@dapr/dapr";
const client = new DaprClient(
dapriHost,
dapriPort,
CommunicationProtocolEnum.HTTP,
{ logger: { level: LogLevel.Verbose } }
);
独自のログサービス
import { LoggerService } from "@dapr/dapr";
import winston from 'winston';
class CustomLogger implements LoggerService {
private logger;
constructor() {
this.logger = winston.createLogger({
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'app.log' })
]
});
}
error(message: any, ...params: any[]): void {
this.logger.error(message, ...params);
}
warn(message: any, ...params: any[]): void {
this.logger.warn(message, ...params);
}
info(message: any, ...params: any[]): void {
this.logger.info(message, ...params);
}
verbose(message: any, ...params: any[]): void {
this.logger.verbose(message, ...params);
}
debug(message: any, ...params: any[]): void {
this.logger.debug(message, ...params);
}
}
SDKに独自のログサービスを設定します:
const customLogger = new CustomLogger();
const client = new DaprClient(
dapriHost,
dapriPort,
CommunicationProtocolEnum.HTTP,
{ logger: { level: LogLevel.Info, service: customLogger } }
);