Як побудувати плагінний компонент Dapr

Плагінні компоненти (Pluggable components) — це компоненти, які не включені безпосередньо в середовище виконання, на відміну від вбудованих компонентів, що додаються за допомогою команди dapr init. Ви можете налаштувати Dapr для використання плагінних компонентів, які використовують API будівельних блоків, але реєструються окремо від вбудованих компонентів Dapr.

Плагінні компоненти vs. вбудовані компоненти

Dapr надає два підходи для реєстрації та створення компонентів:

  • Вбудовані компоненти, які входять до складу середовища виконання і знаходяться в репозиторії components-contrib.
  • Плагінні компоненти, які розгортаються і реєструються незалежно.

Хоча обидва варіанти реєстрації використовують API будівельних блоків Dapr, кожен з них має різні процеси реалізації.

Чому варто використовувати плагінні компоненти?

  • Гнучкість: Dapr надає гнучкість для інтеграції з різними технологіями та сервісами без необхідності змінювати основний код програми. Ви можете підключити компоненти інфраструктури, які вам до вподоби.
  • Налаштовуваність: Ви можете налаштувати поведінку Dapr відповідно до своїх конкретних потреб, замінюючи компоненти (наприклад, використовуючи інший брокер повідомлень або рішення для зберігання даних).
  • Портативність: Завдяки плагінним компонентам, Dapr може працювати на різних хмарних платформах або локальних інфраструктурах, що гарантує портативність ваших мікросервісів.
  • Розв’язування зв'язків: Плагінні компоненти дозволяють знизити зв'язок між вашою програмою і базовою інфраструктурою, даючи можливість змінювати технології чи системи без впливу на код програми.

Створення мого першого плагінного компонента

Я хочу створити демонстраційний додаток, який буде виконувати роль сховища стану Dapr, надаючи можливість легко створювати, отримувати та видаляти записи.

  1. Створюю MyStateStore, який реалізує інтерфейс IStateStore від Dapr.
internal sealed class MyStateStore : IStateStore  
 {  
 private readonly ILogger _logger;  
 private readonly static IDictionary Storage = new ConcurrentDictionary();  
 public MyStateStore(ILogger logger)  
 {  
 _logger = logger;  
 }  

 public Task DeleteAsync(StateStoreDeleteRequest request, CancellationToken cancellationToken = default)  
 {  
 this._logger.LogInformation("Remove request for key {key}", request.Key);  
 Storage.Remove(request.Key);  
 return Task.FromResult(new SetResponse());  
 }  

 public Task GetAsync(StateStoreGetRequest request, CancellationToken cancellationToken = default)  
 {  
 this._logger.LogInformation("Get request for key {key}", request.Key);  
 if (Storage.TryGetValue(request.Key, out var data))  
 {  
 return Task.FromResult(new StateStoreGetResponse  
 {  
 Data = Encoding.ASCII.GetBytes(data),  
 });  
 }  
 return Task.FromResult(new StateStoreGetResponse { });  
 }  

 public Task InitAsync(MetadataRequest request, CancellationToken cancellationToken = default)  
 {  
 _logger.LogInformation("Init request for quickstore");  
 return Task.FromResult(new InitResponse { });  
 }  

 public Task SetAsync(StateStoreSetRequest request, CancellationToken cancellationToken = default)  
 {  
 this._logger.LogInformation("Set request for key {key}", request.Key);  
 Storage[request.Key] = Encoding.UTF8.GetString(request.Value.Span);   
 return Task.FromResult(new SetResponse());  
 }  
 }
  1. Я хочу зареєструвати MyStateStore як сервіс у програмі program.cs.
using Dapr.PluggableComponents;  
using quickstore;  

var app = DaprPluggableComponentsApplication.Create();  

app.RegisterService(  
 "quickstore",  
 serviceBuilder =>  
 {  
 // Реєстрація одного або кількох компонентів для цього сервісу.  
 serviceBuilder.RegisterStateStore();  
 });  

app.Run();
  1. Створюю docker-файл для цього додатка, щоб його можна було контейнеризувати.
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base  
WORKDIR /app  

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build  
WORKDIR /src  
COPY .


RUN dotnet restore "./quickstore.csproj"  

RUN dotnet build "./quickstore.csproj" -c Release -o /app/build  

FROM build AS publish  
RUN dotnet publish "./quickstore.csproj" -c Release -o /app/publish /p:UseAppHost=false  

FROM base AS final  
WORKDIR /app  
COPY --from=publish /app/publish .  
ENTRYPOINT ["dotnet", "quickstore.dll"]
  1. Вам потрібно визначити схему компонента стану і зберегти її у файлі YAML, який ми будемо використовувати пізніше.
apiVersion: dapr.io/v1alpha1  
kind: Component  
metadata:  
 name: statestore  
spec:  
 type: state.quickstore  
 version: v1  
 initTimeout: 1m  
 metadata:  
 - name: quickstore
  1. Для тестування dapr потрібно створити простий dapr-клієнт. Я зробив його достатньо простим, щоб використовувати DaprClient для збереження та читання станів dapr через створене мною "statestore".
