NodeJS & Dapr Javascript SDK 官方使用指南

2022-08-05 12:00:31

Dapr 是一個可移植的、事件驅動的執行時,它使任何開發人員能夠輕鬆構建出彈性的、無狀態和有狀態的應用程式,並可執行在雲平臺或邊緣計算中,它同時也支援多種程式語言和開發框架。Dapr 確保開發人員專注於編寫業務邏輯,不必分神解決分散式系統難題,從而顯著提高了生產力。Dapr 降低了構建微服務架構類現代雲原生應用的門檻。

系列

JavaScript

用於在 JavaScript 和 TypeScript 中構建 Dapr 應用程式的使用者端庫。該使用者端抽象了公共 Dapr API,例如服務到服務呼叫、狀態管理、釋出/訂閱、機密等,併為構建應用程式提供了一個簡單、直觀的 API。

安裝

要開始使用 Javascript SDK,請從 NPM 安裝 Dapr JavaScript SDK 包:

npm install --save @dapr/dapr

⚠️ dapr-client 現在已棄用。 請參閱#259 瞭解更多資訊。

結構

Dapr Javascript SDK 包含兩個主要元件:

  • DaprServer: 管理所有 Dapr sidecar 到應用程式的通訊。
  • DaprClient: 管理所有應用程式到 Dapr sidecar 的通訊。

上述通訊可以設定為使用 gRPC 或 HTTP 協定。

Client

介紹

Dapr Client 允許您與 Dapr Sidecar 通訊並存取其面向使用者端的功能,例如釋出事件、呼叫輸出繫結、狀態管理、Secret 管理等等。

前提條件

安裝和匯入 Dapr 的 JS SDK

  1. 使用 npm 安裝 SDK:
npm i @dapr/dapr --save
  1. 匯入庫:
import { DaprClient, DaprServer, HttpMethod, CommunicationProtocolEnum } from "@dapr/dapr";

const daprHost = "127.0.0.1"; // Dapr Sidecar Host
const daprPort = "3500"; // Dapr Sidecar Port of this Example Server
const serverHost = "127.0.0.1"; // App Host of this Example Server
const serverPort = "50051"; // App Port of this Example Server 

// HTTP Example
const client = new DaprClient(daprHost, daprPort);

// GRPC Example
const client = new DaprClient(daprHost, daprPort, CommunicationProtocolEnum.GRPC);

執行

要執行範例,您可以使用兩種不同的協定與 Dapr sidecar 互動:HTTP(預設)或 gRPC。

使用 HTTP(預設)

import { DaprClient } from "@dapr/dapr";
const client = new DaprClient(daprHost, daprPort);
# Using dapr run
dapr run --app-id example-sdk --app-protocol http -- npm run start

# or, using npm script
npm run start:dapr-http

使用 gRPC

由於 HTTP 是預設設定,因此您必須調整通訊協定以使用 gRPC。 您可以通過向用戶端或伺服器建構函式傳遞一個額外的引數來做到這一點。

import { DaprClient, CommunicationProtocol } from "@dapr/dapr";
const client = new DaprClient(daprHost, daprPort, CommunicationProtocol.GRPC);
# Using dapr run
dapr run --app-id example-sdk --app-protocol grpc -- npm run start

# or, using npm script
npm run start:dapr-grpc

代理請求

通過代理請求,我們可以利用 Dapr 通過其 sidecar 架構帶來的獨特功能,例如服務發現、紀錄檔記錄等,使我們能夠立即「升級」我們的 gRPC 服務。 gRPC 代理的這一特性在community call 41 中得到了展示。

community call 41

https://www.youtube.com/watch?v=B_vkXqptpXY&t=71s

建立代理

要執行 gRPC 代理,只需呼叫 client.proxy.create() 方法建立一個代理:

// As always, create a client to our dapr sidecar
// this client takes care of making sure the sidecar is started, that we can communicate, ...
const clientSidecar = new DaprClient(daprHost, daprPort, CommunicationProtocolEnum.GRPC);

