Dapr(分散式應用程式執行時)介紹
Dapr 是一個可移植的、事件驅動的執行時,它使任何開發人員能夠輕鬆構建出彈性的、無狀態和有狀態的應用程式,並可執行在雲平臺或邊緣計算中,它同時也支援多種程式語言和開發框架。
Dapr 官網:https://dapr.io/
實戰 Dapr 的 Redis 釋出/訂閱應用
1. 建立專案
首先,我們將建立我們的專案根資料夾來託管我們將在後續步驟中建立的所有服務。
mkdir dapr-nestjs-redis-pub-sub
2. 建立 Dapr Placement 服務
由於我們將建立多個服務,我們將使用 docker-compose
來執行這些服務。
讓我們在專案的根資料夾中建立 docker-compose.yml
檔案
cd dapr-nestjs-redis-pub-sub
touch docker-compose.yml
version: "3.5"
services:
dapr-placement:
image: "daprio/dapr"
command: ["./placement", "-port", "50006"]
Dapr placement
服務將負責管理 Dapr actors
(我們的服務)之間的所有通訊。
簡單來說,它負責將所有通訊路由到假設接收通訊的相應 actor
。它充當 message broker
(訊息代理)。
3. 建立 Redis Publish 服務
讓我們繼續通過新增我們的 Redis
服務來修改我們的 docker-compose.yml
檔案。
將以下程式碼新增到 docker-compose.yml
的服務部分:
redis-publisher:
image: redis
restart: always
depends_on:
- dapr-placement
4. 建立 Dapr Pub-Sub 元件
建立一個 dapr/components
資料夾。然後建立元件檔案 redis-pubsub.yaml
。
mkdir -p dapr/components
cd dapr/components
touch redis-pubsub.yaml
然後開啟檔案並插入我們的 Dapr pub/sub
元件的詳細資訊
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: redis-pubsub
namespace: default
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: redis-publisher:6379
- name: redisPassword
value: ""
redisHost
是我們的 Redis
服務 redis-pub
的名稱,預設 Redis
埠為 6379
。
5. 建立 Redis Dapr Sidecar
正如前面部分反覆提到的,服務直接與 Dapr
通訊,而不是直接與其他服務通訊。Dapr
充當所有服務的中間人。
服務通過它們自己的 Dapr sidecar
直接與 Dapr
通訊,Dapr sidecar
將通訊傳遞給 Dapr placement
,該 placement
再次將其傳遞給假設接收通訊的服務的 Dapr sidecar
。
將 redis-dapr-sidecar
服務新增到我們的 docker-compose.yml
redis-dapr-sidecar:
image: "daprio/daprd:edge"
command: [
"./daprd",
"-app-id",
"redis-publisher",
"-app-port",
"6379",
"-dapr-http-port",
"5000",
"-components-path",
"/components",
"-placement-host-address",
"dapr-placement:50006"
]
volumes:
- "./dapr/components/:/components"
depends_on:
- redis-publisher
network_mode: "service:redis-publisher"
在這裡,我們使用 app-id
將 Dapr sidecar
分配給 redis-publisher
,同時我們使用 redis
埠 6379
。
我們還必須將 dapr/components(redis-pubsub.yaml)
資料夾掛載到 docker
容器中。
不要忘記宣告 dapr-http-port
。這是我們的 Dapr sidecar
的 api
,允許我們呼叫各種 HTTP
方法。
定義您的 dapr-http-port
很重要,因為您將在此處呼叫各種 HTTP 呼叫/方法/請求
。
最後,注意將 redis-dapr-sidecar
附加到 redis-publisher
網路名稱空間。
6. 建立 NestJS Server
我們將使用 NestJS
作為我們的 node server
作為我們的 Redis subscriber(訂閱者)
。
進入到專案資料夾
cd dapr-nestjs-redis-pub-sub
然後執行以下命令設定一個 NestJS node server
:
npm i -g @nestjs/cli
nest new nest-subscriber
對於這個專案,我們將選擇 yarn
作為包管理器。
接下來,我們將設定一個 post API
端點。
Dapr
將呼叫這個端點,一旦它收到我們的 Redis
服務釋出,它就被呼叫。
轉到 nest-subscriber/src/app.controller.ts
將此檔案中的程式碼替換為以下內容:
import { Controller, Post, Body } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Post('/redis-publisher')
async postRedisPub(@Body() reqBody) {
console.log(`Redis 釋出了 ${JSON.stringify(reqBody)} `);
return `NestJS 訂閱者收到的 ${reqBody} 釋出`;
}
}
7. 為 NestJS 訂閱伺服器建立 Dockerfile
我們將 NestJS
伺服器作為 Docker
容器執行。需要建立一個 Dockerfile
。
cd nest-subscriber
touch Dockerfile
然後開啟檔案並貼上以下程式碼:
FROM node:16.13.0-alpine
WORKDIR "/app"
COPY ./nest-subscriber/package.json ./
RUN yarn install
COPY ./nest-subscriber .
RUN yarn run build
EXPOSE 3000
CMD ["yarn","start:prod"]
構建映象:
docker build -f ./nest-subscriber/Dockerfile -t nest-subscriber:latest . --no-cache
8. 將 NestJS 訂閱服務新增到 docker-compose 檔案
在建立了我們的 NestJS
伺服器和 Dockerfile
之後,我們建立了 nest-subscriber
docker
服務。
將以下內容新增到 docker-compose.yml
:
nest-subscriber:
image: "nest-subscriber:latest"
depends_on:
- redis-publisher
- dapr-placement
restart: always
9. 建立 Dapr 訂閱
我們將為我們的 pub/sub
訂閱定義配置。
建立一個 dapr/subscriptions
資料夾。然後建立元件檔案 redis-subscription.yaml
mkdir -p dapr/subscriptions
cd dapr/subscriptions
touch redis-subscription.yaml
然後開啟檔案並插入我們的 Dapr
訂閱元件的詳細資訊
apiVersion: dapr.io/v1alpha1
kind: Subscription
metadata:
name: nest-redis-sub
spec:
topic: nest-redis-pub-topic
route: /redis-publisher
pubsubname: redis-pubsub
scopes:
- nest-subscriber
路由是釋出 topic
時 Dapr
將呼叫的 API
scope
是訂閱該 topic
的服務。
pubsubname
是 redis-pubsub,它等於我們的 redis-pubsub.yaml
檔案中定義的後設資料名稱。
在這個專案中,如果釋出了一個 topic nest-redis-pub-topic
,Dapr 將在我們的 nest-subscriber
服務中呼叫 API /redis-publisher
。
10. 建立 NestJS 伺服器 Dapr Sidecar
我們需要為我們的 NestJS
服務建立一個 sidecar
,就像 redis-publisher
服務一樣。
將 nest-subscriber-dapr-sidecar
服務新增到我們的 docker-compose.yml
nest-subscriber-dapr-sidecar:
image: "daprio/daprd:edge"
command: [
"./daprd",
"-app-id",
"nest-subscriber",
"-app-port",
"3000",
"-components-path",
"/components",
"-placement-host-address",
"dapr-placement:50006",
]
volumes:
- "./dapr/components/:/components"
depends_on:
- nest-subscriber
network_mode: "service:nest-subscriber"
11. 測試它是否有效
通常 Dapr Docker
容器會在 Docker
網路中進行通訊。
但是為了我們做測試,我們將開啟對映暴露埠 5000
到我們的本地機器 5001
。
redis-publisher:
image: redis
depends_on:
- dapr-placement
restart: always
ports:
- 5001:5000
然後在您的終端中執行以下命令:
curl --location --request POST 'http://localhost:5001/v1.0/publish/redis-pubsub/nest-redis-pub-topic' \
--header 'Content-Type: application/json' \
--data-raw '{
"hello": "world"
}'
Dapr
的優點之一是它遵循特定的 URL
格式。這裡我們只使用 Dapr sidecar HTTP
埠(5001
),然後是版本號(v1.0
),然後是 action(publish)
。然後是我們 redis-pubsub.yaml
配置檔案中定義的 pubsubname
(redis-pubsub
)和 topic
(nest-redis-pub-topic
)。
一旦發出 HTTP post
請求。我們的 NestJS
伺服器應該在 /redis-publisher
收到一個 post
請求,這將導致以下日誌:
我們可以看到它正在通過 Dapr
接收 Redis
釋出。但是我們的 NestJS
伺服器無法正確處理訊息。
只有 {}
被髮布,而不是我們釋出的訊息。
我們將在下一步中解決這個問題。
注意:我們通過 redis-dapr-sidecar
的 dapr-http-port
呼叫釋出服務。通常會有一個單獨的 Docker
服務(例如另一個伺服器),它有自己的 Dapr sidecar
,它將呼叫 redis
釋出服務。 在這種情況下,我們將使用該 Docker
服務的 Dapr sidecar http-port
。該請求將由 sidecar
傳送到 Dapr placement
服務,然後該服務將確定將請求轉發到的正確 Dapr sidecar
。
12. 允許 NestJS 解析 application/cloudevents+json
我們的 nest-subscriber-dapr-sidecar
向我們的 nest-subscriber
伺服器發出的 post
請求的 Content-Type
將是 application/cloudevents+json 而不是 application/json
目前我們的 NestJS
伺服器無法解析 application/cloudevents+json
。
為了解決這個問題,我們首先需要安裝 body-parser
:
cd nest-subscriber
yarn add body-parser
接下來我們需要修改我們的 NestJS
伺服器 main.ts
:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as bodyParser from 'body-parser';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(bodyParser.json({ type: 'application/cloudevents+json' }));
app.use(bodyParser.json());
await app.listen(3000);
}
bootstrap();
當我們再次傳送 post
請求時,我們的 NestJS
伺服器將能夠處理請求正文並顯示以下日誌:
好了,我們現在有一個基於 Dapr
工作的 Redis Pub/Sub
分散式應用。
13. 完整 docker-compose.yaml
version: "3.5"
services:
dapr-placement:
image: "daprio/dapr"
command: ["./placement", "-port", "50006"]
redis-publisher:
image: redis
depends_on:
- dapr-placement
restart: always
ports:
- 5001:5000
redis-dapr-sidecar:
image: "daprio/daprd:edge"
command: [
"./daprd",
"-app-id",
"redis-publisher",
"-app-port",
"6379",
"-dapr-http-port",
"5000",
"-components-path",
"/components",
"-placement-host-address",
"dapr-placement:50006"
]
volumes:
- "./dapr/components/:/components"
depends_on:
- redis-publisher
network_mode: "service:redis-publisher"
nest-subscriber:
image: "nest-subscriber:latest"
depends_on:
- redis-publisher
- dapr-placement
restart: always
nest-subscriber-dapr-sidecar:
image: "daprio/daprd:edge"
command: [
"./daprd",
"-app-id",
"nest-subscriber",
"-app-port",
"3000",
"-components-path",
"/components",
"-placement-host-address",
"dapr-placement:50006",
]
volumes:
- "./dapr/components/:/components"
depends_on:
- nest-subscriber
network_mode: "service:nest-subscriber"
14. 原始碼
https://github.com/Hacker-Linner/dapr-nestjs-redis-pub-sub.git