class Program  
 {  
 static async Task Main(string[] args)  
 {  
 string DAPR_STORE_NAME = "statestore";  
 var clientBuilder = new DaprClientBuilder();  
 // var port = 50002;  
 // clientBuilder = clientBuilder.UseGrpcEndpoint($"http://localhost:{port}");  
 //clientBuilder = clientBuilder($"http://localhost");  

 using var client = clientBuilder.Build();  
 while (true)  
 {  
 System.Threading.Thread.Sleep(5000);  
 Random random = new Random();  
 int orderId = random.Next(1, 1000);  
 // Використовуємо SDK Dapr для збереження і отримання стану  
 await client.SaveStateAsync(DAPR_STORE_NAME, "order_1", orderId.ToString());  
 await client.SaveStateAsync(DAPR_STORE_NAME, "order_2", orderId.ToString());  
 var result = await client.GetStateAsync(DAPR_STORE_NAME, "order_1");  
 Console.WriteLine("Результат після отримання: " + result);  
 }  
 }  
 }
  1. Щоб запустити цей dapr-клієнт в автономному режимі, ви можете виконати цю команду.
dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 dotnet run
  1. Альтернативно, ви можете контейнеризувати його за допомогою docker-файлу, щоб він міг бути розгорнутий через docker-compose або kubernetes.
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env  
WORKDIR /FirstDaprStateClient  

# Копіюємо все  
COPY . ./  
# Відновлюємо залежності як окремі шари  
RUN dotnet restore  
# Будуємо і публікуємо реліз  
RUN dotnet publish -c Release -o out  

# Створюємо образ для виконання  
FROM mcr.microsoft.com/dotnet/aspnet:7.0  
WORKDIR /FirstDaprStateClient  
COPY --from=build-env /FirstDaprStateClient/out .  
ENTRYPOINT ["dotnet", "FirstDaprStateClient.dll"]
  1. Тепер давайте протестуємо наш плагінний компонент з dapr-клієнтом у docker-compose. Нам потрібно створити наступні сервіси:
  • клієнтський додаток
  • dapr
  • quickstore (тобто мій тестовий плагінний компонент)
  • placement
version: '3'  
services:  
 client:  
 build:  
 context: .  
 dockerfile: FirstDaprStateClient/Dockerfile  
 ports:  
 - "50002:50002"  
 depends_on:  
 - quickstore  
 - placement  
 networks:  
 - hello-dapr  
 dapr:  
 image: "daprio/daprd:edge"  
 command: [  
 "./daprd",  
 "--app-id", "client",  
 "--app-port", "3000",  
 "--dapr-grpc-port", "50002",  
 "--components-path", "/components",  
 "--placement-host-address", "placement:50006", # Сервіс розміщення Dapr доступний через Docker DNS-іменування  
 "--log-level", "debug",  
 ]  
 volumes:  
 - "./components:/components"  
 - "/tmp/dapr-components-sockets:/tmp/dapr-components-sockets"  
 depends_on:  
 - client  
 - quickstore  
 network_mode: "service:client" # Приєднуємо сервіс nodeapp-dapr до мережевого простору імен nodeapp  

 quickstore:  
 build:   
 context: quickstore  
 dockerfile: Dockerfile  
 volumes:  
 - "/tmp/dapr-components-sockets:/tmp/dapr-components-sockets"  
 networks:  
 - hello-dapr  

 placement:  
 image: "daprio/dapr"  
 command: ["./placement", "--port", "50006"]  
 ports:  
 - "50006:50006"  
 depends_on:  
 - quickstore  
 networks:  
 - hello-dapr   

networks:  
 hello-dapr: null

Коли ви запустите команду docker-compose up, ви побачите, що dapr клієнт взаємодіє з dapr statestore, який ми створили, у логах.

dockcer-compose up
```

  1. Якщо ви хочете запустити це в Kubernetes, я попередньо встановив minikube локально для тестування.
apiVersion: apps/v1  
kind: Deployment  
metadata:  
 name: firstdaprstateclient  
 labels:  
 app: firstdaprstateclient  
spec:  
 replicas: 1  
 selector:  
 matchLabels:  
 app: firstdaprstateclient  
 template:  
 metadata:  
 labels:  
 app: firstdaprstateclient  
 annotations:  
 dapr.io/enabled: "true"  
 dapr.io/app-id: "firstdaprstateclient"  
 #dapr.io/app-port: "80"  
 #dapr.io/dapr-grpc-port: "50002"  
 dapr.io/enable-api-logging: "true"  
 dapr.io/unix-domain-socket-path: /tmp/dapr-components-sockets  
 #dapr-unix-domain-socket  
 spec:  
 containers:  
 - name: consumer-app  
 image: superwalnut/dapr-client-demo:latest  
 volumeMounts:  
 - name: dapr-unix-domain-socket  
 mountPath: /tmp/dapr-components-sockets  
 ports:  
 - containerPort: 80  
 imagePullPolicy: Always   

 - name: quickstore  
 image: superwalnut/dapr-component:latest  
 command: ["dotnet", "quickstore.dll"]  
 env:  
 - name: DAPR_GRPC_PORT  
 value: "50001"  
 - name: DAPR_HTTP_PORT  
 value: "3500"  
 ports:  
 - containerPort: 50001  
 - containerPort: 3500  
 volumeMounts:  
 - name: dapr-unix-domain-socket  
 mountPath: /tmp/dapr-components-sockets  
 volumes:  
 - name: dapr-unix-domain-socket  
 emptyDir: {}

За допомогою плагінних компонентів Dapr дозволяє розробникам легко налаштовувати, як їх мікросервіси взаємодіють з зовнішніми системами, забезпечуючи гнучкість, портативність та легкість інтеграції з різними хмарними сервісами.

Ці компоненти є важливою частиною модульного дизайну Dapr і допомагають розробникам вибирати відповідні технології для своїх додатків, одночасно абстрагуючи велику частину складності, пов'язаної з інтеграцією з цими системами.

Перекладено з: How to build a Dapr’s Pluggable Component

Leave a Reply

Your email address will not be published. Required fields are marked *