// Create a Proxy that allows us to use our gRPC code
const clientProxy = await clientSidecar.proxy.create<GreeterClient>(GreeterClient);

我們現在可以呼叫 GreeterClient 介面中定義的方法(在本例中來自 Hello World 範例)

幕後(技術工作)

  1. gRPC 服務在 Dapr 中啟動。 我們通過 --app-port 告訴 Dapr 這個 gRPC 伺服器在哪個埠上執行,並通過 --app-id <APP_ID_HERE> 給它一個唯一的 Dapr 應用 ID
  2. 我們現在可以通過將連線到 Sidecar 的使用者端呼叫 Dapr Sidecar
  3. 在呼叫 Dapr Sidecar 時,我們提供了一個名為 dapr-app-id 的後設資料鍵,其中包含在 Dapr 中啟動的 gRPC 伺服器的值(例如,我們範例中的 server
  4. Dapr 現在會將呼叫轉發到設定的 gRPC 伺服器

構建塊

JavaScript 使用者端 SDK 允許您與專注於 Client to Sidecar 功能的所有 Dapr 構建塊進行互動。

呼叫 API

呼叫一個服務

import { DaprClient, HttpMethod } from "@dapr/dapr"; 

const daprHost = "127.0.0.1"; 
const daprPort = "3500"; 

async function start() {
  const client = new DaprClient(daprHost, daprPort); 

  const serviceAppId = "my-app-id";
  const serviceMethod = "say-hello";
  
  // POST Request
  const response = await client.invoker.invoke(serviceAppId , serviceMethod , HttpMethod.POST, { hello: "world" });

  // GET Request
  const response = await client.invoker.invoke(serviceAppId , serviceMethod , HttpMethod.GET);
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});
有關服務呼叫的完整指南,請存取 How-To: Invoke a service。

https://docs.dapr.io/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services/

狀態管理 API

儲存、獲取和刪除應用程式狀態

import { DaprClient } from "@dapr/dapr"; 

const daprHost = "127.0.0.1"; 
const daprPort = "3500"; 

async function start() {
  const client = new DaprClient(daprHost, daprPort); 

  const serviceStoreName = "my-state-store-name";

  // Save State
  const response = await client.state.save(serviceStoreName, [
    {
      key: "first-key-name",
      value: "hello"
    },
    {
      key: "second-key-name",
      value: "world"
    }
  ]);

  // Get State
  const response = await client.state.get(serviceStoreName, "first-key-name");

  // Get Bulk State
  const response = await client.state.getBulk(serviceStoreName, ["first-key-name", "second-key-name"]);

  // State Transactions
  await client.state.transaction(serviceStoreName, [
    {
      operation: "upsert",
      request: {
        key: "first-key-name",
        value: "new-data"
      }
    },
    {
      operation: "delete",
      request: {
        key: "second-key-name"
      }
    }
  ]);

  // Delete State
  const response = await client.state.delete(serviceStoreName, "first-key-name");
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});
有關狀態操作的完整列表,請存取 How-To: Get & save state。

https://docs.dapr.io/developing-applications/building-blocks/state-management/howto-get-save-state/

查詢狀態 API

import { DaprClient } from "@dapr/dapr";

async function start() {
  const client = new DaprClient(daprHost, daprPort);

  const res = await client.state.query("state-mongodb", {
    filter: {
      OR: [
        {
          EQ: { "person.org": "Dev Ops" }
        },
        {
          "AND": [
            {
              "EQ": { "person.org": "Finance" }
            },
            {
              "IN": { "state": ["CA", "WA"] }
            }
          ]
        }
      ]
    },
    sort: [
      {
        key: "state",
        order: "DESC"
      }
    ],
    page: {
      limit: 10
    }
  });

  console.log(res);
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});

釋出訂閱 API

釋出訊息

import { DaprClient } from "@dapr/dapr"; 

const daprHost = "127.0.0.1"; 
const daprPort = "3500"; 

async function start() {
  const client = new DaprClient(daprHost, daprPort); 

  const pubSubName = "my-pubsub-name";
  const topic = "topic-a";
  const message = { hello: "world" }

  // Publish Message to Topic
  const response = await client.pubsub.publish(pubSubName, topic, message);
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});

訂閱訊息

import { DaprServer } from "@dapr/dapr";

const daprHost = "127.0.0.1"; // Dapr Sidecar Host
const daprPort = "3500"; // Dapr Sidecar Port of this Example Server
const serverHost = "127.0.0.1"; // App Host of this Example Server
const serverPort = "50051"; // App Port of this Example Server "

async function start() {
  const server = new DaprServer(serverHost, serverPort, daprHost, daprPort);

  const pubSubName = "my-pubsub-name";
  const topic = "topic-a";

  // Configure Subscriber for a Topic
  await server.pubsub.subscribe(pubSubName, topic, async (data: any) => console.log(`Got Data: ${JSON.stringify(data)}`));

  await server.start();
}
有關狀態操作的完整列表,請存取 How-To: Publish and subscribe。

https://docs.dapr.io/developing-applications/building-blocks/pubsub/howto-publish-subscribe/

繫結 API

呼叫輸出繫結

import { DaprClient } from "@dapr/dapr"; 

const daprHost = "127.0.0.1"; 
const daprPort = "3500"; 

async function start() {
  const client = new DaprClient(daprHost, daprPort); 

  const bindingName = "my-binding-name";
  const bindingOperation = "create";
  const message = { hello: "world" };

  const response = await client.binding.send(bindingName, bindingOperation, message);
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});
有關輸出繫結的完整指南,請存取 How-To: Use bindings。

https://docs.dapr.io/developing-applications/building-blocks/bindings/howto-bindings/

Secret API

檢索 secret

import { DaprClient } from "@dapr/dapr"; 

const daprHost = "127.0.0.1"; 
const daprPort = "3500"; 

async function start() {
  const client = new DaprClient(daprHost, daprPort); 

  const secretStoreName = "my-secret-store";
  const secretKey = "secret-key";

  // Retrieve a single secret from secret store
  const response = await client.secret.get(secretStoreName, secretKey);

  // Retrieve all secrets from secret store
  const response = await client.secret.getBulk(secretStoreName);
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});
有關 secrets 的完整指南,請存取 How-To: Retrieve Secrets。

https://docs.dapr.io/developing-applications/building-blocks/secrets/howto-secrets/

設定 API

獲取設定 key

import { DaprClient } from "@dapr/dapr";

const daprHost = "127.0.0.1";
const daprAppId = "example-config";

async function start() {

  const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT);

  const config = await client.configuration.get('config-store', ['key1', 'key2']);
  console.log(config);
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});

Server

介紹

Dapr Server 將允許您接收來自 Dapr Sidecar 的通訊並存取其面向伺服器的功能,例如:訂閱事件、接收輸入繫結等等。

前提條件

安裝和匯入 Dapr 的 JS SDK

  1. 使用 npm 安裝 SDK:
npm i @dapr/dapr --save
  1. 匯入庫:
import { DaprClient, DaprServer, HttpMethod, CommunicationProtocolEnum } from "@dapr/dapr";

const daprHost = "127.0.0.1"; // Dapr Sidecar Host
const daprPort = "3500"; // Dapr Sidecar Port of this Example Server
const serverHost = "127.0.0.1"; // App Host of this Example Server
const serverPort = "50051"; // App Port of this Example Server 

// HTTP Example
const client = new DaprClient(daprHost, daprPort);

// GRPC Example
const client = new DaprClient(daprHost, daprPort, CommunicationProtocolEnum.GRPC);

執行

要執行範例,您可以使用兩種不同的協定與 Dapr sidecar 互動:HTTP(預設)或 gRPC。

使用 HTTP(預設)

import { DaprServer } from "@dapr/dapr";

const server= new DaprServer(appHost, appPort, daprHost, daprPort);
// initialize subscribtions, ... before server start
// the dapr sidecar relies on these
await server.start(); 
# Using dapr run
dapr run --app-id example-sdk --app-port 50051 --app-protocol http -- npm run start

# or, using npm script
npm run start:dapr-http
ℹ️ Note:這裡需要 app-port,因為這是我們的伺服器需要繫結的地方。 Dapr 將在完成啟動之前檢查應用程式是否繫結到此埠。

使用 gRPC

由於 HTTP 是預設設定,因此您必須調整通訊協定以使用 gRPC。 您可以通過向用戶端或伺服器建構函式傳遞一個額外的引數來做到這一點。

import { DaprServer, CommunicationProtocol } from "@dapr/dapr";

const server = new DaprServer(appHost, appPort, daprHost, daprPort, CommunicationProtocol.GRPC);
// initialize subscribtions, ... before server start
// the dapr sidecar relies on these
await server.start(); 
# Using dapr run
dapr run --app-id example-sdk --app-port 50051 --app-protocol grpc -- npm run start

# or, using npm script
npm run start:dapr-grpc
ℹ️ Note:這裡需要 app-port,因為這是我們的伺服器需要繫結的地方。 Dapr 將在完成啟動之前檢查應用程式是否繫結到此埠。

構建塊

JavaScript Server SDK 允許您與專注於 Sidecar 到 App 功能的所有 Dapr 構建塊進行互動。

呼叫 API

監聽呼叫

import { DaprServer } from "@dapr/dapr";

const daprHost = "127.0.0.1"; // Dapr Sidecar Host
const daprPort = "3500"; // Dapr Sidecar Port of this Example Server
const serverHost = "127.0.0.1"; // App Host of this Example Server
const serverPort = "50051"; // App Port of this Example Server "

async function start() {
  const server = new DaprServer(serverHost, serverPort, daprHost, daprPort);

  await server.invoker.listen('hello-world', mock, { method: HttpMethod.GET });

  // You can now invoke the service with your app id and method "hello-world"

  await server.start();
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});
有關服務呼叫的完整指南,請存取 How-To: Invoke a service。

https://docs.dapr.io/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services/

釋出訂閱 API

訂閱訊息

import { DaprServer } from "@dapr/dapr";

const daprHost = "127.0.0.1"; // Dapr Sidecar Host
const daprPort = "3500"; // Dapr Sidecar Port of this Example Server
const serverHost = "127.0.0.1"; // App Host of this Example Server
const serverPort = "50051"; // App Port of this Example Server "

async function start() {
  const server = new DaprServer(serverHost, serverPort, daprHost, daprPort);

  const pubSubName = "my-pubsub-name";
  const topic = "topic-a";

  // Configure Subscriber for a Topic
  await server.pubsub.subscribe(pubSubName, topic, async (data: any) => console.log(`Got Data: ${JSON.stringify(data)}`));

  await server.start();
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});
有關狀態操作的完整列表,請存取 How-To: Publish and subscribe。

https://docs.dapr.io/developing-applications/building-blocks/pubsub/howto-publish-subscribe/

繫結 API

接收一個輸入繫結

import { DaprServer } from "@dapr/dapr";

const daprHost = "127.0.0.1"; 
const daprPort = "3500"; 
const serverHost = "127.0.0.1";
const serverPort = "5051";

async function start() {
  const server = new DaprServer(serverHost, serverPort, daprHost, daprPort);

  const bindingName = "my-binding-name";

  const response = await server.binding.receive(bindingName, async (data: any) => console.log(`Got Data: ${JSON.stringify(data)}`));

  await server.start();
}

start().catch((e) => {
  console.error(e);
  process.exit(1);
});
有關輸出繫結的完整指南,請存取 How-To: Use bindings。

https://docs.dapr.io/developing-applications/building-blocks/bindings/howto-bindings/

設定 API